123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- 'use strict';
- /**
- * 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.
- */
- define(['./BaseStylePropertyAction', '../../../../lib/@waca/core-client/js/core-client/utils/Deferred', 'jquery', 'doT', '../EventHelper', '../../../../lib/@waca/dashboard-common/dist/ui/interaction/Utils', '../../../../lib/@waca/core-client/js/core-client/ui/KeyCodes', '../../../../app/util/ScreenReaderUtil', '../../../../app/nls/StringResources', 'text!./MoveEdges.html', './LayoutIndicator', './GuidelineManager', '../../../util/PxPercentUtil', '../../../../lib/@waca/core-client/js/core-client/i18n/Formatter', '../../../../lib/@waca/dashboard-common/dist/utils/EventChainLocal', 'underscore', 'bspopover'], function (BaseClass, Deferred, $, dot, eventHelper, utils, KeyCodes, ScreenReaderUtil, stringResources, MoveTemplate, LayoutIndicator, GuidelineManager, PxPercentUtil, Formatter, EventChainLocal, _) {
- var Move = null;
- Move = BaseClass.extend({
- _savedSelectedNodes: null,
- init: function init(controller) {
- var _this = this;
- Move.inherited('init', this, arguments);
- this.controller = controller;
- this.isDragging = false;
- this._isTouch = false;
- this.dropInfo = {};
- this._guidelineManager = new GuidelineManager({ 'layoutController': controller.layoutController });
- this._ScreenReader = new ScreenReaderUtil();
- this.whenIsReadyDfd = new Deferred();
- this._createAvatarContainer();
- this._layoutIndicator = new LayoutIndicator({ 'node': this._$avatarContainer });
- this._initialize();
- // Formating the coordinates is a pretty big perf hit while moving, so throttle it
- this._updateLayoutIndicatorContent = _.throttle(function (evt, dragBoxSize, nodeInfo, revisedOffsets, dragContext) {
- var node = nodeInfo.node;
- if (node && node._layout && node._layout.layoutController) {
- var dropTargetNode = dragContext.dropTargetNode;
- var containerNode = node._layout.layoutController.getLayoutContentContainerForDropTarget(dropTargetNode);
- var coordinates = _this._convertCoordinates(dragBoxSize, evt.clientX - dragBoxSize.offsetx - revisedOffsets.x, evt.clientY - dragBoxSize.offsety - revisedOffsets.y, containerNode);
- var formatedDragCoordinates = _this._formatPosition(node, coordinates, containerNode);
- _this._layoutIndicator.updateContent('x: ' + formatedDragCoordinates.left + ' y: ' + formatedDragCoordinates.top);
- }
- }, 250);
- },
- /**
- * Creates an avatar container. This container will be shared across dashboards, so only need to create it once.
- */
- _createAvatarContainer: function _createAvatarContainer() {
- // Look for an existing avatar div
- this._$avatarContainer = $('body').children('.avatarContainer.avatarContainerNotDragging');
- if (this._$avatarContainer.length === 0) {
- this._$avatarContainer = $('<div class="avatarContainer avatarContainerNotDragging"></div>');
- $('body').append(this._$avatarContainer);
- }
- },
- /**
- * Get the jQuery object for the avatar node
- * @returns jquery object of the avatar node
- */
- getAvatarContainer: function getAvatarContainer() {
- return this._$avatarContainer;
- },
- whenIsReady: function whenIsReady() {
- return this.whenIsReadyDfd.promise;
- },
- _initialize: function _initialize() {
- this._dndManager = this.controller.dashboardApi.getFeature('DashboardDnd.internal');
- this.whenIsReadyDfd.resolve();
- },
- destroy: function destroy() {
- if (this._dndManager) {
- this._dndManager.resetDragging();
- this._dndManager = null;
- }
- this.resetDraggingState();
- this._ScreenReader.destroy();
- if (this._iKeyPressTimer) {
- clearTimeout(this._iKeyPressTimer);
- }
- this._$avatarContainer.empty();
- },
- /**
- * Setup the event handlers that will start the dragging operation
- *
- */
- setupStartEventHandlers: function setupStartEventHandlers() {
- var moveStart = this.dragStartMouseEvent.bind(this);
- var moveStartTouch = this.initiateDragging.bind(this);
- var dragStart = this.onDragStart.bind(this);
- var n;
- for (var i = 0; i < this.selectedNodes.length; i++) {
- n = this.selectedNodes[i];
- n._events = [];
- n._events.push(eventHelper.on(n, 'touchstart', moveStartTouch));
- n._events.push(eventHelper.on(n, 'mousedown', moveStart));
- n._events.push(eventHelper.on(n, 'dragstart', dragStart));
- }
- },
- onDragStart: function onDragStart() {
- // cancel drag events so they don't interfere with the move.
- return false;
- },
- /**
- * Clear the event handlers used to start the dragging operation
- *
- */
- clearStartEventHandlers: function clearStartEventHandlers() {
- var n;
- for (var i = 0; i < this.selectedNodes.length; i++) {
- n = this.selectedNodes[i];
- if (n._events) {
- utils.removeEvents(n._events);
- n._events = null;
- }
- }
- },
- /**
- * Called by the interaction manager when we have new selection
- *
- * @param nodes -
- * selected nodes
- * @param evt -
- * the event used to select the last node (optional)
- */
- newSelection: function newSelection(nodes, evt) {
- this._resetSelectionState();
- this.selectedNodes = nodes;
- this._savedSelectedNode = nodes;
- this.dropInfo.nodeInfoList = [];
- for (var i = 0; i < nodes.length; i++) {
- this.dropInfo.nodeInfoList.push({ node: nodes[i] });
- }
- this.setupStartEventHandlers();
- var eventTypeInit = evt && (evt.type === 'mousedown' || evt.type === 'hold' || evt.type === 'touchstart');
- if (eventTypeInit && this.selectedNodes.length > 0) {
- // allow the user to move while the selection mousedown/hold event is still in effect")
- this.initiateDragging(evt);
- }
- // set up keyboard navigation
- if (nodes.length > 0) {
- this.controller.$el.on('keydown.interactionMoveKey', this.arrowKeyPress.bind(this));
- } else {
- this.controller.$el.off('keydown.interactionMoveKey');
- }
- this.addMoveHandle(nodes);
- },
- addMoveHandle: function addMoveHandle(nodes) {
- var iconsFeature = this.controller.dashboardApi.getFeature('Icons');
- var moveIcon = iconsFeature.getIcon('move');
- var html = dot.template(MoveTemplate)({
- moveIcon: moveIcon.id
- });
- $(nodes).append(html).find('.moveHandle').attr('title', stringResources.get('moveHandle'));
- },
- saveNodeInfo: function saveNodeInfo() {
- for (var i = 0, iLen = this.dropInfo.nodeInfoList.length; i < iLen; i++) {
- this.setNodePositionInfo(this.dropInfo.nodeInfoList[i]);
- }
- },
- /**
- * Handle the keyboard move events
- */
- _iKeyPressTimer: null,
- /**
- * @param {Event} evt
- * Note: It's a valid event for ArrowKeyPress if and only if:
- * 1. the key is one of the arrow keys
- * 2. shift key hasn't been pressed
- * 3. the target doesn't have 'inlineText' class
- * 4. the target's parent don't in focus mode
- * 5. the event doesn't have a 'preventMoveAction' property
- * 6. the event target is not inside tab controls
- * 7. the event target is inside a canvas (see https://jsw.ibm.com/browse/CADBC-502)
- */
- _isValidEventForArrowKeyPress: function _isValidEventForArrowKeyPress(evt) {
- var $target = $(evt.target);
- var isArrowKey = evt.keyCode === KeyCodes.LEFT_ARROW || evt.keyCode === KeyCodes.RIGHT_ARROW || evt.keyCode === KeyCodes.UP_ARROW || evt.keyCode === KeyCodes.DOWN_ARROW;
- if (isArrowKey) {
- var inTabControls = $target.closest('.ba-common-tabList').length !== 0;
- var eventChainLocal = new EventChainLocal(evt);
- var isFromCanvas = $('.dashboardFrameCentre').has(evt.target).length > 0;
- return !(evt.shiftKey || $target.hasClass('inlineText') || $target.parents().hasClass('widgetFocus') || eventChainLocal.getProperty('preventMoveAction') || inTabControls) && isFromCanvas;
- } else {
- return false;
- }
- },
- /**
- * @param {Event} evt
- * Note: init move delta and adjust according to which arrow key is pressed
- */
- _calculateInitDeltasForArrowKeyPress: function _calculateInitDeltasForArrowKeyPress(evt) {
- var iDeltaTop = 0;
- var iDeltaLeft = 0;
- var sMessage = '';
- var farEdge = false;
- switch (evt.keyCode) {
- case KeyCodes.LEFT_ARROW:
- iDeltaLeft = -1;
- sMessage = stringResources.get('srWidgetMoveLeft');
- break;
- case KeyCodes.UP_ARROW:
- iDeltaTop = -1;
- sMessage = stringResources.get('srWidgetMoveUp');
- break;
- case KeyCodes.RIGHT_ARROW:
- iDeltaLeft = 1;
- sMessage = stringResources.get('srWidgetMoveRight');
- farEdge = true;
- break;
- case KeyCodes.DOWN_ARROW:
- iDeltaTop = 1;
- sMessage = stringResources.get('srWidgetMoveDown');
- farEdge = true;
- break;
- }
- return { iDeltaTop: iDeltaTop, iDeltaLeft: iDeltaLeft, farEdge: farEdge, sMessage: sMessage };
- },
- /**
- * @param node : the node need to be moved
- * @param initDeltas
- * @param nodeInfo
- * Note: calculate the position update for a node when pressing an arrow key.
- */
- _calculatePositionUpdateForNodeWhenArrowKeyPress: function _calculatePositionUpdateForNodeWhenArrowKeyPress(node, initDeltas, nodeInfo) {
- var iDeltaTop = initDeltas.iDeltaTop,
- iDeltaLeft = initDeltas.iDeltaLeft,
- farEdge = initDeltas.farEdge;
- var position = {
- left: parseInt($(node).css('left')),
- top: parseInt($(node).css('top'))
- };
- var newDeltaLeft = 0;
- var newDeltaTop = 0;
- var dragbox = { 'top': position.top, 'left': position.left, 'right': position.left + node.clientWidth, 'bottom': position.top + node.clientHeight };
- // TODO -- should not call getLayoutContentContainer - we need to detect the parent of the node being moved..
- var containerNode = nodeInfo.node._layout.layoutController.getLayoutContentContainer();
- this._guidelineManager.getReady(containerNode, dragbox, true);
- var newSnapCoords = this._getNewCoordinates(dragbox);
- var revisedOffsets = this.getRevisedOffsets(newSnapCoords);
- if (this._guidelineManager.gridGuidelines && this._guidelineManager.gridGuidelines.snapGrid) {
- if (farEdge) {
- revisedOffsets.x = this._guidelineManager.gridGuidelines.gridSize - revisedOffsets.x;
- revisedOffsets.y = this._guidelineManager.gridGuidelines.gridSize - revisedOffsets.y;
- }
- /** If the revisedOffset is less than one we jump to the next snap point. this is
- because we do not support half pixels in our model. */
- if (revisedOffsets.x <= 1) {
- revisedOffsets.x = 0;
- }
- if (revisedOffsets.y <= 1) {
- revisedOffsets.y = 0;
- }
- revisedOffsets.x = revisedOffsets.x === null || revisedOffsets.x === 0 ? this._guidelineManager.gridGuidelines.gridSize : revisedOffsets.x;
- revisedOffsets.y = revisedOffsets.y === null || revisedOffsets.y === 0 ? this._guidelineManager.gridGuidelines.gridSize : revisedOffsets.y;
- newDeltaLeft = iDeltaLeft * revisedOffsets.x;
- newDeltaTop = iDeltaTop * revisedOffsets.y;
- } else {
- newDeltaLeft = iDeltaLeft;
- newDeltaTop = iDeltaTop;
- }
- nodeInfo.dropPosition = {
- x: position.left + newDeltaLeft,
- y: position.top + newDeltaTop,
- before: nodeInfo.oldValues.nextSibling
- };
- var positionUpdate = nodeInfo.node._layout.parentLayout.getWidgetPositionUpdate(nodeInfo);
- return positionUpdate;
- },
- /**
- * @param positionUpdateArray
- * @param sMessage
- * Note: update the model after all nodes had been moved the their destinations.
- */
- _updateModelAfterArrowKeyPress: function _updateModelAfterArrowKeyPress(positionUpdateArray, sMessage) {
- // delay the update of the model to account for multiple keypress events in a row
- clearTimeout(this._iKeyPressTimer);
- this._iKeyPressTimer = setTimeout(function () {
- this.emit('move:end', { 'currentSelection': this.selectedNodes });
- this.updateModel(positionUpdateArray);
- this.refreshProperties();
- this._ScreenReader.callOut(sMessage);
- }.bind(this), 500);
- },
- /**
- * @param {Event} evt
- * Note: move all nodes when pressing an arrow key.
- */
- _moveNodesForArrowKeyPress: function _moveNodesForArrowKeyPress(evt) {
- var positionUpdateArray = [];
- var moves = [];
- var initDeltas = this._calculateInitDeltasForArrowKeyPress(evt);
- for (var i = 0, iLen = this.dropInfo.nodeInfoList.length; i < iLen; i++) {
- var nodeInfo = this.dropInfo.nodeInfoList[i];
- if (nodeInfo.node._layout) {
- var node = this.dropInfo.nodeInfoList[i].node;
- var positionUpdate = this._calculatePositionUpdateForNodeWhenArrowKeyPress(node, initDeltas, nodeInfo);
- positionUpdateArray.push(positionUpdate);
- moves.push({
- node: node,
- newPosition: {
- top: parseInt(positionUpdate.style.top, 10),
- left: parseInt(positionUpdate.style.left, 10)
- }
- });
- }
- }
- this.moveNodesToNewPositions(moves);
- return { positionUpdateArray: positionUpdateArray, sMessage: initDeltas.sMessage };
- },
- /**
- * @param moves
- * Note: the structure of each element of moves:
- * {
- * node: a node need to be moved,
- * newPosition: {
- * top: this value will be set to node.style.top,
- * left: this value will be set to node.style.left
- * }
- * }
- */
- moveNodesToNewPositions: function moveNodesToNewPositions(moves) {
- for (var i = 0; i < moves.length; i++) {
- var move = moves[i];
- var node = move.node;
- var newPosition = move.newPosition;
- utils.setTopLeft(node, newPosition.top, newPosition.left);
- }
- },
- /**
- * @param {Event} evt
- * Note: being called everytime when a key is pressed. But only process the event when it can pass the validation.
- */
- arrowKeyPress: function arrowKeyPress(evt) {
- if (this._isValidEventForArrowKeyPress(evt)) {
- evt.preventDefault();
- // broadcast that a move has started
- this.emit('move:start', { 'currentSelection': this.selectedNodes });
- this.saveNodeInfo();
- var _moveNodesForArrowKey = this._moveNodesForArrowKeyPress(evt),
- positionUpdateArray = _moveNodesForArrowKey.positionUpdateArray,
- sMessage = _moveNodesForArrowKey.sMessage;
- this._updateModelAfterArrowKeyPress(positionUpdateArray, sMessage);
- }
- },
- _resetSelectionState: function _resetSelectionState() {
- if (this.selectedNodes) {
- // If we are dragging and the selection changes, we just need to cancel the drag.
- if (this._dndManager) {
- this._dndManager.resetDragging();
- }
- this.dropInfo = {};
- this.clearStartEventHandlers();
- }
- },
- /**
- * Called to stop the dragging operation
- */
- dragStopEvent: function dragStopEvent(evt, dropContext) {
- if (this.isDragging) {
- this._showingLayoutIndicator = false;
- this.handleDrop(dropContext);
- this.resetDraggingState();
- this._$avatarContainer[0].className = 'avatarContainer avatarContainerNotDragging';
- // While moving, the target will lose focus. We restore the focus once we are done.
- this.$lastFocus.focus();
- //trigger event stopMove
- this.controller.eventRouter.trigger('widget:stopMove');
- this.emit('move:end', { 'currentSelection': this.selectedNodes });
- this.refreshProperties();
- this._guidelineManager.hideAll();
- this._guidelineManager.finish();
- this._layoutIndicator.remove();
- }
- },
- resetDraggingState: function resetDraggingState() {
- this.isDragging = false;
- $(this.selectedNodes).removeClass('nodeDragging');
- this.dropInfo.operation = null;
- },
- handleDrop: function handleDrop() /*dropContext*/{
- // To be overriden by sub-classes
- },
- /**
- * Event handler that will initiate the dragging operation on mouse down
- *
- * @param evt
- * @returns {Boolean} - indicate whether we want to stop the propagation of the event
- */
- dragStartMouseEvent: function dragStartMouseEvent(evt) {
- if (this._isTouch) {
- // We have a touch event, we skip mouse events.
- return false;
- }
- if (evt.type === 'mousedown' && ((evt.buttons || evt.which) !== 1 || evt.ctrlKey)) {
- return;
- }
- this.initiateDragging(evt);
- },
- /**
- * Helper method that will setup all the handlers needed by the dragging operation This method is called when we want to start a dragging operation
- *
- * @param evt
- */
- initiateDragging: function initiateDragging(evt, nodeMove) {
- // ignore drag unless it came from the moveHandle or the margin of a data widget
- var wasDraggedFromMoveHandle = $(evt.target).closest('.moveHandle').length === 1;
- var wasDraggedFromWidgetMargin = $(evt.target).closest('.widgetContent').length === 0;
- if (!wasDraggedFromMoveHandle && !wasDraggedFromWidgetMargin) {
- return true;
- }
- evt.preventDefault();
- this.saveNodeInfo();
- this._isTouch = evt.isTouch;
- this.$lastFocus = $(':focus');
- // get the current drop zone that the node belongs to
- // We need to pass this to the Dnd manager in case we are dropping in a drop zone that support scrolling.
- // The mouse might not be over the drop zone or any other drop zone and we should still be able to move the widget within its current drop zone
- var node = null;
- var currentDropZone = null;
- if (!nodeMove) {
- node = this._getTargetWidgetNode(evt);
- } else {
- node = nodeMove;
- }
- if ($(node).hasClass('moveDisabled') || !node) {
- //If the node implements its own drag and drop behaviour externally (ie: vizControlWidget),
- //it must disable widget drag move by setting class 'moveDisabled'.
- return;
- }
- currentDropZone = node._layout ? node._layout.parentLayout.domNode : null;
- this._dndManager.startDrag({
- event: evt,
- type: this.getDragDataType(),
- data: this.dropInfo,
- avatar: null,
- callerCallbacks: {
- onMove: this.dragMoveEvent.bind(this),
- onDragDone: this.dragStopEvent.bind(this)
- },
- moveXThreshold: 3,
- moveYThreshold: 3,
- currentDropZoneNode: currentDropZone
- });
- },
- _getTargetWidgetNode: function _getTargetWidgetNode(e) {
- var $target = $(e.target);
- if (!$target.is('.widget')) {
- var $targetParentWidget = $target.parents('.widget');
- if ($targetParentWidget.length) {
- $target = $targetParentWidget;
- } else {
- $target = $target.parents('.pagegroup');
- }
- }
- return $target[0];
- },
- getDragDataType: function getDragDataType() {
- return null;
- },
- /**
- * Event handler called when the dragged items are being moved
- *
- * @param evt
- * @returns {Boolean} indicates if we want to stop the propagation of the event
- */
- dragMoveEvent: function dragMoveEvent(evt, dragContext) {
- var _this2 = this;
- eventHelper.fixEvent(evt);
- var i = void 0,
- iLen = void 0;
- if (!this.isDragging) {
- this._$avatarContainer[0].className = 'avatarContainer avatarContainerDragging';
- this.emit('move:start', { 'currentSelection': this.selectedNodes });
- this.prepareDropInfo(this.dropInfo);
- this.isDragging = true;
- var selectedNodes = [],
- nodes = [];
- for (i = 0, iLen = this.dropInfo.nodeInfoList.length; i < iLen; i++) {
- var nodeInfo = this.dropInfo.nodeInfoList[i];
- selectedNodes.push(nodeInfo.node);
- // this is the actual of the widget in the viewport
- var nodePosition = nodeInfo.node.getBoundingClientRect();
- this.prepareNodeForMove(nodeInfo.node);
- nodes.push(nodeInfo.node);
- var parentNodePosition = nodeInfo.node.parentNode.getBoundingClientRect();
- // move to clientX/clientY, then save the offsetX/offsetY of the click to take into account when we move the element.
- var coords = utils.getRotatedCoordinates(nodeInfo.node.parentNode, evt.clientX, evt.clientY, parentNodePosition);
- $(nodeInfo.node).addClass('nodeDragging');
- utils.setTopLeft(nodeInfo.node, coords.y, coords.x);
- var rotateOffset = {
- top: $(nodeInfo.node).position().top - nodeInfo.node.offsetTop,
- left: $(nodeInfo.node).position().left - nodeInfo.node.offsetLeft
- };
- // Get the offset of the mouse down position and the node we're moving
- nodeInfo.offsety = dragContext.dragObject.startPosition.y - nodePosition.top + rotateOffset.top;
- nodeInfo.offsetx = dragContext.dragObject.startPosition.x - nodePosition.left + rotateOffset.left;
- nodeInfo.parentNodeRect = parentNodePosition;
- nodeInfo.position = nodePosition;
- }
- // get the size of the drag box
- this.dropInfo.dragBoxSize = this._calculateBoxSize(this.dropInfo.nodeInfoList);
- // initialize the drag box
- if (dragContext.dragObject && dragContext.dropTargetNode) {
- dragContext.dropTargetNodeOffset = $(dragContext.dropTargetNode).offset();
- var dragBox = this._convertCoordinates(this.dropInfo.dragBoxSize, evt.clientX - this.dropInfo.dragBoxSize.offsetx, evt.clientY - this.dropInfo.dragBoxSize.offsety, dragContext.dropTargetNode, dragContext.dropTargetNodeOffset);
- dragContext.dragObject.dragBox = dragBox;
- dragContext.dragObject.dragBox.ids = this.getIds(this.dropInfo.nodeInfoList).ids;
- this._guidelineManager.getReady(dragContext.dropTargetNode, dragContext.dragObject);
- this.postNodeForMove(nodes);
- }
- this.controller.eventRouter.trigger('widget:startMove', { selectedNodes: selectedNodes });
- $('.popover').popover('hide');
- if (this.dropInfo.nodeInfoList.length > 0) {
- this._showingLayoutIndicator = false;
- // Don't start showing the drag coordinates right away since it's a pretty big perf hit on IE.
- // Let the move start doing its thing before we show the coordinates
- setTimeout(function () {
- _this2._layoutIndicator.render();
- _this2._layoutIndicator.show();
- _this2._showingLayoutIndicator = true;
- }, 150);
- }
- }
- // get the revised offsets, if applicable
- var revisedOffsets = this._preMoveNode(this.dropInfo.dragBoxSize, dragContext, evt, this.dropInfo.nodeInfoList[0]);
- // calculate the position of each node
- for (i = 0, iLen = this.dropInfo.nodeInfoList.length; i < iLen; i++) {
- this.moveNode(this.dropInfo.nodeInfoList[i], evt, dragContext, revisedOffsets);
- }
- return false;
- },
- /**
- * Calculate the dimensions for a conceptual box which covers all
- * selected shapes which need to be dragged on the canvas
- *
- * @param nodes
- * List of selected nodes
- *
- * @return boxDimensions
- * Object containing the dimensions of the drag box, containing all selected widgets
- */
- _calculateBoxSize: function _calculateBoxSize(nodes) {
- var boxDimensions = {
- top: Number.MAX_SAFE_INTEGER,
- left: Number.MAX_SAFE_INTEGER,
- bottom: Number.MIN_SAFE_INTEGER,
- right: Number.MIN_SAFE_INTEGER,
- offsety: 0,
- offsetx: 0,
- width: 0,
- height: 0
- };
- var i = void 0,
- iLen = void 0;
- for (i = 0, iLen = nodes.length; i < iLen; i++) {
- var node = nodes[i];
- if (node.position.top < boxDimensions.top) {
- boxDimensions.top = node.position.top;
- boxDimensions.offsety = node.offsety;
- }
- if (node.position.left < boxDimensions.left) {
- boxDimensions.left = node.position.left;
- boxDimensions.offsetx = node.offsetx;
- }
- if (node.position.bottom > boxDimensions.bottom) {
- boxDimensions.bottom = node.position.bottom;
- }
- if (node.position.right > boxDimensions.right) {
- boxDimensions.right = node.position.right;
- }
- }
- boxDimensions.width = boxDimensions.right - boxDimensions.left;
- boxDimensions.height = boxDimensions.bottom - boxDimensions.top;
- return boxDimensions;
- },
- /**
- * Perform some necessary calculations before calling moveNode(), also
- * return a set of revised offsets if applicable
- *
- * @param dragBoxSize
- * @param dragContext
- * @param evt
- * @param nodeInfo
- *
- * @return revisedOffsets
- * Either x and y will be valid numbers or NULLs
- * When null used in calculation, it is converted into number 0. So we can safely return NULL
- * and use it in our calculations to determine whether we need to snap to grid or not.
- */
- _preMoveNode: function _preMoveNode(dragBoxSize, dragContext, evt, nodeInfo) {
- var revisedOffsets = { x: 0, y: 0 };
- if (dragContext.dropTargetNode && nodeInfo.node._layout) {
- dragContext.dropTargetNodeOffset = $(dragContext.dropTargetNode).offset();
- var dragBox = this._convertCoordinates(dragBoxSize, evt.clientX - dragBoxSize.offsetx, evt.clientY - dragBoxSize.offsety, dragContext.dropTargetNode, dragContext.dropTargetNodeOffset);
- var revisedCoordinates = this._getNewCoordinates(dragBox);
- revisedOffsets = this.getRevisedOffsets(revisedCoordinates);
- }
- if (this._showingLayoutIndicator && nodeInfo.node._layout) {
- this._layoutIndicator.updatePosition({ 'top': evt.clientY, 'left': evt.clientX });
- this._updateLayoutIndicatorContent(evt, dragBoxSize, nodeInfo, revisedOffsets, dragContext);
- }
- return revisedOffsets;
- },
- /**
- * Prepare the drop info object that will be delivered to the drop zone.
- * This method is called once when a drag starts
- */
- prepareDropInfo: function prepareDropInfo() /*dropInfo*/{
- // To be overriden by sub-classes
- },
- /**
- * Prepare the nodes to be dragged. This method is called once for every selected node
- * @param {object} n - the dom node being dragged
- */
- prepareNodeForMove: function prepareNodeForMove(n) {
- return n;
- },
- /**
- * Invoke after all nodes are prepared for move
- *
- * @param {array} nodes - collection of nodes
- */
- postNodeForMove: function postNodeForMove() /*nodes*/{
- // To be overriden by sub-classes
- },
- /**
- * Retrieve collection of selected Dom nodes
- *
- * @return {array} collection of Dom nodes
- */
- getSelectedDomNodes: function getSelectedDomNodes() {
- var i, iLen;
- var nodeInfo,
- nodes = [];
- for (i = 0, iLen = this.dropInfo.nodeInfoList.length; i < iLen; i++) {
- nodeInfo = this.dropInfo.nodeInfoList[i];
- nodes.push(nodeInfo.node);
- }
- return nodes;
- },
- /**
- * Move selected nodes to a drop zone
- *
- * @param nodeInfo
- * @param evt
- * @param dragContext
- * @param revisedOffsets
- */
- moveNode: function moveNode(nodeInfo, evt, dragContext, revisedOffsets) {
- var node = nodeInfo.node;
- var coords = {};
- // revisedOffsets will always be a valid object, the values of x and y might be a number or null
- // if revisedOffsets x and y are numbers, that means snap to grid is on
- // if revisedOffsets x and y are nulls, that means snap to grid is not on (null converted to number is 0)
- coords = utils.getRotatedCoordinates(node.parentNode, evt.clientX - nodeInfo.offsetx - revisedOffsets.x, evt.clientY - nodeInfo.offsety - revisedOffsets.y, nodeInfo.parentNodeRect);
- utils.setTopLeft(node, coords.y, coords.x);
- this.handleMove(nodeInfo, coords, dragContext);
- },
- _formatPosition: function _formatPosition(node, dragBox, containerNode) {
- if (node._layout) {
- var layoutPositioning = node._layout.model.getValueFromSelfOrParent('layoutPositioning');
- if (layoutPositioning === 'relative') {
- var dragBoxModel = { top: dragBox.top + 'px', left: dragBox.left + 'px' };
- var pageSize = { width: $(containerNode).width(), height: $(containerNode).height() };
- PxPercentUtil.changePixelPropertiesToPercent(dragBoxModel, pageSize, 1);
- dragBox.top = Formatter.formatNumber(dragBoxModel.top.replace(/%/g, '')) + ' %';
- dragBox.left = Formatter.formatNumber(dragBoxModel.left.replace(/%/g, '')) + ' %';
- } else {
- dragBox.top = Formatter.formatNumber(dragBox.top) + ' ' + stringResources.get('pixelUnit') + ' ';
- dragBox.left = Formatter.formatNumber(dragBox.left) + ' ' + stringResources.get('pixelUnit') + ' ';
- }
- }
- return dragBox;
- },
- getIds: function getIds(nodeInfoList) {
- var dragBox = nodeInfoList.reduce(function (box, current) {
- box.ids.push(current.node.id);
- return box;
- }, {
- ids: []
- });
- return dragBox;
- },
- getRevisedOffsets: function getRevisedOffsets(revisedCoordinates) {
- var revisedOffset = {};
- if (revisedCoordinates.left === null) {
- revisedOffset.x = revisedCoordinates.right;
- } else if (revisedCoordinates.right === null) {
- revisedOffset.x = revisedCoordinates.left;
- } else {
- revisedOffset.x = Math.abs(revisedCoordinates.left) < Math.abs(revisedCoordinates.right) ? revisedCoordinates.left : revisedCoordinates.right;
- }
- if (revisedCoordinates.top === null) {
- revisedOffset.y = revisedCoordinates.bottom;
- } else if (revisedCoordinates.bottom === null) {
- revisedOffset.y = revisedCoordinates.top;
- } else {
- revisedOffset.y = Math.abs(revisedCoordinates.bottom) < Math.abs(revisedCoordinates.top) ? revisedCoordinates.bottom : revisedCoordinates.top;
- }
- return revisedOffset;
- },
- _convertCoordinates: function _convertCoordinates(dragBoxSize, x, y, dropTargetNode, dropTargetNodeOffset) {
- var dragBox = {};
- if (dropTargetNode) {
- var rect = dropTargetNodeOffset || $(dropTargetNode).offset();
- var xNew = Math.round(x - rect.left + dropTargetNode.scrollLeft);
- var yNew = Math.round(y - rect.top + dropTargetNode.scrollTop);
- dragBox = {
- top: yNew,
- right: xNew + dragBoxSize.width,
- bottom: yNew + dragBoxSize.height,
- left: xNew,
- center_x: xNew + dragBoxSize.width / 2,
- center_y: yNew + dragBoxSize.height / 2
- };
- }
- return dragBox;
- },
- _getNewCoordinates: function _getNewCoordinates(dragBox) {
- this._guidelineManager.hideAll();
- return this._guidelineManager.getSnapCoordinates(dragBox);
- },
- handleMove: function handleMove() /*nodeInfo, coords, dragContext*/{
- // To be overriden by sub-classes
- },
- /**
- * Save the node original position. To be used when we want to restore the original position
- *
- * @param node
- */
- setNodePositionInfo: function setNodePositionInfo(nodeInfo) {
- var node = nodeInfo.node;
- nodeInfo.oldValues = {
- 'top': node.style.top,
- 'left': node.style.left,
- 'height': node.style.height,
- 'width': node.style.width,
- 'parent': node.parentElement,
- 'nextSibling': node.nextSibling
- };
- while (nodeInfo.oldValues.nextSibling && this.selectedNodes.indexOf(nodeInfo.oldValues.nextSibling) > -1) {
- nodeInfo.oldValues.nextSibling = nodeInfo.oldValues.nextSibling.nextSibling;
- }
- },
- moveByProperty: function moveByProperty(style, value, units) {
- var sanitizedValue = value;
- var sizeProperty = style === 'left' ? 'width' : 'height';
- var parentSize = this._getParentDim(sizeProperty, this.propertyNodes);
- var offset = this._getWidgetOffset(sizeProperty, this.propertyNodes);
- offset = offset + this._getWidgetSize(sizeProperty, this.propertyNodes); // correct the offset since move moves the left top edge and doesnt count widget size
- if (units === 'px') {
- var minValueFn = style === 'left' ? this.propertyNodes[0]._layout.parentLayout.getMinimumLeft : this.propertyNodes[0]._layout.parentLayout.getMinimumTop;
- sanitizedValue = Math.max(minValueFn(), sanitizedValue);
- sanitizedValue = Math.min(parentSize - offset, sanitizedValue);
- } else {
- //% size
- sanitizedValue = this._sanitizePercentSizeOnPage(sanitizedValue, sizeProperty);
- }
- var propertyValue = sanitizedValue + units;
- var positionUpdateArray = [];
- this.propertyNodes.forEach(function (node) {
- var styleUpdate = {};
- styleUpdate[style] = propertyValue;
- positionUpdateArray.push({
- style: styleUpdate,
- id: node._layout.model.id
- });
- });
- this.updateModel(positionUpdateArray);
- return sanitizedValue;
- },
- _processChange: function _processChange(id, numericValue, units) {
- return this.moveByProperty(id === 'left' ? 'left' : 'top', numericValue, units);
- },
- /* TODO: This has been replaced by API work. Remove the unused functions, and the base class if no longer needed. */
- getProperties: function getProperties() {
- Move.inherited('getProperties', this, arguments);
- var unit = this.getUnitsFromValue(this.propertyNodes[0]._layout.model.style.left);
- var propValues = [{
- units: unit,
- getProp: function getProp(node) {
- return node._layout.model.style.left;
- }
- }, {
- units: unit,
- getProp: function getProp(node) {
- return node._layout.model.style.top;
- }
- }];
- this._getValuesForProperties(propValues);
- var properties = [{
- 'type': 'SectionLabel',
- 'label': stringResources.get('propPositionLabel'),
- 'tabName': stringResources.get('tabName_general'),
- 'sectionName': stringResources.get('sectionName_layout'),
- 'sectionOpened': true,
- order: 20
- }, {
- 'type': 'Split',
- 'name': 'moveWidgetSplit',
- 'id': 'moveWidgetSplit',
- 'tabName': stringResources.get('tabName_general'),
- 'sectionName': stringResources.get('sectionName_layout'),
- order: 21,
- 'items': [{
- align: 'left',
- items: [{
- 'type': 'InputLabel',
- 'label': stringResources.get('propPositionXAxis'),
- 'name': 'left',
- 'id': 'left',
- 'tabName': stringResources.get('tabName_general'),
- 'sectionName': stringResources.get('sectionName_layout'),
- 'readOnly': false,
- 'value': propValues[0].value,
- 'onChangeValueHold': propValues[0].value,
- 'multiline': true,
- 'handleReturnKey': true,
- 'units': unit,
- 'onChange': this.wrapChangeProperty()
- }]
- }, {
- align: 'right',
- items: [{
- 'type': 'InputLabel',
- 'label': stringResources.get('propPositionYAxis'),
- 'name': 'top',
- 'id': 'top',
- 'tabName': stringResources.get('tabName_general'),
- 'sectionName': stringResources.get('sectionName_layout'),
- 'readOnly': false,
- 'value': propValues[1].value,
- 'onChangeValueHold': propValues[1].value,
- 'multiline': true,
- 'handleReturnKey': true,
- 'units': unit,
- 'onChange': this.wrapChangeProperty()
- }]
- }]
- }];
- if (this.showStyleProperties(this.propertyNodes[0])) {
- return properties;
- } else {
- return [];
- }
- }
- });
- return Move;
- });
- //# sourceMappingURL=Move.js.map
|