'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2013, 2021 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ /** * Main class that handles the interaction with a node. * * Various interaction can be added to specific node or using a selector. * * Using the selector will guarantee that the interaction is enabled for nodes that are created in the future. * * This class will add support for doing selection and multi selection for all the nodes that are registered with an action (e.g. move, resize) * * Once there is a selection, all interaction handlers are notified with the list of nodes. * * If we have multiple selection, then only the common interactions are notified. * */ define(['../../../../lib/@waca/core-client/js/core-client/ui/core/Events', '../../../../lib/@waca/core-client/js/core-client/ui/KeyCodes', 'jquery', '../../../layout/authoring/EventHelper', '../../../../app/util/EventChainLocal', '../../../../lib/@waca/core-client/js/core-client/utils/Utils', 'underscore', '../../../../lib/@waca/dashboard-common/dist/utils/EventChainLocal', '../../../../lib/@waca/core-client/js/core-client/utils/BrowserUtils'], function (BaseClass, KeyCodes, $, eventHelper, EventChainLocale, Utils, _, EventChainLocal, BrowserUtils) { var Selection = BaseClass.extend({ _utils: Utils, init: function init(options) { Selection.inherited('init', this, arguments); this.selectedNodes = []; this.orderedActionsBySelector = []; this.actionsBySelector = {}; this.selectionHandlers = null; this.deselectHandlers = []; this.saveMouseEvent = null; this.selector = ''; this.deselectionSelector = options.deselectionSelector || function () { return '.page'; }; this.canvas = options.canvas; this.transaction = options.transaction; this.controller = options.controller; if (options.utils) { this._utils = options.utils; } this.delegate = options.delegate; this.canvas.on('change:selections:select', this.onSelection.bind(this)); this.canvas.on('change:selections:deselect', this.onDeselection.bind(this)); }, startTransaction: function startTransaction() { return this.transaction.startTransaction(); }, endTransaction: function endTransaction(token) { this.transaction.endTransaction(token); }, /** * Initialize. For now, we simply add a listener to the MultiUserBroadcast to listen for event from other users. */ initialize: function initialize() {}, /** * Event handler to handle deselecting all items. Click/tapping somewhere on the document or when clicking or tapping an item without the intent of doing multi-select * * @param {Event} [e] - event * @param {boolean} [isSelectionInProgress] - If true, a deselection:ready will not be fired * * @returns {boolean} */ deselectAll: function deselectAll(e) { var isSelectionInProgress = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var transactionToken = arguments[2]; if (e && e.type === 'mousedown' && (e.buttons || e.which) !== 1 || this._isDraggingAction() || !this.selectedNodes.length) { return; } // don't do deselectAll if we are selecting another widget or the same widget again var $targetNode = void 0; if (e) { $targetNode = $(this._getTargetNode(e)); } if ($targetNode && ($targetNode.closest('.widget').length || $targetNode.closest('.pagegroup').length) && !isSelectionInProgress) { return; } this._deselectDomEvent = e; this._isSelectionInProgress = isSelectionInProgress; this.canvas.deselectContent(this.selectedNodes.map(function (n) { return n.getAttribute('id'); }), transactionToken); return false; }, onDeselection: function onDeselection(event) { var _this = this; var deselectionHandled = false; event.info.value.forEach(function (id) { var content = _this.canvas.getContent(id); var selectionSupported = content && _this._isSelectionSupported(content); if (selectionSupported) { deselectionHandled = true; var contentViewDom = content.getFeature('ContentViewDOM'); var n = contentViewDom.getNode(); if (_this._removeNodeFromSelection(n)) { $(n).removeClass('nodeSelected').blur(); _this.trigger('selection:nodeDeselected', { 'node': n }); } } else { _this.selectedNodes = _this.selectedNodes.filter(function (currentValue) { return currentValue.getAttribute('id') !== id; }); } }); if (deselectionHandled || this._deselectDomEvent) { this.updateDeSelection(this._deselectDomEvent); if (!this._isSelectionInProgress) { this._triggerSelectionReadyEvent(this._deselectDomEvent); } delete this._deselectDomEvent; delete this._isSelectionInProgress; } }, /** * Deselect a given node * * @param node * @param e - event that cause the deselection * @param isSelectionInProgress - optional. If true, a deselection:ready will not be fired. */ deselectNode: function deselectNode(n, e, isSelectionInProgress, transactionToken) { this._deselectDomEvent = e; this._isSelectionInProgress = isSelectionInProgress; this.canvas.deselectContent([n.getAttribute('id')], transactionToken); }, /** * Notify the interaction handlers that we have a new selection If no items are selected, we cleanup the deselect event handlers. * * @param e */ updateDeSelection: function updateDeSelection(e) { if (this.selectedNodes.length === 0) { this.deselectHandlers.forEach(function (handler) { return handler.off(); }); this.deselectHandlers = []; } this.notifyActionHandlers(e); }, /** * Enable an intreraction (e.g. move, resize, etc..) for all nodes that match the given selector * * @param action * @param selector */ addActionForSelector: function addActionForSelector(action, selector, disableUserSelection) { var aActions = this.actionsBySelector[selector]; if (!aActions) { aActions = []; this.actionsBySelector[selector] = aActions; if (!disableUserSelection) { // Register delegated handlers for this selector. this.enableSelectionFor(selector); } } if (aActions.indexOf(action) === -1) { aActions.push(action); this.orderedActionsBySelector.push({ selector: selector, action: action }); } }, /** * Disable dragging for nodes with a given selectors. * * @param selector */ disableInteraction: function disableInteraction() { // Invoke the deselectAll event handler to deselect and cleanup this.deselectAll(); // Clear the selection handlers this.selector = ''; this._detachHandlers(this.selectionHandlers); this.selectionHandlers = null; this.actionsBySelector = {}; this.orderedActionsBySelector = []; }, disableActionsForSelector: function disableActionsForSelector(selector) { this._removeSelector(selector); if (this.selector === '') { this._detachHandlers(this.selectionHandlers); this.selectionHandlers = null; } }, /** * Register nodes with a given selectors as objects that can be interacted with. * * @param selector */ enableSelectionFor: function enableSelectionFor(selector) { if (!this.selectionHandlers) { this.selectionHandlers = []; this._attachStartSelectionHandlers(this.controller.$el, this.selectionHandlers); this.selector = selector; } else { this.selector += ',' + selector; } }, /** * @return whether this controller should allow selections. */ isEnabled: function isEnabled() { return !this.delegate || this.delegate.isEnabled(); }, /** * Filters out deleted nodes from selectedNodes. This is necessary for cases * where a node was deleted and the next selection comes from the CanvasAPI rather than the UI. * Not necessary if the next selection comes from the UI since it triggers a this.deselectAll() first. * @todo: Remove selectedNodes member variable from this file. There should not be two sources of truth. * Use CanvasAPI.getSelectedContentList() instead and remove this patch. */ _cleanRemovedSelectedNodes: function _cleanRemovedSelectedNodes() { var _this2 = this; this.selectedNodes = this.selectedNodes.filter(function (n) { return _this2.canvas.getContent(n.getAttribute('id')); }); }, _isSelectionSupported: function _isSelectionSupported(content) { var type = content.getType(); // TODO: includes? if (type.indexOf('widget') !== -1 || type === 'group') { return true; } var capabilities = content.getFeature('ContentRegistry.internal.' + type + 'Capabilities'); var selectionSupported = false; if (capabilities) { var contentCapabilities = capabilities.getCapabilities(); selectionSupported = contentCapabilities.selection ? contentCapabilities.selection : selectionSupported; } return selectionSupported; }, onSelection: function onSelection(event) { var _this3 = this; var selectionHandled = false; event.info.value.forEach(function (id) { var content = _this3.canvas.getContent(id); var selectionSupported = content && _this3._isSelectionSupported(content); if (selectionSupported) { selectionHandled = true; var contentViewDom = content.getFeature('ContentViewDOM'); var n = contentViewDom.getNode(); _this3._attachDeselectHandlers(); _this3._addSelectedNode(n); _this3._emitSelectionAfterEvent(); // Workaround for IE11 focus, see Jira CADBC-2721 if (BrowserUtils.isIE11()) { setTimeout(function () { $(n).addClass('nodeSelected').focus(); }, 0); } else { $(n).addClass('nodeSelected').focus(); } _this3._cleanRemovedSelectedNodes(); _this3.trigger('selection:nodeSelected', { 'node': n, 'selectedNodes': _this3.selectedNodes }); } }); // Checks if the event contains the hideContextBar parameter if (event.tracking.action.params) { event.tracking.action.params.forEach(function (param) { if (param && param.hideContextBar) { _this3._selectDomEvent = _extends({}, _this3._selectDomEvent, { hideContextBar: param.hideContextBar }); var eventChainLocal = new EventChainLocal(_this3._selectDomEvent); eventChainLocal.setProperty('preventDefaultContextBar', true); } }); } if (selectionHandled || this._selectDomEvent) { this.notifyActionHandlers(this._selectDomEvent); if (!this._isSelectionInProgress) { this._triggerSelectionReadyEvent(this._selectDomEvent); } delete this._selectDomEvent; delete this._isSelectionInProgress; } }, /** * Handle the selection of a certain node. * * @param node * @param e - optional - If available, event information will be used to update the selection * @param isSelectionInProgress -- if true a selection:ready is not fired. */ selectNode: function selectNode(node, e, isSelectionInProgress, transactionToken) { if (this._isDraggingAction()) { return; } var content = this.canvas.getContent(node.getAttribute('id')); this._selectDomEvent = e; this._isSelectionInProgress = isSelectionInProgress; var currentSelectionSupported = content.getPropertyValue('selectable') !== false; if (currentSelectionSupported) { this.canvas.selectContent([node.getAttribute('id')], transactionToken); } else { var parentContainer = content.getContainer(); var parentSelectionSupported = parentContainer.getPropertyValue('selectable') !== false; if (parentSelectionSupported) { var parentNode = parentContainer.getFeature('ContentViewDOM').getNode(); this.canvas.selectContent([parentNode.getAttribute('id')], transactionToken); } } }, /** * Handle the tap event * @param e */ onTap: function onTap(e) { if (e.type === 'mousedown' && ((e.buttons || e.which) !== 1 || this._suppressSyntheticMouseEvent)) { this._suppressSyntheticMouseEvent = false; return; // If a mousedown event occurs right after a tap, suppress. } this._suppressSyntheticMouseEvent = e.type === 'tap'; var n = this._emitSelectionBeforeEvent(this._getTargetNode(e), e.type); var content = this.canvas.getContent(n.getAttribute('id')); var currentSelectionSupported = content.getPropertyValue('selectable') !== false; if (this.selectedNodes.length === 1 && this._getIndexFromSelection(n) > -1 || !currentSelectionSupported && this.selectedNodes.length > 0 && this._isInSelection(content.getContainer().getId())) { this._triggerReselectEvent(e, n); } else { var token = this.startTransaction(); if (this.selectedNodes.length > 0) { this.deselectAll(e, true, token); } this.selectNode(n, e, false, token); this.endTransaction(token); } }, /** * Handle the hold event . * @param e */ onHold: function onHold(e) { var token = this.startTransaction(); var n = this._emitSelectionBeforeEvent(this._getTargetNode(e), e.type); if (this._getIndexFromSelection(n) > -1) { this.deselectNode(n, e, false, token); // prevent mouse events if (e.gesture) { e.gesture.preventDefault(); } return false; } this.selectNode(n, e, false, token); this.endTransaction(token); }, onTouchStart: function onTouchStart() { //Removing mouse events if touch compatible device to avoid touch and mouse events clashing this.selectionHandlers[2].off(); this.selectionHandlers[3].off(); }, onTouchEnd: function onTouchEnd(e) { var _this4 = this; var node = this._getTargetNode(e); //Adding the mouse events back after the touch event on touch compatible device. setTimeout(function () { eventHelper.on(node, 'mousedown', _this4.saveMouseEvent); eventHelper.on(node, 'click', _this4.saveMouseEvent); }, 600); }, onKeyDownSelectEvent: function onKeyDownSelectEvent(e) { if (!this._shouldSelect(e)) { return; } var token = this.startTransaction(); // broadcast selection var n = this._emitSelectionBeforeEvent(this._getTargetNode(e), e.type); // if selected then deselect (only if multi-select) if (this._shouldDeselect(n, e)) { this.deselectNode(n, e, false, token); } else { // not multi-select so select and deselect others accordingly if (this._shouldDeselectAll(e)) { this.deselectAll(e, true, token); } this.selectNode(n, e, false, token); } this.endTransaction(token); }, /** * Handle the mouse down and up events * @param e * @returns {Boolean} */ onMouseSelectEvent: function onMouseSelectEvent(e) { var ret = true; // Only a left click if ((e.buttons || e.which) === 1) { var n = this._getTargetNode(e); var $n = $(n); // click & contextmenu is used to select sub children. It is only handled under the following conditions: // 1 - The _ignoreMouseUpSelect is not set. This is set when we select a node on the mousedown. // 2 - The target node is not selected but the parent is // 3 - The parent of the node is not being dragged or resized // 4 - The mouse up is a result of a drag into the widget. // var isParentMoving = this._isParentMoving($n); var isParentSelectedAndNotTarget = this._isParentSelectedAndNotTarget($n); var isDraggingAction = this._isDraggingAction(); if ((e.type === 'click' || e.type === 'contextmenu') && (this._ignoreMouseUpSelect || isParentMoving || isParentSelectedAndNotTarget || isDraggingAction)) { if (this._ignoreMouseUpSelect && !isParentMoving && !isParentSelectedAndNotTarget && !this._deselectPrevented) { //We selected on mouse down. Now we trigger the selection:ready event when the mouse is up this._triggerSelectionReadyEvent(e); } this._ignoreMouseUpSelect = false; this._deselectPrevented = false; } else { ret = this._handleMouseSelection(e, n); } } return ret; }, // Private /** * remove selected node from selectedNode List * * @param node - selected Node * * @returns boolean - node successfully removed or node not exist in list */ _removeNodeFromSelection: function _removeNodeFromSelection(node) { var index = this._getIndexFromSelection(node); if (index > -1) { this.selectedNodes.splice(index, 1); return true; } return false; }, /** * get index for target node from selectedNode List * * @param node - selected Node * * @returns node Index in List, -1 if node not exist */ _getIndexFromSelection: function _getIndexFromSelection(node) { var nodeMap = _.map(this.selectedNodes, function (n) { return n.id; }); return nodeMap.indexOf(node && node.id); }, _createDelegatedEventHandler: function _createDelegatedEventHandler(func) { var _this5 = this; return function (e) { if (!_this5.isEnabled()) { return; } var n = e.target; if (n && $(n).is(_this5.selector)) { return func(e); } else if (n) { //This is needed if the target is a child of a staticHTML element (e.g. tag) // check if the parent has a selector var parents = $(n).parents(_this5.selector); if (parents.length > 0) { e._delegateTarget = parents[0]; return func(e); } } }; }, _detachHandlers: function _detachHandlers(handlers) { if (handlers) { handlers.forEach(function (handler) { return handler.off(); }); } }, _removeSelector: function _removeSelector(selector) { var values = this.selector.split(','); for (var i = 0; i < values.length; i++) { if (values[i] === selector) { values.splice(i, 1); this.selector = values.join(','); return; } } }, _attachStartSelectionHandlers: function _attachStartSelectionHandlers(node, handlers) { var onTap = this._createDelegatedEventHandler(this.onTap.bind(this)); var onHold = this._createDelegatedEventHandler(this.onHold.bind(this)); var onMouseSelectEvent = this._createDelegatedEventHandler(this.onMouseSelectEvent.bind(this)); var onKeyDownSelectEvent = this._createDelegatedEventHandler(this.onKeyDownSelectEvent.bind(this)); var onTouchStart = this._createDelegatedEventHandler(this.onTouchStart.bind(this)); var onTouchEnd = this._createDelegatedEventHandler(this.onTouchEnd.bind(this)); this.saveMouseEvent = onMouseSelectEvent; if (this._utils.isIpad()) { handlers.push(eventHelper.on(node, 'tap', onTap), eventHelper.on(node, 'hold', onHold), eventHelper.on(node, 'keydown', onKeyDownSelectEvent), eventHelper.on(node, 'mousedown', onTap)); } else { handlers.push(eventHelper.on(node, 'tap', onTap), eventHelper.on(node, 'hold', onHold), eventHelper.on(node, 'mousedown', onMouseSelectEvent), eventHelper.on(node, 'click', onMouseSelectEvent), eventHelper.on(node, 'contextmenu', onMouseSelectEvent), eventHelper.on(node, 'keydown', onKeyDownSelectEvent), eventHelper.on(node, 'touchstart', onTouchStart), eventHelper.on(node, 'touchend', onTouchEnd)); } }, /** * Helper function used to get the target node from the event. * * The target can be set either: * 1 - delegateTarget set by the delegate handler when the actual event target is a child of the the target we are interested in. * 2 - the event target * */ _getTargetNode: function _getTargetNode(e) { return e._delegateTarget ? e._delegateTarget : e.target; }, /** * Detect if the target should be selected. * Note: Allow using spacebar to select, but skip when type space on an element * whose parent is a livewidget will set the property 'preventWidgetSelection'. * @param {Event} e The Keydown event. * * @returns {Boolean} True means the target should be selected. Otherwise false. */ _shouldSelect: function _shouldSelect(e) { var eventChainLocal = new EventChainLocal(e); return (e.keyCode === KeyCodes.ENTER || e.keyCode === KeyCodes.SPACE) && e.target && e.target.className !== 'inlineText' && !eventChainLocal.getProperty('preventWidgetSelection'); }, _shouldDeselect: function _shouldDeselect(newSelection, e) { return this._getIndexFromSelection(newSelection) > -1 && Utils.isControlKey(e); }, _shouldDeselectAll: function _shouldDeselectAll(e) { return this.selectedNodes.length > 0 && !Utils.isControlKey(e); }, _isParentMoving: function _isParentMoving($n) { return $n.parents('.nodeDragging, .nodeResizing').length > 0; }, _isParentSelectedAndNotTarget: function _isParentSelectedAndNotTarget($n) { return !$n.is('.nodeSelected') && $n.parents('.nodeSelected').length === 0; }, // Looks in the dom to verify that an avatar has been tagged with the doNotSelect class _isDraggingAction: function _isDraggingAction() { return $('body').hasClass('dragging'); }, _isInSelection: function _isInSelection(id) { var inSelection = false; for (var i = 0; i < this.selectedNodes.length; i++) { inSelection = this.selectedNodes[i].getAttribute('id') === id; if (inSelection) { break; } } return inSelection; }, _handleMouseSelection: function _handleMouseSelection(e, n) { var content = this.canvas.getContent(n.getAttribute('id')); var currentSelectionSupported = content.getPropertyValue('selectable') !== false; var ret = true; n = this._emitSelectionBeforeEvent(n, e.type); if (this.selectedNodes.indexOf(n) > -1 || !currentSelectionSupported && this.selectedNodes.length > 0 && this._isInSelection(content.getContainer().getId())) { // multi-select (ctrl for windows/linux, cmd for mac) if (Utils.isControlKey(e)) { this._handleCtrlKeyClick(e, n); ret = false; } else if (e.type === 'click' && !$(e.target).hasClass('resizePoint')) { // Issue the reselect on the mouseup to make sure that we finished the selection. this._triggerReselectEvent(e, n); } } else { ret = this._mouseEventSelectNode(e, n); } return ret; }, _handleCtrlKeyClick: function _handleCtrlKeyClick(e, n) { var eventChainLocale = new EventChainLocale(e); if (eventChainLocale.getProperty('preventWidgetDeselect')) { // some handler down the chain doesn't want the widget to be deselected. // e.g doing multiple selection within the data widget this._ignoreMouseUpSelect = true; this._deselectPrevented = true; } else { this.deselectNode(n, e); } }, _mouseEventSelectNode: function _mouseEventSelectNode(e, n) { var ret = true; var token = this.startTransaction(); // multi-select (ctrl for windows/linux, cmd for mac) if (this.selectedNodes.length > 0 && !Utils.isControlKey(e)) { this.deselectAll(e, true, token); } if (e && e.type === 'mousedown') { // Mark that we are selecting on mouse down so we can ignore the mouse up. this._ignoreMouseUpSelect = true; } this.selectNode(n, e, this._ignoreMouseUpSelect, token); this.endTransaction(token); return ret; }, finishMoveSelection: function finishMoveSelection(e) { if (this._ignoreMouseUpSelect) { this._triggerSelectionReadyEvent(e); } this._ignoreMouseUpSelect = false; }, _triggerReselectEvent: function _triggerReselectEvent(e, n) { // Reselect is when the user selects an already selected item. if (!$(n).is('.nodeDragging, .nodeResizing')) { this.emit('selection:reselect', { node: n, event: e }); } }, _triggerSelectionReadyEvent: function _triggerSelectionReadyEvent(e) { this.emit('selection:ready', { 'selectedNodes': this.selectedNodes, event: e }); }, _emitSelectionBeforeEvent: function _emitSelectionBeforeEvent(n, eventType) { // listeners can change the selection var payload = { 'newSelection': n, 'currentSelection': this.selectedNodes.concat(), 'eventType': eventType }; this.emit('selection:before', payload); return payload.newSelection; }, _emitSelectionAfterEvent: function _emitSelectionAfterEvent() { this.emit('selection:after', { 'currentSelection': this.selectedNodes.concat() }); }, _addSelectedNode: function _addSelectedNode(n) { // Maintain the document order. // If node already added, do nothing; if (this._getIndexFromSelection(n) !== -1) { return; } // Find the the sibling that is before the node that is already selected. var next = n; var index = -1; while (next && index === -1) { next = next.nextSibling; index = this._getIndexFromSelection(next); } if (index !== -1) { this.selectedNodes.splice(index, 0, n); } else { this.selectedNodes.push(n); } }, _deselectHandler: function _deselectHandler(e) { if (e.keyCode && !(e.keyCode === KeyCodes.ENTER || e.keyCode === KeyCodes.SPACE)) { return; } var $target = $(e.target); var selector = this.deselectionSelector(); var isClickScrollbarY = false; var isClickScrollbarX = false; if (this.selectedNodes.length >= 1) { // Get the page within the same tab with the selected node var selectedNode = this.selectedNodes[0]; var page = $(selectedNode).parents('.pagecontainer')[0]; // If there is a pagecontainer for the selected nodes we do the following. // Otherwise, just deselect all selections. if (page) { // Detect if there are scrollbar(s), and get the standard scrollbar width // scrollbarY: vertical scrollbar // scrollbarX: horizontal scrollbar var isScrollbarX = false; var isScrollbarY = false; if (page.scrollHeight > page.clientHeight || page.style.overflowY === 'scroll') { isScrollbarY = true; } if (page.scrollWidth > page.clientWidth || page.style.overflowX === 'scroll') { isScrollbarX = true; } // If there are scrollbar(s), and the scrollbar's width hasn't been // calculated and cahced, then create a temporary element to // get the standard scrollbar width. if ((isScrollbarY || isScrollbarX) && !this.scrollbarWidth) { var tmpDiv = document.createElement('div'); tmpDiv.style.overflow = 'scroll'; tmpDiv.style.width = '50px'; tmpDiv.style.width = '50px'; document.body.appendChild(tmpDiv); this.scrollbarWidth = tmpDiv.offsetWidth - tmpDiv.clientWidth; document.body.removeChild(tmpDiv); } // If the vertical scrollbar exists, get the scrollbar's area and tell if // the mouse click occurs within the area. if (isScrollbarY) { // Determine the scrollbar area for ScrollbarY (vertical scrollbar). var rect = page.getBoundingClientRect(); var scrollbarYOffsetLeft = rect.left + page.clientLeft + page.clientWidth; var scrollbarYOffsetRight = scrollbarYOffsetLeft + this.scrollbarWidth; var scrollbarYOffsetTop = rect.top + page.clientTop; var scrollbarYOffsetBottom = scrollbarYOffsetTop + page.clientHeight; // Detect if the scrollbarY is clicked if (e.clientX >= scrollbarYOffsetLeft && e.clientX <= scrollbarYOffsetRight && e.clientY >= scrollbarYOffsetTop && e.clientY <= scrollbarYOffsetBottom) { isClickScrollbarY = true; } } // If the horizontal scrollbar exists, get the scrollbar's area and tell if // the mouse click occurs within the area. if (isScrollbarX) { // Determine the scrollbar area for ScrollbarX (horizontal scrollbar). var _rect = page.getBoundingClientRect(); var scrollbarXOffsetLeft = _rect.left + page.clientLeft; var scrollbarXOffsetRight = scrollbarXOffsetLeft + page.clientWidth; var scrollbarXOffsetTop = _rect.top + page.clientTop + page.clientHeight; var scrollbarXOffsetBottom = scrollbarXOffsetTop + this.scrollbarWidth; // Detect if the scrollbarY is clicked if (e.clientX >= scrollbarXOffsetLeft && e.clientX <= scrollbarXOffsetRight && e.clientY >= scrollbarXOffsetTop && e.clientY <= scrollbarXOffsetBottom) { isClickScrollbarX = true; } } } } // Only deselect when a child of the deselection selector is clicked, and the scrollbars are not clicked. if (($target.is(selector) || $target.parents(selector).length > 0) && !(isClickScrollbarX || isClickScrollbarY) && $target.closest('.dashboardAuthoringToolsPane').length === 0 /* Temporary: In the future an api will give a node which will take care of the event selection: RTC: 295170 */) { this.deselectAll(e); } }, _attachDeselectHandlers: function _attachDeselectHandlers() { if (this.deselectHandlers.length === 0) { var deselectHandler = this._deselectHandler.bind(this); this.deselectHandlers.push(eventHelper.on(this.controller.$el, 'tap', deselectHandler)); this.deselectHandlers.push(eventHelper.on(this.controller.$el, 'mousedown', deselectHandler)); this.deselectHandlers.push(eventHelper.on(this.controller.$el, 'keydown', deselectHandler)); } }, /** * Notify the interaction handlers (e.g. move, resize) that there is a new selection If we have a group selection, we will only notify the intersection of all interactions that is supported by * all selected items * * @param e */ notifyActionHandlers: function notifyActionHandlers(e) { var _this6 = this; var actions = this.getActionsForSelection(); actions.forEach(function (action) { // New actions are notified from Controller.notifyActionsWithNewSelection() action.newSelection && action.newSelection(_this6.selectedNodes.concat(), e); if (_this6.previousActions) { var index = _this6.previousActions.indexOf(action); if (index > -1) { _this6.previousActions.splice(index, 1); } } }); if (this.previousActions) { this.previousActions.forEach(function (previousAction) { return previousAction.newSelection([], e); }); } this.previousActions = actions; }, getInteractionProperties: function getInteractionProperties() { var actions = this.getActionsForSelection(); var items = []; actions.forEach(function (action) { var actionProperties = action.getProperties(); items = items.concat(actionProperties); }); items = _.sortBy(items, 'order'); return items; }, /** * Get the list of all interaction handlers that is supported by all selected items * * @returns */ getActionsForSelection: function getActionsForSelection() { var _this7 = this; var actions = this.selectedNodes.map(function (selectedNode) { return _this7.getNodeActionBySelector(selectedNode); }); return _.intersection.apply(_, actions); }, getNodeActionBySelector: function getNodeActionBySelector(n) { var nodeActions = []; // search selectors this.orderedActionsBySelector.forEach(function (item) { if ($(n).is(item.selector)) { nodeActions = nodeActions.concat(item.action); } }); return nodeActions; } }); return Selection; }); //# sourceMappingURL=Selection.js.map