'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 = $('
'); $('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