123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889 |
- '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. <img> 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
|