From 68914be67df3893f6ea414b6bb6fed0236c8a53d Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Mon, 5 Feb 2018 16:57:44 -0800 Subject: [PATCH 1/4] Wire up bubble dragging, with coordinate problems --- core/bubble.js | 92 ++++++++++---- core/bubble_dragger.js | 282 +++++++++++++++++++++++++++++++++++++++++ core/gesture.js | 109 +++++++++++++++- 3 files changed, 457 insertions(+), 26 deletions(-) create mode 100644 core/bubble_dragger.js diff --git a/core/bubble.js b/core/bubble.js index df3b69e3f..e6a8ada2a 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -279,28 +279,33 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { * @private */ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) { - this.promote_(); - Blockly.Bubble.unbindDragEvents_(); - if (Blockly.utils.isRightButton(e)) { - // No right-click. - e.stopPropagation(); - return; - } else if (Blockly.utils.isTargetInput(e)) { - // When focused on an HTML text input widget, don't trap any events. - return; + var gesture = this.workspace_.getGesture(e); + if (gesture) { + gesture.handleBubbleStart(e, this); } - // Left-click (or middle click) - this.workspace_.startDrag(e, new goog.math.Coordinate( - this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_, - this.relativeTop_)); - Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, - 'mouseup', this, Blockly.Bubble.bubbleMouseUp_); - Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, - 'mousemove', this, this.bubbleMouseMove_); - Blockly.hideChaff(); - // This event has been handled. No need to bubble up to the document. - e.stopPropagation(); + // this.promote_(); + // Blockly.Bubble.unbindDragEvents_(); + // if (Blockly.utils.isRightButton(e)) { + // // No right-click. + // e.stopPropagation(); + // return; + // } else if (Blockly.utils.isTargetInput(e)) { + // // When focused on an HTML text input widget, don't trap any events. + // return; + // } + // // Left-click (or middle click) + // this.workspace_.startDrag(e, new goog.math.Coordinate( + // this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_, + // this.relativeTop_)); + + // Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, + // 'mouseup', this, Blockly.Bubble.bubbleMouseUp_); + // Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, + // 'mousemove', this, this.bubbleMouseMove_); + // Blockly.hideChaff(); + // // This event has been handled. No need to bubble up to the document. + // e.stopPropagation(); }; /** @@ -445,8 +450,11 @@ Blockly.Bubble.prototype.positionBubble_ = function() { left += this.relativeLeft_; } var top = this.relativeTop_ + this.anchorXY_.y; - this.bubbleGroup_.setAttribute('transform', - 'translate(' + left + ',' + top + ')'); + this.moveTo_(left, top); +}; + +Blockly.Bubble.prototype.moveTo_ = function(x, y) { + this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')'); }; /** @@ -595,3 +603,43 @@ Blockly.Bubble.prototype.dispose = function() { this.content_ = null; this.shape_ = null; }; + +Blockly.Bubble.prototype.moveToDragSurface = function(dragSurface) { + //var bubbleXY = this.getRelativeToSurfaceXY(); + //var anchorXY = this.getAnchorRelativeToSurfaceXY(); + + // TODO: check RTL. + var x = this.anchorXY_.x + this.relativeLeft_; + var y = this.anchorXY_.y + this.relativeTop_; + this.savedRelativeXY_ = + new goog.math.Coordinate(this.relativeLeft_, this.relativeTop_); + this.savedAnchorXY_ = this.anchorXY_; + this.moveTo_(0, 0); + dragSurface.translateSurface(x, y); + // Execute the move on the top-level SVG component. + dragSurface.setBlocksAndShow(this.bubbleGroup_); +}; + +Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) { + console.log(newLoc); + dragSurface.translateSurface(newLoc.x, newLoc.y); + this.relativeLeft_ = this.savedRelativeXY_.x + newLoc.x; + this.relativeTop_ = this.savedRelativeXY_.y + newLoc.y; + this.renderArrow_(); +}; + +Blockly.Bubble.prototype.moveOffDragSurface = function(dragSurface, newXY) { + + //this.savedAnchorXY_ = this.anchorXY_; + this.anchorXY_ = this.savedAnchorXY_; + this.moveTo_(newXY.x, newXY.y); + dragSurface.clearAndHide(this.workspace_.getBubbleCanvas()); +}; + + +Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() { + // This may not be quite right. It's probably the top-left of the bubble + // group. + return new goog.math.Coordinate(this.anchorXY_.x + this.relativeLeft_, + this.anchorXY_.y + this.relativeTop_); +}; diff --git a/core/bubble_dragger.js b/core/bubble_dragger.js new file mode 100644 index 000000000..e66b500bf --- /dev/null +++ b/core/bubble_dragger.js @@ -0,0 +1,282 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2018 Google Inc. + * https://developers.google.com/blockly/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Methods for dragging a bubble visually. + * @author fenichel@google.com (Rachel Fenichel) + */ +'use strict'; + +goog.provide('Blockly.BubbleDragger'); + +goog.require('Blockly.DraggedConnectionManager'); + +goog.require('goog.math.Coordinate'); +goog.require('goog.asserts'); + + +/** + * Class for a block dragger. It moves blocks around the workspace when they + * are being dragged by a mouse or touch. + * @param {!Blockly.Bubble} bubble The bubble to drag. + * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on. + * @constructor + */ +Blockly.BubbleDragger = function(bubble, workspace) { + /** + * The top block in the stack that is being dragged. + * @type {!Blockly.BlockSvg} + * @private + */ + this.draggingBubble_ = bubble; + + /** + * The workspace on which the bubble is being dragged. + * @type {!Blockly.WorkspaceSvg} + * @private + */ + this.workspace_ = workspace; + + /** + * Object that keeps track of connections on dragged blocks. + * @type {!Blockly.DraggedConnectionManager} + * @private + */ + this.draggedConnectionManager_ = null; + // new Blockly.DraggedConnectionManager( + // this.draggingBubble_); + + /** + * Which delete area the mouse pointer is over, if any. + * One of {@link Blockly.DELETE_AREA_TRASH}, + * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. + * @type {?number} + * @private + */ + this.deleteArea_ = null; + + /** + * Whether the block would be deleted if dropped immediately. + * @type {boolean} + * @private + */ + this.wouldDeleteBlock_ = false; + + /** + * The location of the top left corner of the dragging block at the beginning + * of the drag in workspace coordinates. + * @type {!goog.math.Coordinate} + * @private + */ + this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY(); + + // TODO: validate, getters, etc. + this.dragSurface_ = workspace.blockDragSurface_; +}; + +/** + * Sever all links from this object. + * @package + */ +Blockly.BubbleDragger.prototype.dispose = function() { + this.draggingBubble_ = null; + this.workspace_ = null; + this.startWorkspace_ = null; + this.dragIconData_.length = 0; + + if (this.draggedConnectionManager_) { + this.draggedConnectionManager_.dispose(); + this.draggedConnectionManager_ = null; + } +}; + +/** + * Start dragging a block. This includes moving it to the drag surface. + * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has + * moved from the position at mouse down, in pixel units. + * @package + */ +Blockly.BubbleDragger.prototype.startBubbleDrag = function(currentDragDeltaXY) { + console.log('starting bubble drag'); + if (!Blockly.Events.getGroup()) { + Blockly.Events.setGroup(true); + } + + this.workspace_.setResizesEnabled(false); + + // if (this.draggingBubble_.getParent()) { + // this.draggingBubble_.unplug(); + // var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); + // var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); + + // this.draggingBubble_.translate(newLoc.x, newLoc.y); + // this.draggingBubble_.disconnectUiEffect(); + // } + //this.draggingBubble_.setDragging(true); + // For future consideration: we may be able to put moveToDragSurface inside + // the block dragger, which would also let the block not track the block drag + // surface. + this.draggingBubble_.moveToDragSurface(this.dragSurface_); + + if (this.workspace_.toolbox_) { + var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : + 'blocklyToolboxGrab'; + this.workspace_.toolbox_.addStyle(style); + } +}; + +/** + * Execute a step of block dragging, based on the given event. Update the + * display accordingly. + * @param {!Event} e The most recent move event. + * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has + * moved from the position at the start of the drag, in pixel units. + * @package + */ +Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) { + var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); + var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); + + this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc); + //this.dragIcons_(delta); + + this.deleteArea_ = this.workspace_.isDeleteArea(e); + //this.draggedConnectionManager_.update(delta, this.deleteArea_); + + //this.updateCursorDuringBubbleDrag_(); +}; + +/** + * Finish a block drag and put the block back on the workspace. + * @param {!Event} e The mouseup/touchend event. + * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has + * moved from the position at the start of the drag, in pixel units. + * @package + */ +Blockly.BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) { + // Make sure internal state is fresh. + this.dragBubble(e, currentDragDeltaXY); + this.dragIconData_ = []; + + Blockly.BlockSvg.disconnectUiStop_(); + + var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); + var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); + this.draggingBubble_.moveOffDragSurface(this.dragSurface_, newLoc); + + var deleted = true;//this.maybeDeleteBlock_(); + if (!deleted) { + // These are expensive and don't need to be done if we're deleting. + this.draggingBubble_.moveConnections_(delta.x, delta.y); + this.draggingBubble_.setDragging(false); + //this.draggedConnectionManager_.applyConnections(); + this.draggingBubble_.render(); + this.fireMoveEvent_(); + this.draggingBubble_.scheduleSnapAndBump(); + } + this.workspace_.setResizesEnabled(true); + + if (this.workspace_.toolbox_) { + var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : + 'blocklyToolboxGrab'; + this.workspace_.toolbox_.removeStyle(style); + } + Blockly.Events.setGroup(false); +}; + +/** + * Fire a move event at the end of a block drag. + * @private + */ +Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() { + var event = new Blockly.Events.BlockMove(this.draggingBubble_); + event.oldCoordinate = this.startXY_; + event.recordNew(); + Blockly.Events.fire(event); +}; + +/** + * Shut the trash can and, if necessary, delete the dragging block. + * Should be called at the end of a block drag. + * @return {boolean} whether the block was deleted. + * @private + */ +Blockly.BubbleDragger.prototype.maybeDeleteBubble_ = function() { + var trashcan = this.workspace_.trashcan; + + if (this.wouldDeleteBlock_) { + if (trashcan) { + goog.Timer.callOnce(trashcan.close, 100, trashcan); + } + // Fire a move event, so we know where to go back to for an undo. + this.fireMoveEvent_(); + this.draggingBubble_.dispose(false, true); + } else if (trashcan) { + // Make sure the trash can is closed. + trashcan.close(); + } + return this.wouldDeleteBlock_; +}; + +/** + * Update the cursor (and possibly the trash can lid) to reflect whether the + * dragging block would be deleted if released immediately. + * @private + */ +Blockly.BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() { + this.wouldDeleteBlock_ = false; //this.draggedConnectionManager_.wouldDeleteBlock(); + var trashcan = this.workspace_.trashcan; + if (this.wouldDeleteBlock_) { + this.draggingBubble_.setDeleteStyle(true); + if (this.deleteArea_ == Blockly.DELETE_AREA_TRASH && trashcan) { + trashcan.setOpen_(true); + } + } else { + this.draggingBubble_.setDeleteStyle(false); + if (trashcan) { + trashcan.setOpen_(false); + } + } +}; + +/** + * Convert a coordinate object from pixels to workspace units, including a + * correction for mutator workspaces. + * This function does not consider differing origins. It simply scales the + * input's x and y values. + * @param {!goog.math.Coordinate} pixelCoord A coordinate with x and y values + * in css pixel units. + * @return {!goog.math.Coordinate} The input coordinate divided by the workspace + * scale. + * @private + */ +Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) { + var result = new goog.math.Coordinate(pixelCoord.x / this.workspace_.scale, + pixelCoord.y / this.workspace_.scale); + if (this.workspace_.isMutator) { + // If we're in a mutator, its scale is always 1, purely because of some + // oddities in our rendering optimizations. The actual scale is the same as + // the scale on the parent workspace. + // Fix that for dragging. + var mainScale = this.workspace_.options.parentWorkspace.scale; + result = result.scale(1 / mainScale); + } + return result; +}; diff --git a/core/gesture.js b/core/gesture.js index 8fb8938c0..f7b9262ce 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -28,6 +28,7 @@ goog.provide('Blockly.Gesture'); goog.require('Blockly.BlockDragger'); +goog.require('Blockly.BubbleDragger'); goog.require('Blockly.constants'); goog.require('Blockly.FlyoutDragger'); goog.require('Blockly.Tooltip'); @@ -68,6 +69,14 @@ Blockly.Gesture = function(e, creatorWorkspace) { */ this.currentDragDeltaXY_ = 0; + /** + * The bubble that the gesture started on, or null if it did not start on a + * bubble. + * @type {Blockly.Bubble} + * @private + */ + this.startBubble_ = null; + /** * The field that the gesture started on, or null if it did not start on a * field. @@ -136,6 +145,13 @@ Blockly.Gesture = function(e, creatorWorkspace) { */ this.isDraggingBlock_ = false; + /** + * Whether the bubble is currently being dragged. + * @type {boolean} + * @private + */ + this.isDraggingBubble_ = false; + /** * The event that most recently updated this gesture. * @type {!Event} @@ -159,6 +175,13 @@ Blockly.Gesture = function(e, creatorWorkspace) { */ this.onUpWrapper_ = null; + /** + * The object tracking a bubble drag, or null if none is in progress. + * @type {Blockly.BubbleDragger} + * @private + */ + this.bubbleDragger_ = null; + /** * The object tracking a block drag, or null if none is in progress. * @type {Blockly.BlockDragger} @@ -235,6 +258,10 @@ Blockly.Gesture.prototype.dispose = function() { this.workspaceDragger_.dispose(); this.workspaceDragger_ = null; } + if (this.bubbleDragger_) { + this.bubbleDragger_.dispose(); + this.bubbleDragger_ = null; + } }; /** @@ -312,6 +339,25 @@ Blockly.Gesture.prototype.updateIsDraggingFromFlyout_ = function() { return false; }; +/** + * Update this gesture to record whether a bubble is being dragged. + * This function should be called on a mouse/touch move event the first time the + * drag radius is exceeded. It should be called no more than once per gesture. + * If a bubble should be dragged this function creates the necessary + * BubbleDragger and starts the drag. + * @return {boolean} true if a bubble is being dragged. + * @private + */ +Blockly.Gesture.prototype.updateIsDraggingBubble_ = function() { + if (!this.startBubble_) { + return false; + } + + this.isDraggingBubble_ = true; + this.startDraggingBubble_(); + return true; +}; + /** * Update this gesture to record whether a block is being dragged. * This function should be called on a mouse/touch move event the first time the @@ -377,7 +423,11 @@ Blockly.Gesture.prototype.updateIsDragging_ = function() { 'updateIsDragging_ should only be called once per gesture.'); this.calledUpdateIsDragging_ = true; - // First check if it was a block drag. + // First check if it was a bubble drag. Bubbles always sit on top of blocks. + if (this.updateIsDraggingBubble_()) { + return; + } + // Then check if it was a block drag. if (this.updateIsDraggingBlock_()) { return; } @@ -397,6 +447,19 @@ Blockly.Gesture.prototype.startDraggingBlock_ = function() { this.currentDragDeltaXY_); }; +/** + * Create a bubble dragger and start dragging the selected bubble. + * TODO (fenichel): Possibly combine this and startDraggingBlock_. + * @private + */ +Blockly.Gesture.prototype.startDraggingBubble_ = function() { + this.bubbleDragger_ = new Blockly.BubbleDragger(this.startBubble_, + this.startWorkspace_); + this.bubbleDragger_.startBubbleDrag(this.currentDragDeltaXY_); + this.bubbleDragger_.dragBubble(this.mostRecentEvent_, + this.currentDragDeltaXY_); +}; + /** * Start a gesture: update the workspace to indicate that a gesture is in * progress and bind mousemove and mouseup handlers. @@ -470,6 +533,9 @@ Blockly.Gesture.prototype.handleMove = function(e) { } else if (this.isDraggingBlock_) { this.blockDragger_.dragBlock(this.mostRecentEvent_, this.currentDragDeltaXY_); + } else if (this.isDraggingBubble_) { + this.bubbleDragger_.dragBubble(this.mostRecentEvent_, + this.currentDragDeltaXY_); } e.preventDefault(); e.stopPropagation(); @@ -492,7 +558,11 @@ Blockly.Gesture.prototype.handleUp = function(e) { // The ordering of these checks is important: drags have higher priority than // clicks. Fields have higher priority than blocks; blocks have higher // priority than workspaces. - if (this.isDraggingBlock_) { + // The ordering within drags does not matter, because the three types of + // dragging are exclusive. + if (this.isDraggingBubble_) { + this.bubbleDragger_.endBubbleDrag(e, this.currentDragDeltaXY_); + } else if (this.isDraggingBlock_) { this.blockDragger_.endBlockDrag(e, this.currentDragDeltaXY_); } else if (this.isDraggingWorkspace_) { this.workspaceDragger_.endDrag(this.currentDragDeltaXY_); @@ -522,7 +592,10 @@ Blockly.Gesture.prototype.cancel = function() { return; } Blockly.longStop_(); - if (this.isDraggingBlock_) { + if (this.isDraggingBubble_) { + this.bubbleDragger_.endBubbleDrag(this.mostRecentEvent_, + this.currentDragDeltaXY_); + } else if (this.isDraggingBlock_) { this.blockDragger_.endBlockDrag(this.mostRecentEvent_, this.currentDragDeltaXY_); } else if (this.isDraggingWorkspace_) { @@ -546,6 +619,7 @@ Blockly.Gesture.prototype.handleRightClick = function(e) { this.startWorkspace_.showContextMenu_(e); } + // TODO: Handle right-click on a bubble. e.preventDefault(); e.stopPropagation(); @@ -595,6 +669,20 @@ Blockly.Gesture.prototype.handleBlockStart = function(e, block) { this.mostRecentEvent_ = e; }; +/** + * Handle a mousedown/touchstart event on a bubble. + * @param {!Event} e A mouse down or touch start event. + * @param {!Blockly.Bubble} bubble The bubble the event hit. + * @package + */ +Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) { + goog.asserts.assert(!this.hasStarted_, + 'Tried to call gesture.handleBubbleStart, but the gesture had already ' + + 'been started.'); + this.setStartBubble(bubble); + this.mostRecentEvent_ = e; +}; + /* Begin functions defining what actions to take to execute clicks on each type * of target. Any developer wanting to add behaviour on clicks should modify * only this code. */ @@ -644,6 +732,7 @@ Blockly.Gesture.prototype.doWorkspaceClick_ = function() { /* End functions defining what actions to take to execute clicks on each type * of target. */ +// TODO (fenichel): Move bubbles to the front. /** * Move the dragged/clicked block to the front of the workspace so that it is * not occluded by other blocks. @@ -672,6 +761,17 @@ Blockly.Gesture.prototype.setStartField = function(field) { } }; +/** + * Record the bubble that a gesture started on + * @param {Blockly.Bubble} bubble The bubble the gesture started on. + * @package + */ +Blockly.Gesture.prototype.setStartBubble = function(bubble) { + if (!this.startBubble_) { + this.startBubble_ = bubble; + } +}; + /** * Record the block that a gesture started on, and set the target block * appropriately. @@ -778,7 +878,8 @@ Blockly.Gesture.prototype.isWorkspaceClick_ = function() { * @package */ Blockly.Gesture.prototype.isDragging = function() { - return this.isDraggingWorkspace_ || this.isDraggingBlock_; + return this.isDraggingWorkspace_ || this.isDraggingBlock_ || + this.isDraggingBubble_; }; /** From 7acd4133aab0d96b1845a74ef6e44beab014138e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 6 Feb 2018 18:07:21 -0800 Subject: [PATCH 2/4] Remove unused code in the bubble dragger. --- core/bubble.js | 103 ++++++++++++----------------- core/bubble_dragger.js | 146 ++++------------------------------------- core/gesture.js | 26 +++++++- 3 files changed, 80 insertions(+), 195 deletions(-) diff --git a/core/bubble.js b/core/bubble.js index e6a8ada2a..8e8023b03 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -283,43 +283,6 @@ Blockly.Bubble.prototype.bubbleMouseDown_ = function(e) { if (gesture) { gesture.handleBubbleStart(e, this); } - - // this.promote_(); - // Blockly.Bubble.unbindDragEvents_(); - // if (Blockly.utils.isRightButton(e)) { - // // No right-click. - // e.stopPropagation(); - // return; - // } else if (Blockly.utils.isTargetInput(e)) { - // // When focused on an HTML text input widget, don't trap any events. - // return; - // } - // // Left-click (or middle click) - // this.workspace_.startDrag(e, new goog.math.Coordinate( - // this.workspace_.RTL ? -this.relativeLeft_ : this.relativeLeft_, - // this.relativeTop_)); - - // Blockly.Bubble.onMouseUpWrapper_ = Blockly.bindEventWithChecks_(document, - // 'mouseup', this, Blockly.Bubble.bubbleMouseUp_); - // Blockly.Bubble.onMouseMoveWrapper_ = Blockly.bindEventWithChecks_(document, - // 'mousemove', this, this.bubbleMouseMove_); - // Blockly.hideChaff(); - // // This event has been handled. No need to bubble up to the document. - // e.stopPropagation(); -}; - -/** - * Drag this bubble to follow the mouse. - * @param {!Event} e Mouse move event. - * @private - */ -Blockly.Bubble.prototype.bubbleMouseMove_ = function(e) { - this.autoLayout_ = false; - var newXY = this.workspace_.moveDrag(e); - this.relativeLeft_ = this.workspace_.RTL ? -newXY.x : newXY.x; - this.relativeTop_ = newXY.y; - this.positionBubble_(); - this.renderArrow_(); }; /** @@ -453,6 +416,12 @@ Blockly.Bubble.prototype.positionBubble_ = function() { this.moveTo_(left, top); }; +/** + * Move the bubble group to the specified location in workspace coordinates. + * @param {number} x The x position to move to. + * @param {nubmer} y The y position to move to. + * @private + */ Blockly.Bubble.prototype.moveTo_ = function(x, y) { this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')'); }; @@ -604,42 +573,54 @@ Blockly.Bubble.prototype.dispose = function() { this.shape_ = null; }; +/** + * + * @package + */ Blockly.Bubble.prototype.moveToDragSurface = function(dragSurface) { - //var bubbleXY = this.getRelativeToSurfaceXY(); - //var anchorXY = this.getAnchorRelativeToSurfaceXY(); - - // TODO: check RTL. - var x = this.anchorXY_.x + this.relativeLeft_; - var y = this.anchorXY_.y + this.relativeTop_; - this.savedRelativeXY_ = - new goog.math.Coordinate(this.relativeLeft_, this.relativeTop_); - this.savedAnchorXY_ = this.anchorXY_; - this.moveTo_(0, 0); - dragSurface.translateSurface(x, y); - // Execute the move on the top-level SVG component. - dragSurface.setBlocksAndShow(this.bubbleGroup_); + this.autoLayout_ = false; + if (dragSurface) { + // TODO: check RTL. + var x = this.anchorXY_.x + this.relativeLeft_; + var y = this.anchorXY_.y + this.relativeTop_; + this.moveTo_(0, 0); + dragSurface.translateSurface(x, y); + // Execute the move on the top-level SVG component. + dragSurface.setBlocksAndShow(this.bubbleGroup_); + } }; +/** + * + * @package + */ Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) { - console.log(newLoc); - dragSurface.translateSurface(newLoc.x, newLoc.y); - this.relativeLeft_ = this.savedRelativeXY_.x + newLoc.x; - this.relativeTop_ = this.savedRelativeXY_.y + newLoc.y; + if (dragSurface) { + dragSurface.translateSurface(newLoc.x, newLoc.y); + } else { + this.moveTo_(newLoc.x, newLoc.y); + } + this.relativeLeft_ = - this.anchorXY_.x + newLoc.x; + this.relativeTop_ = - this.anchorXY_.y + newLoc.y; this.renderArrow_(); }; +/** + * + * @package + */ Blockly.Bubble.prototype.moveOffDragSurface = function(dragSurface, newXY) { - - //this.savedAnchorXY_ = this.anchorXY_; - this.anchorXY_ = this.savedAnchorXY_; this.moveTo_(newXY.x, newXY.y); - dragSurface.clearAndHide(this.workspace_.getBubbleCanvas()); + if (dragSurface) { + dragSurface.clearAndHide(this.workspace_.getBubbleCanvas()); + } }; - +/** + * + * @package + */ Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() { - // This may not be quite right. It's probably the top-left of the bubble - // group. return new goog.math.Coordinate(this.anchorXY_.x + this.relativeLeft_, this.anchorXY_.y + this.relativeTop_); }; diff --git a/core/bubble_dragger.js b/core/bubble_dragger.js index e66b500bf..c801233f3 100644 --- a/core/bubble_dragger.js +++ b/core/bubble_dragger.js @@ -26,14 +26,12 @@ goog.provide('Blockly.BubbleDragger'); -goog.require('Blockly.DraggedConnectionManager'); - goog.require('goog.math.Coordinate'); goog.require('goog.asserts'); /** - * Class for a block dragger. It moves blocks around the workspace when they + * Class for a bubble dragger. It moves bubbles around the workspace when they * are being dragged by a mouse or touch. * @param {!Blockly.Bubble} bubble The bubble to drag. * @param {!Blockly.WorkspaceSvg} workspace The workspace to drag on. @@ -41,8 +39,8 @@ goog.require('goog.asserts'); */ Blockly.BubbleDragger = function(bubble, workspace) { /** - * The top block in the stack that is being dragged. - * @type {!Blockly.BlockSvg} + * The bubble that is being dragged. + * @type {!Blockly.Bubble} * @private */ this.draggingBubble_ = bubble; @@ -55,32 +53,7 @@ Blockly.BubbleDragger = function(bubble, workspace) { this.workspace_ = workspace; /** - * Object that keeps track of connections on dragged blocks. - * @type {!Blockly.DraggedConnectionManager} - * @private - */ - this.draggedConnectionManager_ = null; - // new Blockly.DraggedConnectionManager( - // this.draggingBubble_); - - /** - * Which delete area the mouse pointer is over, if any. - * One of {@link Blockly.DELETE_AREA_TRASH}, - * {@link Blockly.DELETE_AREA_TOOLBOX}, or {@link Blockly.DELETE_AREA_NONE}. - * @type {?number} - * @private - */ - this.deleteArea_ = null; - - /** - * Whether the block would be deleted if dropped immediately. - * @type {boolean} - * @private - */ - this.wouldDeleteBlock_ = false; - - /** - * The location of the top left corner of the dragging block at the beginning + * The location of the top left corner of the dragging bubble at the beginning * of the drag in workspace coordinates. * @type {!goog.math.Coordinate} * @private @@ -88,7 +61,9 @@ Blockly.BubbleDragger = function(bubble, workspace) { this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY(); // TODO: validate, getters, etc. - this.dragSurface_ = workspace.blockDragSurface_; + this.dragSurface_ = + Blockly.utils.is3dSupported() && !!workspace.blockDragSurface_ ? + workspace.blockDragSurface_ : null; }; /** @@ -99,47 +74,19 @@ Blockly.BubbleDragger.prototype.dispose = function() { this.draggingBubble_ = null; this.workspace_ = null; this.startWorkspace_ = null; - this.dragIconData_.length = 0; - - if (this.draggedConnectionManager_) { - this.draggedConnectionManager_.dispose(); - this.draggedConnectionManager_ = null; - } }; /** * Start dragging a block. This includes moving it to the drag surface. - * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has - * moved from the position at mouse down, in pixel units. * @package */ -Blockly.BubbleDragger.prototype.startBubbleDrag = function(currentDragDeltaXY) { - console.log('starting bubble drag'); +Blockly.BubbleDragger.prototype.startBubbleDrag = function() { if (!Blockly.Events.getGroup()) { Blockly.Events.setGroup(true); } this.workspace_.setResizesEnabled(false); - - // if (this.draggingBubble_.getParent()) { - // this.draggingBubble_.unplug(); - // var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); - // var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - - // this.draggingBubble_.translate(newLoc.x, newLoc.y); - // this.draggingBubble_.disconnectUiEffect(); - // } - //this.draggingBubble_.setDragging(true); - // For future consideration: we may be able to put moveToDragSurface inside - // the block dragger, which would also let the block not track the block drag - // surface. this.draggingBubble_.moveToDragSurface(this.dragSurface_); - - if (this.workspace_.toolbox_) { - var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - this.workspace_.toolbox_.addStyle(style); - } }; /** @@ -155,12 +102,8 @@ Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) { var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); this.draggingBubble_.moveDuringDrag(this.dragSurface_, newLoc); - //this.dragIcons_(delta); - - this.deleteArea_ = this.workspace_.isDeleteArea(e); - //this.draggedConnectionManager_.update(delta, this.deleteArea_); - - //this.updateCursorDuringBubbleDrag_(); + // TODO (fenichel): Possibly update the cursor if dragging to the trash can + // is allowed. }; /** @@ -173,31 +116,14 @@ Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) { Blockly.BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) { // Make sure internal state is fresh. this.dragBubble(e, currentDragDeltaXY); - this.dragIconData_ = []; - - Blockly.BlockSvg.disconnectUiStop_(); var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); this.draggingBubble_.moveOffDragSurface(this.dragSurface_, newLoc); - var deleted = true;//this.maybeDeleteBlock_(); - if (!deleted) { - // These are expensive and don't need to be done if we're deleting. - this.draggingBubble_.moveConnections_(delta.x, delta.y); - this.draggingBubble_.setDragging(false); - //this.draggedConnectionManager_.applyConnections(); - this.draggingBubble_.render(); - this.fireMoveEvent_(); - this.draggingBubble_.scheduleSnapAndBump(); - } + this.fireMoveEvent_(); this.workspace_.setResizesEnabled(true); - if (this.workspace_.toolbox_) { - var style = this.draggingBubble_.isDeletable() ? 'blocklyToolboxDelete' : - 'blocklyToolboxGrab'; - this.workspace_.toolbox_.removeStyle(style); - } Blockly.Events.setGroup(false); }; @@ -206,54 +132,8 @@ Blockly.BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) * @private */ Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() { - var event = new Blockly.Events.BlockMove(this.draggingBubble_); - event.oldCoordinate = this.startXY_; - event.recordNew(); - Blockly.Events.fire(event); -}; - -/** - * Shut the trash can and, if necessary, delete the dragging block. - * Should be called at the end of a block drag. - * @return {boolean} whether the block was deleted. - * @private - */ -Blockly.BubbleDragger.prototype.maybeDeleteBubble_ = function() { - var trashcan = this.workspace_.trashcan; - - if (this.wouldDeleteBlock_) { - if (trashcan) { - goog.Timer.callOnce(trashcan.close, 100, trashcan); - } - // Fire a move event, so we know where to go back to for an undo. - this.fireMoveEvent_(); - this.draggingBubble_.dispose(false, true); - } else if (trashcan) { - // Make sure the trash can is closed. - trashcan.close(); - } - return this.wouldDeleteBlock_; -}; - -/** - * Update the cursor (and possibly the trash can lid) to reflect whether the - * dragging block would be deleted if released immediately. - * @private - */ -Blockly.BubbleDragger.prototype.updateCursorDuringBubbleDrag_ = function() { - this.wouldDeleteBlock_ = false; //this.draggedConnectionManager_.wouldDeleteBlock(); - var trashcan = this.workspace_.trashcan; - if (this.wouldDeleteBlock_) { - this.draggingBubble_.setDeleteStyle(true); - if (this.deleteArea_ == Blockly.DELETE_AREA_TRASH && trashcan) { - trashcan.setOpen_(true); - } - } else { - this.draggingBubble_.setDeleteStyle(false); - if (trashcan) { - trashcan.setOpen_(false); - } - } + // TODO: move events for comments. + return; }; /** diff --git a/core/gesture.js b/core/gesture.js index f7b9262ce..6c222ddfb 100644 --- a/core/gesture.js +++ b/core/gesture.js @@ -455,7 +455,7 @@ Blockly.Gesture.prototype.startDraggingBlock_ = function() { Blockly.Gesture.prototype.startDraggingBubble_ = function() { this.bubbleDragger_ = new Blockly.BubbleDragger(this.startBubble_, this.startWorkspace_); - this.bubbleDragger_.startBubbleDrag(this.currentDragDeltaXY_); + this.bubbleDragger_.startBubbleDrag(); this.bubbleDragger_.dragBubble(this.mostRecentEvent_, this.currentDragDeltaXY_); }; @@ -566,6 +566,9 @@ Blockly.Gesture.prototype.handleUp = function(e) { this.blockDragger_.endBlockDrag(e, this.currentDragDeltaXY_); } else if (this.isDraggingWorkspace_) { this.workspaceDragger_.endDrag(this.currentDragDeltaXY_); + } else if (this.isBubbleClick_()) { + // Bubbles are in front of all fields and blocks. + this.doBubbleClick_(); } else if (this.isFieldClick_()) { this.doFieldClick_(); } else if (this.isBlockClick_()) { @@ -687,6 +690,15 @@ Blockly.Gesture.prototype.handleBubbleStart = function(e, bubble) { * of target. Any developer wanting to add behaviour on clicks should modify * only this code. */ +/** + * Execute a bubble click. + * @private + */ +Blockly.Gesture.prototype.doBubbleClick_ = function() { + // TODO: This isn't really enough, is it. + this.startBubble_.promote_(); +}; + /** * Execute a field click. * @private @@ -831,6 +843,18 @@ Blockly.Gesture.prototype.setStartFlyout_ = function(flyout) { /* Begin helper functions defining types of clicks. Any developer wanting * to change the definition of a click should modify only this code. */ +/** + * Whether this gesture is a click on a bubble. This should only be called when + * ending a gesture (mouse up, touch end). + * @return {boolean} whether this gesture was a click on a bubble. + * @private + */ +Blockly.Gesture.prototype.isBubbleClick_ = function() { + // A bubble click starts on a bubble and never escapes the drag radius. + var hasStartBubble = !!this.startBubble_; + return hasStartBubble && !this.hasExceededDragRadius_; +}; + /** * Whether this gesture is a click on a block. This should only be called when * ending a gesture (mouse up, touch end). From 9d2cb829a95700ab705ef0e824e8bb55f79e30f7 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 6 Feb 2018 18:56:26 -0800 Subject: [PATCH 3/4] Move code into the bubble dragger where possible --- core/bubble.js | 80 ++++++++++++++++++++++-------------------- core/bubble_dragger.js | 54 ++++++++++++++++++++-------- core/workspace_svg.js | 9 +++++ 3 files changed, 91 insertions(+), 52 deletions(-) diff --git a/core/bubble.js b/core/bubble.js index 8e8023b03..92dc28894 100644 --- a/core/bubble.js +++ b/core/bubble.js @@ -162,14 +162,15 @@ Blockly.Bubble.bubbleMouseUp_ = function(/*e*/) { Blockly.Bubble.prototype.rendered_ = false; /** - * Absolute coordinate of anchor point. + * Absolute coordinate of anchor point, in workspace coordinates. * @type {goog.math.Coordinate} * @private */ Blockly.Bubble.prototype.anchorXY_ = null; /** - * Relative X coordinate of bubble with respect to the anchor's centre. + * Relative X coordinate of bubble with respect to the anchor's centre, + * in workspace units. * In RTL mode the initial value is negated. * @private */ @@ -273,6 +274,14 @@ Blockly.Bubble.prototype.createDom_ = function(content, hasResize) { return this.bubbleGroup_; }; +/** + * Return the root node of the bubble's SVG group. + * @return {Element} The root SVG node of the bubble's group. + */ +Blockly.Bubble.prototype.getSvgRoot = function() { + return this.bubbleGroup_; +}; + /** * Handle a mouse-down on bubble's border. * @param {!Event} e Mouse down event. @@ -413,16 +422,16 @@ Blockly.Bubble.prototype.positionBubble_ = function() { left += this.relativeLeft_; } var top = this.relativeTop_ + this.anchorXY_.y; - this.moveTo_(left, top); + this.moveTo(left, top); }; /** * Move the bubble group to the specified location in workspace coordinates. * @param {number} x The x position to move to. - * @param {nubmer} y The y position to move to. - * @private + * @param {number} y The y position to move to. + * @package */ -Blockly.Bubble.prototype.moveTo_ = function(x, y) { +Blockly.Bubble.prototype.moveTo = function(x, y) { this.bubbleGroup_.setAttribute('transform', 'translate(' + x + ',' + y + ')'); }; @@ -574,53 +583,48 @@ Blockly.Bubble.prototype.dispose = function() { }; /** - * - * @package - */ -Blockly.Bubble.prototype.moveToDragSurface = function(dragSurface) { - this.autoLayout_ = false; - if (dragSurface) { - // TODO: check RTL. - var x = this.anchorXY_.x + this.relativeLeft_; - var y = this.anchorXY_.y + this.relativeTop_; - this.moveTo_(0, 0); - dragSurface.translateSurface(x, y); - // Execute the move on the top-level SVG component. - dragSurface.setBlocksAndShow(this.bubbleGroup_); - } -}; - -/** - * + * Move this bubble during a drag, taking into account whether or not there is + * a drag surface. + * @param {?Blockly.BlockDragSurfaceSvg} dragSurface The surface that carries + * rendered items during a drag, or null if no drag surface is in use. + * @param {!goog.math.Coordinate} newLoc The location to translate to, in + * workspace coordinates. * @package */ Blockly.Bubble.prototype.moveDuringDrag = function(dragSurface, newLoc) { if (dragSurface) { dragSurface.translateSurface(newLoc.x, newLoc.y); } else { - this.moveTo_(newLoc.x, newLoc.y); + this.moveTo(newLoc.x, newLoc.y); } - this.relativeLeft_ = - this.anchorXY_.x + newLoc.x; - this.relativeTop_ = - this.anchorXY_.y + newLoc.y; + if (this.workspace_.RTL) { + this.relativeLeft_ = this.anchorXY_.x - newLoc.x - this.width_; + } else { + this.relativeLeft_ = newLoc.x - this.anchorXY_.x; + } + this.relativeTop_ = newLoc.y - this.anchorXY_.y; this.renderArrow_(); }; /** - * - * @package + * Return the coordinates of the top-left corner of this bubble's body relative + * to the drawing surface's origin (0,0), in workspace units. + * @return {!goog.math.Coordinate} Object with .x and .y properties. */ -Blockly.Bubble.prototype.moveOffDragSurface = function(dragSurface, newXY) { - this.moveTo_(newXY.x, newXY.y); - if (dragSurface) { - dragSurface.clearAndHide(this.workspace_.getBubbleCanvas()); - } +Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() { + return new goog.math.Coordinate( + this.anchorXY_.x + this.relativeLeft_, + this.anchorXY_.y + this.relativeTop_); }; /** - * + * Set whether auto-layout of this bubble is enabled. The first time a bubble + * is shown it positions itself to not cover any blocks. Once a user has + * dragged it to reposition, it renders where the user put it. + * @param {boolean} enable True if auto-layout should be enabled, false + * otherwise. * @package */ -Blockly.Bubble.prototype.getRelativeToSurfaceXY = function() { - return new goog.math.Coordinate(this.anchorXY_.x + this.relativeLeft_, - this.anchorXY_.y + this.relativeTop_); +Blockly.Bubble.prototype.setAutoLayout = function(enable) { + this.autoLayout_ = enable; }; diff --git a/core/bubble_dragger.js b/core/bubble_dragger.js index c801233f3..e0dc03eda 100644 --- a/core/bubble_dragger.js +++ b/core/bubble_dragger.js @@ -53,17 +53,22 @@ Blockly.BubbleDragger = function(bubble, workspace) { this.workspace_ = workspace; /** - * The location of the top left corner of the dragging bubble at the beginning - * of the drag in workspace coordinates. + * The location of the top left corner of the dragging bubble's body at the + * beginning of the drag, in workspace coordinates. * @type {!goog.math.Coordinate} * @private */ this.startXY_ = this.draggingBubble_.getRelativeToSurfaceXY(); - // TODO: validate, getters, etc. + /** + * The drag surface to move bubbles to during a drag, or null if none should + * be used. Block dragging and bubble dragging use the same surface. + * @type {?Blockly.BlockDragSurfaceSvg} + * @private + */ this.dragSurface_ = - Blockly.utils.is3dSupported() && !!workspace.blockDragSurface_ ? - workspace.blockDragSurface_ : null; + Blockly.utils.is3dSupported() && !!workspace.getBlockDragSurface() ? + workspace.getBlockDragSurface() : null; }; /** @@ -73,11 +78,11 @@ Blockly.BubbleDragger = function(bubble, workspace) { Blockly.BubbleDragger.prototype.dispose = function() { this.draggingBubble_ = null; this.workspace_ = null; - this.startWorkspace_ = null; + this.dragSurface_ = null; }; /** - * Start dragging a block. This includes moving it to the drag surface. + * Start dragging a bubble. This includes moving it to the drag surface. * @package */ Blockly.BubbleDragger.prototype.startBubbleDrag = function() { @@ -86,11 +91,14 @@ Blockly.BubbleDragger.prototype.startBubbleDrag = function() { } this.workspace_.setResizesEnabled(false); - this.draggingBubble_.moveToDragSurface(this.dragSurface_); + this.draggingBubble_.setAutoLayout(false); + if (this.dragSurface_) { + this.moveToDragSurface_(); + } }; /** - * Execute a step of block dragging, based on the given event. Update the + * Execute a step of bubble dragging, based on the given event. Update the * display accordingly. * @param {!Event} e The most recent move event. * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has @@ -107,19 +115,26 @@ Blockly.BubbleDragger.prototype.dragBubble = function(e, currentDragDeltaXY) { }; /** - * Finish a block drag and put the block back on the workspace. + * Finish a bubble drag and put the bubble back on the workspace. * @param {!Event} e The mouseup/touchend event. * @param {!goog.math.Coordinate} currentDragDeltaXY How far the pointer has * moved from the position at the start of the drag, in pixel units. * @package */ -Blockly.BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) { +Blockly.BubbleDragger.prototype.endBubbleDrag = function( + e, currentDragDeltaXY) { // Make sure internal state is fresh. this.dragBubble(e, currentDragDeltaXY); var delta = this.pixelsToWorkspaceUnits_(currentDragDeltaXY); var newLoc = goog.math.Coordinate.sum(this.startXY_, delta); - this.draggingBubble_.moveOffDragSurface(this.dragSurface_, newLoc); + + // Move the bubble to its final location. + this.draggingBubble_.moveTo(newLoc.x, newLoc.y); + // Put everything back onto the bubble canvas. + if (this.dragSurface_) { + this.dragSurface_.clearAndHide(this.workspace_.getBubbleCanvas()); + } this.fireMoveEvent_(); this.workspace_.setResizesEnabled(true); @@ -128,11 +143,11 @@ Blockly.BubbleDragger.prototype.endBubbleDrag = function(e, currentDragDeltaXY) }; /** - * Fire a move event at the end of a block drag. + * Fire a move event at the end of a bubble drag. * @private */ Blockly.BubbleDragger.prototype.fireMoveEvent_ = function() { - // TODO: move events for comments. + // TODO (fenichel): move events for comments. return; }; @@ -160,3 +175,14 @@ Blockly.BubbleDragger.prototype.pixelsToWorkspaceUnits_ = function(pixelCoord) { } return result; }; +/** + * Move the bubble onto the drag surface at the beginning of a drag. Move the + * drag surface to preserve the apparent location of the bubble. + * @private + */ +Blockly.BubbleDragger.prototype.moveToDragSurface_ = function() { + this.draggingBubble_.moveTo(0, 0); + this.dragSurface_.translateSurface(this.startXY_.x, this.startXY_.y); + // Execute the move on the top-level SVG component. + this.dragSurface_.setBlocksAndShow(this.draggingBubble_.getSvgRoot()); +}; diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 2f78a1663..53b600ae8 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -754,6 +754,15 @@ Blockly.WorkspaceSvg.prototype.setupDragSurface = function() { this.workspaceDragSurface_.translateSurface(coord.x, coord.y); }; +/** + * @return {?Blockly.BlockDragSurfaceSvg} This workspace's block drag surface, + * if one is in use. + * @package + */ +Blockly.WorkspaceSvg.prototype.getBlockDragSurface = function() { + return this.blockDragSurface_; +}; + /** * Returns the horizontal offset of the workspace. * Intended for LTR/RTL compatibility in XML. From 4ddba5aadbffa62bee4bebbad155b619440099ea Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 6 Feb 2018 19:00:32 -0800 Subject: [PATCH 4/4] rebuild --- blockly_compressed.js | 57 ++++++++++++++--------- blockly_uncompressed.js | 101 +++++++++++++++++++++++----------------- 2 files changed, 91 insertions(+), 67 deletions(-) diff --git a/blockly_compressed.js b/blockly_compressed.js index 09f7168ab..4fcb20369 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -924,25 +924,25 @@ Blockly.Workspace.prototype.getWidth=function(){return 0};Blockly.Workspace.prot Blockly.Workspace.prototype.undo=function(a){var b=a?this.redoStack_:this.undoStack_,c=a?this.undoStack_:this.redoStack_,d=b.pop();if(d){for(var e=[d];b.length&&d.group&&d.group==b[b.length-1].group;)e.push(b.pop());for(b=0;d=e[b];b++)c.push(d);e=Blockly.Events.filter(e,a);Blockly.Events.recordUndo=!1;try{for(b=0;d=e[b];b++)d.run(a)}finally{Blockly.Events.recordUndo=!0}}};Blockly.Workspace.prototype.clearUndo=function(){this.undoStack_.length=0;this.redoStack_.length=0;Blockly.Events.clearPendingUndo()}; Blockly.Workspace.prototype.addChangeListener=function(a){this.listeners_.push(a);return a};Blockly.Workspace.prototype.removeChangeListener=function(a){goog.array.remove(this.listeners_,a)};Blockly.Workspace.prototype.fireChangeListener=function(a){a.recordUndo&&(this.undoStack_.push(a),this.redoStack_.length=0,this.undoStack_.length>this.MAX_UNDO&&this.undoStack_.unshift());for(var b=0,c;c=this.listeners_[b];b++)c(a)}; Blockly.Workspace.prototype.getBlockById=function(a){return this.blockDB_[a]||null};Blockly.Workspace.prototype.allInputsFilled=function(a){for(var b=this.getTopBlocks(!1),c=0,d;d=b[c];c++)if(!d.allInputsFilled(a))return!1;return!0};Blockly.Workspace.prototype.getPotentialVariableMap=function(){return this.potentialVariableMap_};Blockly.Workspace.prototype.createPotentialVariableMap=function(){this.potentialVariableMap_=new Blockly.VariableMap(this)};Blockly.Workspace.prototype.getVariableMap=function(){return this.variableMap_}; -Blockly.Workspace.WorkspaceDB_=Object.create(null);Blockly.Workspace.getById=function(a){return Blockly.Workspace.WorkspaceDB_[a]||null};Blockly.Workspace.prototype.clear=Blockly.Workspace.prototype.clear;Blockly.Workspace.prototype.clearUndo=Blockly.Workspace.prototype.clearUndo;Blockly.Workspace.prototype.addChangeListener=Blockly.Workspace.prototype.addChangeListener;Blockly.Workspace.prototype.removeChangeListener=Blockly.Workspace.prototype.removeChangeListener;Blockly.Bubble=function(a,b,c,d,e,f){this.workspace_=a;this.content_=b;this.shape_=c;c=Blockly.Bubble.ARROW_ANGLE;this.workspace_.RTL&&(c=-c);this.arrow_radians_=goog.math.toRadians(c);a.getBubbleCanvas().appendChild(this.createDom_(b,!(!e||!f)));this.setAnchorLocation(d);e&&f||(b=this.content_.getBBox(),e=b.width+2*Blockly.Bubble.BORDER_WIDTH,f=b.height+2*Blockly.Bubble.BORDER_WIDTH);this.setBubbleSize(e,f);this.positionBubble_();this.renderArrow_();this.rendered_=!0;a.options.readOnly||(Blockly.bindEventWithChecks_(this.bubbleBack_, -"mousedown",this,this.bubbleMouseDown_),this.resizeGroup_&&Blockly.bindEventWithChecks_(this.resizeGroup_,"mousedown",this,this.resizeMouseDown_))};Blockly.Bubble.BORDER_WIDTH=6;Blockly.Bubble.ARROW_THICKNESS=5;Blockly.Bubble.ARROW_ANGLE=20;Blockly.Bubble.ARROW_BEND=4;Blockly.Bubble.ANCHOR_RADIUS=8;Blockly.Bubble.onMouseUpWrapper_=null;Blockly.Bubble.onMouseMoveWrapper_=null;Blockly.Bubble.prototype.resizeCallback_=null; +Blockly.Workspace.WorkspaceDB_=Object.create(null);Blockly.Workspace.getById=function(a){return Blockly.Workspace.WorkspaceDB_[a]||null};Blockly.Workspace.prototype.clear=Blockly.Workspace.prototype.clear;Blockly.Workspace.prototype.clearUndo=Blockly.Workspace.prototype.clearUndo;Blockly.Workspace.prototype.addChangeListener=Blockly.Workspace.prototype.addChangeListener;Blockly.Workspace.prototype.removeChangeListener=Blockly.Workspace.prototype.removeChangeListener;Blockly.Bubble=function(a,b,c,d,e,f){this.workspace_=a;this.content_=b;this.shape_=c;this.isDeletable_=!1;c=Blockly.Bubble.ARROW_ANGLE;this.workspace_.RTL&&(c=-c);this.arrow_radians_=goog.math.toRadians(c);a.getBubbleCanvas().appendChild(this.createDom_(b,!(!e||!f)));this.setAnchorLocation(d);e&&f||(b=this.content_.getBBox(),e=b.width+2*Blockly.Bubble.BORDER_WIDTH,f=b.height+2*Blockly.Bubble.BORDER_WIDTH);this.setBubbleSize(e,f);this.positionBubble_();this.renderArrow_();this.rendered_=!0;a.options.readOnly|| +(Blockly.bindEventWithChecks_(this.bubbleBack_,"mousedown",this,this.bubbleMouseDown_),this.resizeGroup_&&Blockly.bindEventWithChecks_(this.resizeGroup_,"mousedown",this,this.resizeMouseDown_))};Blockly.Bubble.BORDER_WIDTH=6;Blockly.Bubble.ARROW_THICKNESS=5;Blockly.Bubble.ARROW_ANGLE=20;Blockly.Bubble.ARROW_BEND=4;Blockly.Bubble.ANCHOR_RADIUS=8;Blockly.Bubble.onMouseUpWrapper_=null;Blockly.Bubble.onMouseMoveWrapper_=null;Blockly.Bubble.prototype.resizeCallback_=null; Blockly.Bubble.unbindDragEvents_=function(){Blockly.Bubble.onMouseUpWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseUpWrapper_),Blockly.Bubble.onMouseUpWrapper_=null);Blockly.Bubble.onMouseMoveWrapper_&&(Blockly.unbindEvent_(Blockly.Bubble.onMouseMoveWrapper_),Blockly.Bubble.onMouseMoveWrapper_=null)};Blockly.Bubble.bubbleMouseUp_=function(){Blockly.Touch.clearTouchIdentifier();Blockly.Bubble.unbindDragEvents_()};Blockly.Bubble.prototype.rendered_=!1;Blockly.Bubble.prototype.anchorXY_=null; Blockly.Bubble.prototype.relativeLeft_=0;Blockly.Bubble.prototype.relativeTop_=0;Blockly.Bubble.prototype.width_=0;Blockly.Bubble.prototype.height_=0;Blockly.Bubble.prototype.autoLayout_=!0; Blockly.Bubble.prototype.createDom_=function(a,b){this.bubbleGroup_=Blockly.utils.createSvgElement("g",{},null);var c={filter:"url(#"+this.workspace_.options.embossFilterId+")"};-1!=goog.userAgent.getUserAgentString().indexOf("JavaFX")&&(c={});c=Blockly.utils.createSvgElement("g",c,this.bubbleGroup_);this.bubbleArrow_=Blockly.utils.createSvgElement("path",{},c);this.bubbleBack_=Blockly.utils.createSvgElement("rect",{"class":"blocklyDraggable",x:0,y:0,rx:Blockly.Bubble.BORDER_WIDTH,ry:Blockly.Bubble.BORDER_WIDTH}, c);b?(this.resizeGroup_=Blockly.utils.createSvgElement("g",{"class":this.workspace_.RTL?"blocklyResizeSW":"blocklyResizeSE"},this.bubbleGroup_),c=2*Blockly.Bubble.BORDER_WIDTH,Blockly.utils.createSvgElement("polygon",{points:"0,x x,x x,0".replace(/x/g,c.toString())},this.resizeGroup_),Blockly.utils.createSvgElement("line",{"class":"blocklyResizeLine",x1:c/3,y1:c-1,x2:c-1,y2:c/3},this.resizeGroup_),Blockly.utils.createSvgElement("line",{"class":"blocklyResizeLine",x1:2*c/3,y1:c-1,x2:c-1,y2:2*c/3}, -this.resizeGroup_)):this.resizeGroup_=null;this.bubbleGroup_.appendChild(a);return this.bubbleGroup_}; -Blockly.Bubble.prototype.bubbleMouseDown_=function(a){this.promote_();Blockly.Bubble.unbindDragEvents_();Blockly.utils.isRightButton(a)?a.stopPropagation():Blockly.utils.isTargetInput(a)||(this.workspace_.startDrag(a,new goog.math.Coordinate(this.workspace_.RTL?-this.relativeLeft_:this.relativeLeft_,this.relativeTop_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,Blockly.Bubble.bubbleMouseUp_),Blockly.Bubble.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document, -"mousemove",this,this.bubbleMouseMove_),Blockly.hideChaff(),a.stopPropagation())};Blockly.Bubble.prototype.bubbleMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.relativeLeft_=this.workspace_.RTL?-a.x:a.x;this.relativeTop_=a.y;this.positionBubble_();this.renderArrow_()}; +this.resizeGroup_)):this.resizeGroup_=null;this.bubbleGroup_.appendChild(a);return this.bubbleGroup_};Blockly.Bubble.prototype.bubbleMouseDown_=function(a){var b=this.workspace_.getGesture(a);b&&b.handleBubbleStart(a,this)};Blockly.Bubble.prototype.bubbleMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.relativeLeft_=this.workspace_.RTL?-a.x:a.x;this.relativeTop_=a.y;this.positionBubble_();this.renderArrow_()}; Blockly.Bubble.prototype.resizeMouseDown_=function(a){this.promote_();Blockly.Bubble.unbindDragEvents_();Blockly.utils.isRightButton(a)||(this.workspace_.startDrag(a,new goog.math.Coordinate(this.workspace_.RTL?-this.width_:this.width_,this.height_)),Blockly.Bubble.onMouseUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",this,Blockly.Bubble.bubbleMouseUp_),Blockly.Bubble.onMouseMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",this,this.resizeMouseMove_),Blockly.hideChaff()); a.stopPropagation()};Blockly.Bubble.prototype.resizeMouseMove_=function(a){this.autoLayout_=!1;a=this.workspace_.moveDrag(a);this.setBubbleSize(this.workspace_.RTL?-a.x:a.x,a.y);this.workspace_.RTL&&this.positionBubble_()};Blockly.Bubble.prototype.registerResizeEvent=function(a){this.resizeCallback_=a};Blockly.Bubble.prototype.promote_=function(){this.bubbleGroup_.parentNode.appendChild(this.bubbleGroup_)};Blockly.Bubble.prototype.setAnchorLocation=function(a){this.anchorXY_=a;this.rendered_&&this.positionBubble_()}; Blockly.Bubble.prototype.layoutBubble_=function(){var a=-this.width_/4,b=-this.height_-Blockly.BlockSvg.MIN_BLOCK_Y,c=this.workspace_.getMetrics();c.viewWidth/=this.workspace_.scale;c.viewLeft/=this.workspace_.scale;var d=this.anchorXY_.x;this.workspace_.RTL?d-c.viewLeft-a-this.width_c.viewWidth&&(a=d-c.viewLeft-c.viewWidth):d+ae&&(g=2*Math.PI-g);var h=g+Math.PI/2;h>2*Math.PI&&(h-=2*Math.PI);var k=Math.sin(h),n=Math.cos(h),p=this.getBubbleSize();h=(p.width+p.height)/Blockly.Bubble.ARROW_THICKNESS;h=Math.min(h,p.width,p.height)/4;p=1-Blockly.Bubble.ANCHOR_RADIUS/f;d=b+ p*d;e=c+p*e;p=b+h*n;var l=c+h*k;b-=h*n;c-=h*k;k=g+this.arrow_radians_;k>2*Math.PI&&(k-=2*Math.PI);g=Math.sin(k)*f/Blockly.Bubble.ARROW_BEND;f=Math.cos(k)*f/Blockly.Bubble.ARROW_BEND;a.push("M"+p+","+l);a.push("C"+(p+f)+","+(l+g)+" "+d+","+e+" "+d+","+e);a.push("C"+d+","+e+" "+(b+f)+","+(c+g)+" "+b+","+c)}a.push("z");this.bubbleArrow_.setAttribute("d",a.join(" "))};Blockly.Bubble.prototype.setColour=function(a){this.bubbleBack_.setAttribute("fill",a);this.bubbleArrow_.setAttribute("fill",a)}; -Blockly.Bubble.prototype.dispose=function(){Blockly.Bubble.unbindDragEvents_();goog.dom.removeNode(this.bubbleGroup_);this.shape_=this.content_=this.workspace_=this.resizeGroup_=this.bubbleBack_=this.bubbleArrow_=this.bubbleGroup_=null};Blockly.Icon=function(a){this.block_=a};Blockly.Icon.prototype.collapseHidden=!0;Blockly.Icon.prototype.SIZE=17;Blockly.Icon.prototype.bubble_=null;Blockly.Icon.prototype.iconXY_=null; +Blockly.Bubble.prototype.dispose=function(){Blockly.Bubble.unbindDragEvents_();goog.dom.removeNode(this.bubbleGroup_);this.shape_=this.content_=this.workspace_=this.resizeGroup_=this.bubbleBack_=this.bubbleArrow_=this.bubbleGroup_=null};Blockly.Bubble.prototype.moveToDragSurface=function(a){var b=this.anchorXY_.x+this.relativeLeft_,c=this.anchorXY_.y+this.relativeTop_;this.savedAnchorXY_=this.anchorXY_;this.moveTo_(0,0);a.translateSurface(b,c);a.setBlocksAndShow(this.bubbleGroup_)}; +Blockly.Bubble.prototype.moveDuringDrag=function(a,b){a.translateSurface(b.x,b.y);this.relativeLeft_=-this.savedAnchorXY_.x+b.x;this.relativeTop_=-this.savedAnchorXY_.y+b.y;this.renderArrow_()};Blockly.Bubble.prototype.moveOffDragSurface=function(a,b){this.anchorXY_=this.savedAnchorXY_;this.moveTo_(b.x,b.y);a.clearAndHide(this.workspace_.getBubbleCanvas())}; +Blockly.Bubble.prototype.getRelativeToSurfaceXY=function(){return new goog.math.Coordinate(this.anchorXY_.x+this.relativeLeft_,this.anchorXY_.y+this.relativeTop_)};Blockly.Bubble.prototype.isDeletable=function(){return this.isDeletable_};Blockly.Bubble.prototype.setDeletable=function(a){this.isDeletable_=a};Blockly.Icon=function(a){this.block_=a};Blockly.Icon.prototype.collapseHidden=!0;Blockly.Icon.prototype.SIZE=17;Blockly.Icon.prototype.bubble_=null;Blockly.Icon.prototype.iconXY_=null; Blockly.Icon.prototype.createIcon=function(){this.iconGroup_||(this.iconGroup_=Blockly.utils.createSvgElement("g",{"class":"blocklyIconGroup"},null),this.block_.isInFlyout&&Blockly.utils.addClass(this.iconGroup_,"blocklyIconGroupReadonly"),this.drawIcon_(this.iconGroup_),this.block_.getSvgRoot().appendChild(this.iconGroup_),Blockly.bindEventWithChecks_(this.iconGroup_,"mouseup",this,this.iconClick_),this.updateEditable())}; Blockly.Icon.prototype.dispose=function(){goog.dom.removeNode(this.iconGroup_);this.iconGroup_=null;this.setVisible(!1);this.block_=null};Blockly.Icon.prototype.updateEditable=function(){};Blockly.Icon.prototype.isVisible=function(){return!!this.bubble_};Blockly.Icon.prototype.iconClick_=function(a){this.block_.workspace.isDragging()||this.block_.isInFlyout||Blockly.utils.isRightButton(a)||this.setVisible(!this.isVisible())};Blockly.Icon.prototype.updateColour=function(){this.isVisible()&&this.bubble_.setColour(this.block_.getColour())}; Blockly.Icon.prototype.renderIcon=function(a){if(this.collapseHidden&&this.block_.isCollapsed())return this.iconGroup_.setAttribute("display","none"),a;this.iconGroup_.setAttribute("display","block");var b=this.SIZE;this.block_.RTL&&(a-=b);this.iconGroup_.setAttribute("transform","translate("+a+",5)");this.computeIconLocation();return a=this.block_.RTL?a-Blockly.BlockSvg.SEP_SPACE_X:a+(b+Blockly.BlockSvg.SEP_SPACE_X)}; @@ -952,7 +952,7 @@ Blockly.Comment.prototype.drawIcon_=function(a){Blockly.utils.createSvgElement(" Blockly.Comment.prototype.createEditor_=function(){this.foreignObject_=Blockly.utils.createSvgElement("foreignObject",{x:Blockly.Bubble.BORDER_WIDTH,y:Blockly.Bubble.BORDER_WIDTH},null);var a=document.createElementNS(Blockly.HTML_NS,"body");a.setAttribute("xmlns",Blockly.HTML_NS);a.className="blocklyMinimalBody";var b=document.createElementNS(Blockly.HTML_NS,"textarea");b.className="blocklyCommentTextarea";b.setAttribute("dir",this.block_.RTL?"RTL":"LTR");a.appendChild(b);this.textarea_=b;this.foreignObject_.appendChild(a); Blockly.bindEventWithChecks_(b,"mouseup",this,this.textareaFocus_);Blockly.bindEventWithChecks_(b,"wheel",this,function(a){a.stopPropagation()});Blockly.bindEventWithChecks_(b,"change",this,function(a){this.text_!=b.value&&(Blockly.Events.fire(new Blockly.Events.BlockChange(this.block_,"comment",null,this.text_,b.value)),this.text_=b.value)});setTimeout(function(){b.focus()},0);return this.foreignObject_}; Blockly.Comment.prototype.updateEditable=function(){this.isVisible()&&(this.setVisible(!1),this.setVisible(!0));Blockly.Icon.prototype.updateEditable.call(this)};Blockly.Comment.prototype.resizeBubble_=function(){if(this.isVisible()){var a=this.bubble_.getBubbleSize(),b=2*Blockly.Bubble.BORDER_WIDTH;this.foreignObject_.setAttribute("width",a.width-b);this.foreignObject_.setAttribute("height",a.height-b);this.textarea_.style.width=a.width-b-4+"px";this.textarea_.style.height=a.height-b-4+"px"}}; -Blockly.Comment.prototype.setVisible=function(a){if(a!=this.isVisible())if(Blockly.Events.fire(new Blockly.Events.Ui(this.block_,"commentOpen",!a,a)),!this.block_.isEditable()&&!this.textarea_||goog.userAgent.IE)Blockly.Warning.prototype.setVisible.call(this,a);else{var b=this.getText(),c=this.getBubbleSize();a?(this.bubble_=new Blockly.Bubble(this.block_.workspace,this.createEditor_(),this.block_.svgPath_,this.iconXY_,this.width_,this.height_),this.bubble_.registerResizeEvent(this.resizeBubble_.bind(this)), +Blockly.Comment.prototype.setVisible=function(a){if(a!=this.isVisible())if(Blockly.Events.fire(new Blockly.Events.Ui(this.block_,"commentOpen",!a,a)),!this.block_.isEditable()&&!this.textarea_||goog.userAgent.IE)Blockly.Warning.prototype.setVisible.call(this,a);else{var b=this.getText(),c=this.getBubbleSize();a?(this.bubble_=new Blockly.Bubble(this.block_.workspace,this.createEditor_(),this.block_.svgPath_,this.iconXY_,this.width_,this.height_),this.bubble_.setDeletable(!0),this.bubble_.registerResizeEvent(this.resizeBubble_.bind(this)), this.updateColour()):(this.bubble_.dispose(),this.foreignObject_=this.textarea_=this.bubble_=null);this.setText(b);this.setBubbleSize(c.width,c.height)}};Blockly.Comment.prototype.textareaFocus_=function(a){this.bubble_.promote_();this.textarea_.focus()};Blockly.Comment.prototype.getBubbleSize=function(){return this.isVisible()?this.bubble_.getBubbleSize():{width:this.width_,height:this.height_}}; Blockly.Comment.prototype.setBubbleSize=function(a,b){this.textarea_?this.bubble_.setBubbleSize(a,b):(this.width_=a,this.height_=b)};Blockly.Comment.prototype.getText=function(){return this.textarea_?this.textarea_.value:this.text_};Blockly.Comment.prototype.setText=function(a){this.text_!=a&&(Blockly.Events.fire(new Blockly.Events.BlockChange(this.block_,"comment",null,this.text_,a)),this.text_=a);this.textarea_&&(this.textarea_.value=a)}; Blockly.Comment.prototype.dispose=function(){Blockly.Events.isEnabled()&&this.setText("");this.block_.comment=null;Blockly.Icon.prototype.dispose.call(this)};Blockly.Connection=function(a,b){this.sourceBlock_=a;this.type=b;a.workspace.connectionDBList&&(this.db_=a.workspace.connectionDBList[b],this.dbOpposite_=a.workspace.connectionDBList[Blockly.OPPOSITE_TYPE[b]],this.hidden_=!this.db_)};Blockly.Connection.CAN_CONNECT=0;Blockly.Connection.REASON_SELF_CONNECTION=1;Blockly.Connection.REASON_WRONG_TYPE=2;Blockly.Connection.REASON_TARGET_NULL=3;Blockly.Connection.REASON_CHECKS_FAILED=4;Blockly.Connection.REASON_DIFFERENT_WORKSPACES=5; @@ -1006,7 +1006,15 @@ Blockly.BlockDragger.prototype.endBlockDrag=function(a,b){this.dragBlock(a,b);th this.workspace_.setResizesEnabled(!0);this.workspace_.toolbox_&&(c=this.draggingBlock_.isDeletable()?"blocklyToolboxDelete":"blocklyToolboxGrab",this.workspace_.toolbox_.removeStyle(c));Blockly.Events.setGroup(!1)};Blockly.BlockDragger.prototype.fireMoveEvent_=function(){var a=new Blockly.Events.BlockMove(this.draggingBlock_);a.oldCoordinate=this.startXY_;a.recordNew();Blockly.Events.fire(a)}; Blockly.BlockDragger.prototype.maybeDeleteBlock_=function(){var a=this.workspace_.trashcan;this.wouldDeleteBlock_?(a&&goog.Timer.callOnce(a.close,100,a),this.fireMoveEvent_(),this.draggingBlock_.dispose(!1,!0)):a&&a.close();return this.wouldDeleteBlock_}; Blockly.BlockDragger.prototype.updateCursorDuringBlockDrag_=function(){this.wouldDeleteBlock_=this.draggedConnectionManager_.wouldDeleteBlock();var a=this.workspace_.trashcan;this.wouldDeleteBlock_?(this.draggingBlock_.setDeleteStyle(!0),this.deleteArea_==Blockly.DELETE_AREA_TRASH&&a&&a.setOpen_(!0)):(this.draggingBlock_.setDeleteStyle(!1),a&&a.setOpen_(!1))}; -Blockly.BlockDragger.prototype.pixelsToWorkspaceUnits_=function(a){a=new goog.math.Coordinate(a.x/this.workspace_.scale,a.y/this.workspace_.scale);this.workspace_.isMutator&&(a=a.scale(1/this.workspace_.options.parentWorkspace.scale));return a};Blockly.BlockDragger.prototype.dragIcons_=function(a){for(var b=0;bb.height+window.scrollY&&(d-=Blockly.Tooltip.DIV.offsetHeight+2*Blockly.Tooltip.OFFSET_Y);a?c=Math.max(Blockly.Tooltip.MARGINS-window.scrollX,c):c+Blockly.Tooltip.DIV.offsetWidth> -b.width+window.scrollX-2*Blockly.Tooltip.MARGINS&&(c=b.width-Blockly.Tooltip.DIV.offsetWidth-2*Blockly.Tooltip.MARGINS);Blockly.Tooltip.DIV.style.top=d+"px";Blockly.Tooltip.DIV.style.left=c+"px"}};Blockly.Gesture=function(a,b){this.mouseDownXY_=null;this.currentDragDeltaXY_=0;this.startWorkspace_=this.targetBlock_=this.startBlock_=this.startField_=null;this.creatorWorkspace_=b;this.isDraggingBlock_=this.isDraggingWorkspace_=this.hasExceededDragRadius_=!1;this.mostRecentEvent_=a;this.flyout_=this.workspaceDragger_=this.blockDragger_=this.onUpWrapper_=this.onMoveWrapper_=null;this.isEnding_=this.hasStarted_=this.calledUpdateIsDragging_=!1}; -Blockly.Gesture.prototype.dispose=function(){Blockly.Touch.clearTouchIdentifier();Blockly.Tooltip.unblock();this.creatorWorkspace_.clearGesture();this.onMoveWrapper_&&Blockly.unbindEvent_(this.onMoveWrapper_);this.onUpWrapper_&&Blockly.unbindEvent_(this.onUpWrapper_);this.flyout_=this.startWorkspace_=this.targetBlock_=this.startBlock_=this.startField_=null;this.blockDragger_&&(this.blockDragger_.dispose(),this.blockDragger_=null);this.workspaceDragger_&&(this.workspaceDragger_.dispose(),this.workspaceDragger_= -null)};Blockly.Gesture.prototype.updateFromEvent_=function(a){var b=new goog.math.Coordinate(a.clientX,a.clientY);this.updateDragDelta_(b)&&(this.updateIsDragging_(),Blockly.longStop_());this.mostRecentEvent_=a}; +b.width+window.scrollX-2*Blockly.Tooltip.MARGINS&&(c=b.width-Blockly.Tooltip.DIV.offsetWidth-2*Blockly.Tooltip.MARGINS);Blockly.Tooltip.DIV.style.top=d+"px";Blockly.Tooltip.DIV.style.left=c+"px"}};Blockly.Gesture=function(a,b){this.mouseDownXY_=null;this.currentDragDeltaXY_=0;this.startWorkspace_=this.targetBlock_=this.startBlock_=this.startField_=this.startBubble_=null;this.creatorWorkspace_=b;this.isDraggingBubble_=this.isDraggingBlock_=this.isDraggingWorkspace_=this.hasExceededDragRadius_=!1;this.mostRecentEvent_=a;this.flyout_=this.workspaceDragger_=this.blockDragger_=this.bubbleDragger_=this.onUpWrapper_=this.onMoveWrapper_=null;this.isEnding_=this.hasStarted_=this.calledUpdateIsDragging_= +!1}; +Blockly.Gesture.prototype.dispose=function(){Blockly.Touch.clearTouchIdentifier();Blockly.Tooltip.unblock();this.creatorWorkspace_.clearGesture();this.onMoveWrapper_&&Blockly.unbindEvent_(this.onMoveWrapper_);this.onUpWrapper_&&Blockly.unbindEvent_(this.onUpWrapper_);this.flyout_=this.startWorkspace_=this.targetBlock_=this.startBlock_=this.startField_=null;this.blockDragger_&&(this.blockDragger_.dispose(),this.blockDragger_=null);this.workspaceDragger_&&(this.workspaceDragger_.dispose(),this.workspaceDragger_=null); +this.bubbleDragger_&&(this.bubbleDragger_.dispose(),this.bubbleDragger_=null)};Blockly.Gesture.prototype.updateFromEvent_=function(a){var b=new goog.math.Coordinate(a.clientX,a.clientY);this.updateDragDelta_(b)&&(this.updateIsDragging_(),Blockly.longStop_());this.mostRecentEvent_=a}; Blockly.Gesture.prototype.updateDragDelta_=function(a){this.currentDragDeltaXY_=goog.math.Coordinate.difference(a,this.mouseDownXY_);return this.hasExceededDragRadius_?!1:this.hasExceededDragRadius_=goog.math.Coordinate.magnitude(this.currentDragDeltaXY_)>(this.flyout_?Blockly.FLYOUT_DRAG_RADIUS:Blockly.DRAG_RADIUS)}; Blockly.Gesture.prototype.updateIsDraggingFromFlyout_=function(){return this.targetBlock_.disabled?!1:!this.flyout_.isScrollable()||this.flyout_.isDragTowardWorkspace(this.currentDragDeltaXY_)?(this.startWorkspace_=this.flyout_.targetWorkspace_,this.startWorkspace_.updateScreenCalculationsIfScrolled(),Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.startBlock_=null,this.targetBlock_=this.flyout_.createBlock(this.targetBlock_),this.targetBlock_.select(),!0):!1}; -Blockly.Gesture.prototype.updateIsDraggingBlock_=function(){if(!this.targetBlock_)return!1;this.flyout_?this.isDraggingBlock_=this.updateIsDraggingFromFlyout_():this.targetBlock_.isMovable()&&(this.isDraggingBlock_=!0);return this.isDraggingBlock_?(this.startDraggingBlock_(),!0):!1}; +Blockly.Gesture.prototype.updateIsDraggingBubble_=function(){if(!this.startBubble_)return!1;this.isDraggingBubble_=!0;this.startDraggingBubble_();return!0};Blockly.Gesture.prototype.updateIsDraggingBlock_=function(){if(!this.targetBlock_)return!1;this.flyout_?this.isDraggingBlock_=this.updateIsDraggingFromFlyout_():this.targetBlock_.isMovable()&&(this.isDraggingBlock_=!0);return this.isDraggingBlock_?(this.startDraggingBlock_(),!0):!1}; Blockly.Gesture.prototype.updateIsDraggingWorkspace_=function(){if(this.flyout_?this.flyout_.isScrollable():this.startWorkspace_&&this.startWorkspace_.isDraggable())this.workspaceDragger_=this.flyout_?new Blockly.FlyoutDragger(this.flyout_):new Blockly.WorkspaceDragger(this.startWorkspace_),this.isDraggingWorkspace_=!0,this.workspaceDragger_.startDrag()}; -Blockly.Gesture.prototype.updateIsDragging_=function(){goog.asserts.assert(!this.calledUpdateIsDragging_,"updateIsDragging_ should only be called once per gesture.");this.calledUpdateIsDragging_=!0;this.updateIsDraggingBlock_()||this.updateIsDraggingWorkspace_()}; +Blockly.Gesture.prototype.updateIsDragging_=function(){goog.asserts.assert(!this.calledUpdateIsDragging_,"updateIsDragging_ should only be called once per gesture.");this.calledUpdateIsDragging_=!0;this.updateIsDraggingBubble_()||this.updateIsDraggingBlock_()||this.updateIsDraggingWorkspace_()}; Blockly.Gesture.prototype.startDraggingBlock_=function(){this.blockDragger_=new Blockly.BlockDragger(this.targetBlock_,this.startWorkspace_);this.blockDragger_.startBlockDrag(this.currentDragDeltaXY_);this.blockDragger_.dragBlock(this.mostRecentEvent_,this.currentDragDeltaXY_)}; +Blockly.Gesture.prototype.startDraggingBubble_=function(){this.bubbleDragger_=new Blockly.BubbleDragger(this.startBubble_,this.startWorkspace_);this.bubbleDragger_.startBubbleDrag(this.currentDragDeltaXY_);this.bubbleDragger_.dragBubble(this.mostRecentEvent_,this.currentDragDeltaXY_)}; Blockly.Gesture.prototype.doStart=function(a){Blockly.utils.isTargetInput(a)?this.cancel():(this.hasStarted_=!0,Blockly.BlockSvg.disconnectUiStop_(),this.startWorkspace_.updateScreenCalculationsIfScrolled(),this.startWorkspace_.isMutator&&this.startWorkspace_.resize(),this.startWorkspace_.markFocused(),this.mostRecentEvent_=a,Blockly.hideChaff(!!this.flyout_),Blockly.Tooltip.block(),this.targetBlock_&&this.targetBlock_.select(),Blockly.utils.isRightButton(a)?this.handleRightClick(a):((goog.string.caseInsensitiveEquals(a.type, "touchstart")||goog.string.caseInsensitiveEquals(a.type,"pointerdown"))&&Blockly.longStart_(a,this),this.mouseDownXY_=new goog.math.Coordinate(a.clientX,a.clientY),this.bindMouseEvents(a)))};Blockly.Gesture.prototype.bindMouseEvents=function(a){this.onMoveWrapper_=Blockly.bindEventWithChecks_(document,"mousemove",null,this.handleMove.bind(this));this.onUpWrapper_=Blockly.bindEventWithChecks_(document,"mouseup",null,this.handleUp.bind(this));a.preventDefault();a.stopPropagation()}; -Blockly.Gesture.prototype.handleMove=function(a){this.updateFromEvent_(a);this.isDraggingWorkspace_?this.workspaceDragger_.drag(this.currentDragDeltaXY_):this.isDraggingBlock_&&this.blockDragger_.dragBlock(this.mostRecentEvent_,this.currentDragDeltaXY_);a.preventDefault();a.stopPropagation()}; -Blockly.Gesture.prototype.handleUp=function(a){this.updateFromEvent_(a);Blockly.longStop_();this.isEnding_?console.log("Trying to end a gesture recursively."):(this.isEnding_=!0,this.isDraggingBlock_?this.blockDragger_.endBlockDrag(a,this.currentDragDeltaXY_):this.isDraggingWorkspace_?this.workspaceDragger_.endDrag(this.currentDragDeltaXY_):this.isFieldClick_()?this.doFieldClick_():this.isBlockClick_()?this.doBlockClick_():this.isWorkspaceClick_()&&this.doWorkspaceClick_(),a.preventDefault(),a.stopPropagation(), -this.dispose())};Blockly.Gesture.prototype.cancel=function(){this.isEnding_||(Blockly.longStop_(),this.isDraggingBlock_?this.blockDragger_.endBlockDrag(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingWorkspace_&&this.workspaceDragger_.endDrag(this.currentDragDeltaXY_),this.dispose())}; +Blockly.Gesture.prototype.handleMove=function(a){this.updateFromEvent_(a);this.isDraggingWorkspace_?this.workspaceDragger_.drag(this.currentDragDeltaXY_):this.isDraggingBlock_?this.blockDragger_.dragBlock(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingBubble_&&this.bubbleDragger_.dragBubble(this.mostRecentEvent_,this.currentDragDeltaXY_);a.preventDefault();a.stopPropagation()}; +Blockly.Gesture.prototype.handleUp=function(a){this.updateFromEvent_(a);Blockly.longStop_();this.isEnding_?console.log("Trying to end a gesture recursively."):(this.isEnding_=!0,this.isDraggingBubble_?this.bubbleDragger_.endBubbleDrag(a,this.currentDragDeltaXY_):this.isDraggingBlock_?this.blockDragger_.endBlockDrag(a,this.currentDragDeltaXY_):this.isDraggingWorkspace_?this.workspaceDragger_.endDrag(this.currentDragDeltaXY_):this.isFieldClick_()?this.doFieldClick_():this.isBlockClick_()?this.doBlockClick_(): +this.isWorkspaceClick_()&&this.doWorkspaceClick_(),a.preventDefault(),a.stopPropagation(),this.dispose())};Blockly.Gesture.prototype.cancel=function(){this.isEnding_||(Blockly.longStop_(),this.isDraggingBubble_?this.bubbleDragger_.endBubbleDrag(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingBlock_?this.blockDragger_.endBlockDrag(this.mostRecentEvent_,this.currentDragDeltaXY_):this.isDraggingWorkspace_&&this.workspaceDragger_.endDrag(this.currentDragDeltaXY_),this.dispose())}; Blockly.Gesture.prototype.handleRightClick=function(a){this.targetBlock_?(this.bringBlockToFront_(),Blockly.hideChaff(this.flyout_),this.targetBlock_.showContextMenu_(a)):this.startWorkspace_&&!this.flyout_&&(Blockly.hideChaff(),this.startWorkspace_.showContextMenu_(a));a.preventDefault();a.stopPropagation();this.dispose()}; Blockly.Gesture.prototype.handleWsStart=function(a,b){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.handleWsStart, but the gesture had already been started.");this.setStartWorkspace_(b);this.mostRecentEvent_=a;this.doStart(a)};Blockly.Gesture.prototype.handleFlyoutStart=function(a,b){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.handleFlyoutStart, but the gesture had already been started.");this.setStartFlyout_(b);this.handleWsStart(a,b.getWorkspace())}; -Blockly.Gesture.prototype.handleBlockStart=function(a,b){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.handleBlockStart, but the gesture had already been started.");this.setStartBlock(b);this.mostRecentEvent_=a};Blockly.Gesture.prototype.doFieldClick_=function(){this.startField_.showEditor_();this.bringBlockToFront_()}; -Blockly.Gesture.prototype.doBlockClick_=function(){this.flyout_&&this.flyout_.autoClose?this.targetBlock_.disabled||(Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.flyout_.createBlock(this.targetBlock_).scheduleSnapAndBump()):Blockly.Events.fire(new Blockly.Events.Ui(this.startBlock_,"click",void 0,void 0));this.bringBlockToFront_();Blockly.Events.setGroup(!1)};Blockly.Gesture.prototype.doWorkspaceClick_=function(){Blockly.selected&&Blockly.selected.unselect()}; -Blockly.Gesture.prototype.bringBlockToFront_=function(){this.targetBlock_&&!this.flyout_&&this.targetBlock_.bringToFront()};Blockly.Gesture.prototype.setStartField=function(a){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.setStartField, but the gesture had already been started.");this.startField_||(this.startField_=a)};Blockly.Gesture.prototype.setStartBlock=function(a){this.startBlock_||(this.startBlock_=a,a.isInFlyout&&a!=a.getRootBlock()?this.setTargetBlock_(a.getRootBlock()):this.setTargetBlock_(a))}; -Blockly.Gesture.prototype.setTargetBlock_=function(a){a.isShadow()?this.setTargetBlock_(a.getParent()):this.targetBlock_=a};Blockly.Gesture.prototype.setStartWorkspace_=function(a){this.startWorkspace_||(this.startWorkspace_=a)};Blockly.Gesture.prototype.setStartFlyout_=function(a){this.flyout_||(this.flyout_=a)};Blockly.Gesture.prototype.isBlockClick_=function(){return!!this.startBlock_&&!this.hasExceededDragRadius_&&!this.isFieldClick_()}; -Blockly.Gesture.prototype.isFieldClick_=function(){return(this.startField_?this.startField_.isCurrentlyEditable():!1)&&!this.hasExceededDragRadius_&&(!this.flyout_||!this.flyout_.autoClose)};Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startField_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.utils={};Blockly.utils.getMessageArray_=function(){return goog.global.Blockly.Msg};Blockly.utils.removeAttribute=function(a,b){goog.userAgent.IE&&goog.userAgent.isVersion("10.0")?a.setAttribute(b,null):a.removeAttribute(b)};Blockly.utils.addClass=function(a,b){var c=a.getAttribute("class")||"";if(-1!=(" "+c+" ").indexOf(" "+b+" "))return!1;c&&(c+=" ");a.setAttribute("class",c+b);return!0}; +Blockly.Gesture.prototype.handleBlockStart=function(a,b){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.handleBlockStart, but the gesture had already been started.");this.setStartBlock(b);this.mostRecentEvent_=a};Blockly.Gesture.prototype.handleBubbleStart=function(a,b){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.handleBubbleStart, but the gesture had already been started.");this.setStartBubble(b);this.mostRecentEvent_=a}; +Blockly.Gesture.prototype.doFieldClick_=function(){this.startField_.showEditor_();this.bringBlockToFront_()};Blockly.Gesture.prototype.doBlockClick_=function(){this.flyout_&&this.flyout_.autoClose?this.targetBlock_.disabled||(Blockly.Events.getGroup()||Blockly.Events.setGroup(!0),this.flyout_.createBlock(this.targetBlock_).scheduleSnapAndBump()):Blockly.Events.fire(new Blockly.Events.Ui(this.startBlock_,"click",void 0,void 0));this.bringBlockToFront_();Blockly.Events.setGroup(!1)}; +Blockly.Gesture.prototype.doWorkspaceClick_=function(){Blockly.selected&&Blockly.selected.unselect()};Blockly.Gesture.prototype.bringBlockToFront_=function(){this.targetBlock_&&!this.flyout_&&this.targetBlock_.bringToFront()};Blockly.Gesture.prototype.setStartField=function(a){goog.asserts.assert(!this.hasStarted_,"Tried to call gesture.setStartField, but the gesture had already been started.");this.startField_||(this.startField_=a)}; +Blockly.Gesture.prototype.setStartBubble=function(a){this.startBubble_||(this.startBubble_=a)};Blockly.Gesture.prototype.setStartBlock=function(a){this.startBlock_||(this.startBlock_=a,a.isInFlyout&&a!=a.getRootBlock()?this.setTargetBlock_(a.getRootBlock()):this.setTargetBlock_(a))};Blockly.Gesture.prototype.setTargetBlock_=function(a){a.isShadow()?this.setTargetBlock_(a.getParent()):this.targetBlock_=a}; +Blockly.Gesture.prototype.setStartWorkspace_=function(a){this.startWorkspace_||(this.startWorkspace_=a)};Blockly.Gesture.prototype.setStartFlyout_=function(a){this.flyout_||(this.flyout_=a)};Blockly.Gesture.prototype.isBlockClick_=function(){return!!this.startBlock_&&!this.hasExceededDragRadius_&&!this.isFieldClick_()};Blockly.Gesture.prototype.isFieldClick_=function(){return(this.startField_?this.startField_.isCurrentlyEditable():!1)&&!this.hasExceededDragRadius_&&(!this.flyout_||!this.flyout_.autoClose)}; +Blockly.Gesture.prototype.isWorkspaceClick_=function(){return!this.startBlock_&&!this.startField_&&!this.hasExceededDragRadius_};Blockly.Gesture.prototype.isDragging=function(){return this.isDraggingWorkspace_||this.isDraggingBlock_||this.isDraggingBubble_};Blockly.Gesture.prototype.hasStarted=function(){return this.hasStarted_};Blockly.utils={};Blockly.utils.getMessageArray_=function(){return goog.global.Blockly.Msg};Blockly.utils.removeAttribute=function(a,b){goog.userAgent.IE&&goog.userAgent.isVersion("10.0")?a.setAttribute(b,null):a.removeAttribute(b)};Blockly.utils.addClass=function(a,b){var c=a.getAttribute("class")||"";if(-1!=(" "+c+" ").indexOf(" "+b+" "))return!1;c&&(c+=" ");a.setAttribute("class",c+b);return!0}; Blockly.utils.removeClass=function(a,b){var c=a.getAttribute("class");if(-1==(" "+c+" ").indexOf(" "+b+" "))return!1;c=c.split(/\s+/);for(var d=0;d