From bb9b0888264ac16eb738bb6ab99868e070150f5e Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Fri, 27 May 2016 13:53:46 -0700 Subject: [PATCH 01/13] test page that creates random blocks and randomly drags them around the page --- tests/drag_tests.html | 606 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 606 insertions(+) create mode 100644 tests/drag_tests.html diff --git a/tests/drag_tests.html b/tests/drag_tests.html new file mode 100644 index 000000000..06acef0f5 --- /dev/null +++ b/tests/drag_tests.html @@ -0,0 +1,606 @@ + + + + +Blockly Playground + + + + + + + + + + + + + + + + +
+ + + +

Blockly Playground

+ +

Show + - Hide

+ + + +

+ +   + + +

+ +

+ Stress test:   + + +

+

+ Log events:   + +

+ + + From d6f84bff562371541f1303716b4c01d93bd13cc0 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Sun, 29 May 2016 13:43:45 -0700 Subject: [PATCH 02/13] Do not allow importing of top-level shadow blocks. --- core/xml.js | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/core/xml.js b/core/xml.js index e2651e025..07ac7b8e4 100644 --- a/core/xml.js +++ b/core/xml.js @@ -28,6 +28,7 @@ goog.provide('Blockly.Xml'); // TODO(scr): Fix circular dependencies // goog.require('Blockly.Block'); +goog.require('goog.asserts'); goog.require('goog.dom'); @@ -261,7 +262,7 @@ Blockly.Xml.textToDom = function(text) { dom.firstChild.nodeName.toLowerCase() != 'xml' || dom.firstChild !== dom.lastChild) { // Whatever we got back from the parser is not XML. - throw 'Blockly.Xml.textToDom did not obtain a valid XML tree.'; + goog.asserts.fail('Blockly.Xml.textToDom did not obtain a valid XML tree.'); } return dom.firstChild; }; @@ -295,13 +296,15 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { for (var i = 0; i < childCount; i++) { var xmlChild = xml.childNodes[i]; var name = xmlChild.nodeName.toLowerCase(); - if (name == 'block' || name == 'shadow') { + if (name == 'block') { var block = Blockly.Xml.domToBlock(xmlChild, workspace); var blockX = parseInt(xmlChild.getAttribute('x'), 10); var blockY = parseInt(xmlChild.getAttribute('y'), 10); if (!isNaN(blockX) && !isNaN(blockY)) { block.moveBy(workspace.RTL ? width - blockX : blockX, blockY); } + } else if (name == 'shadow') { + goog.asserts.fail('Shadow block cannot be a top-level block.'); } } if (!existingGroup) { @@ -369,9 +372,8 @@ Blockly.Xml.domToBlock = function(xmlBlock, workspace) { Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { var block = null; var prototypeName = xmlBlock.getAttribute('type'); - if (!prototypeName) { - throw 'Block type unspecified: \n' + xmlBlock.outerHTML; - } + goog.asserts.assert(prototypeName, 'Block type unspecified: %s', + xmlBlock.outerHTML); var id = xmlBlock.getAttribute('id'); block = workspace.newBlock(prototypeName, id); @@ -466,7 +468,8 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { } else if (blockChild.previousConnection) { input.connection.connect(blockChild.previousConnection); } else { - throw 'Child block does not have output or previous statement.'; + goog.asserts.fail( + 'Child block does not have output or previous statement.'); } } break; @@ -475,17 +478,15 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { block.nextConnection.setShadowDom(childShadowNode); } if (childBlockNode) { - if (!block.nextConnection) { - throw 'Next statement does not exist.'; - } else if (block.nextConnection.isConnected()) { - // This could happen if there is more than one XML 'next' tag. - throw 'Next statement is already connected.'; - } + goog.asserts.assert(block.nextConnection, + 'Next statement does not exist.'); + // If there is more than one XML 'next' tag. + goog.asserts.assert(!block.nextConnection.isConnected(), + 'Next statement is already connected.'); blockChild = Blockly.Xml.domToBlockHeadless_(childBlockNode, workspace); - if (!blockChild.previousConnection) { - throw 'Next block does not have previous statement.'; - } + goog.asserts.assert(blockChild.previousConnection, + 'Next block does not have previous statement.'); block.nextConnection.connect(blockChild.previousConnection); } break; From 1db8092f15a81b1475e8785c080b17f53780fa34 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Sun, 29 May 2016 14:53:29 -0700 Subject: [PATCH 03/13] Do not allow shadow blocks to have non-shadow children. --- core/connection.js | 18 ++++++++++++++---- core/xml.js | 6 ++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/core/connection.js b/core/connection.js index c52280464..77da88b43 100644 --- a/core/connection.js +++ b/core/connection.js @@ -62,6 +62,7 @@ Blockly.Connection.REASON_WRONG_TYPE = 2; Blockly.Connection.REASON_TARGET_NULL = 3; Blockly.Connection.REASON_CHECKS_FAILED = 4; Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5; +Blockly.Connection.REASON_SHADOW_PARENT = 6; /** * Connection this connection connects to. Null if not connected. @@ -283,18 +284,25 @@ Blockly.Connection.prototype.isConnected = function() { * @private */ Blockly.Connection.prototype.canConnectWithReason_ = function(target) { + if (this.isSuperior()) { + var blockA = this.sourceBlock_; + var blockB = target.getSourceBlock(); + } else { + var blockB = this.sourceBlock_; + var blockA = target.getSourceBlock(); + } if (!target) { return Blockly.Connection.REASON_TARGET_NULL; - } else if (this.sourceBlock_ && - target.getSourceBlock() == this.sourceBlock_) { + } else if (blockA && blockA == blockB) { return Blockly.Connection.REASON_SELF_CONNECTION; } else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) { return Blockly.Connection.REASON_WRONG_TYPE; - } else if (this.sourceBlock_ && target.getSourceBlock() && - this.sourceBlock_.workspace !== target.getSourceBlock().workspace) { + } else if (blockA && blockB && blockA.workspace !== blockB.workspace) { return Blockly.Connection.REASON_DIFFERENT_WORKSPACES; } else if (!this.checkType_(target)) { return Blockly.Connection.REASON_CHECKS_FAILED; + } else if (blockA.isShadow() && !blockB.isShadow()) { + return Blockly.Connection.REASON_SHADOW_PARENT; } return Blockly.Connection.CAN_CONNECT; }; @@ -321,6 +329,8 @@ Blockly.Connection.prototype.checkConnection_ = function(target) { throw 'Target connection is null.'; case Blockly.Connection.REASON_CHECKS_FAILED: throw 'Connection checks failed.'; + case Blockly.Connection.REASON_SHADOW_PARENT: + throw 'Connecting non-shadow to shadow block.'; default: throw 'Unknown connection failure: this should never happen!'; } diff --git a/core/xml.js b/core/xml.js index 07ac7b8e4..48cef7216 100644 --- a/core/xml.js +++ b/core/xml.js @@ -521,6 +521,12 @@ Blockly.Xml.domToBlockHeadless_ = function(xmlBlock, workspace) { block.setCollapsed(collapsed == 'true'); } if (xmlBlock.nodeName.toLowerCase() == 'shadow') { + // Ensure all children are also shadows. + var children = block.getChildren(); + for (var i = 0, child; child = children[i]; i++) { + goog.asserts.assert(child.isShadow(), + 'Shadow block not allowed non-shadow child.'); + } block.setShadow(true); } // Give the block a chance to clean up any initial inputs. From a242c5d6e4cf49bc38d999119f2dae892970a94f Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Sun, 29 May 2016 15:59:46 -0700 Subject: [PATCH 04/13] Stop context menu in context menu. Issue #372. --- core/contextmenu.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/contextmenu.js b/core/contextmenu.js index ebd92df17..af92cf6e2 100644 --- a/core/contextmenu.js +++ b/core/contextmenu.js @@ -77,6 +77,8 @@ Blockly.ContextMenu.show = function(e, options, rtl) { menu.render(div); var menuDom = menu.getElement(); Blockly.addClass_(menuDom, 'blocklyContextMenu'); + // Prevent system context menu when right-clicking a Blockly context menu. + Blockly.bindEvent_(menuDom, 'contextmenu', null, Blockly.noEvent); // Record menuSize after adding menu. var menuSize = goog.style.getSize(menuDom); From 2db6841b66247f7e84aed9d1caa4a2a8348fd1e5 Mon Sep 17 00:00:00 2001 From: Katelyn Mann Date: Tue, 31 May 2016 09:15:42 -0700 Subject: [PATCH 05/13] Add a button to flip between flyouts and toolboxes. --- tests/multi_playground.html | 120 ++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 40 deletions(-) diff --git a/tests/multi_playground.html b/tests/multi_playground.html index 9cf4d3d7b..56975b265 100644 --- a/tests/multi_playground.html +++ b/tests/multi_playground.html @@ -15,49 +15,79 @@ - - +
+ + + From 03de6b6a92c966dea3e311a5fb3ad8e78104b4e8 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 31 May 2016 15:39:08 -0700 Subject: [PATCH 06/13] Move connections at the end of render, instead of in the middle of drawing --- core/block_render_svg.js | 116 ++++++++++++++++++++---------------- core/rendered_connection.js | 22 +++++++ 2 files changed, 87 insertions(+), 51 deletions(-) diff --git a/core/block_render_svg.js b/core/block_render_svg.js index aa17738c8..cad98f062 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -260,6 +260,7 @@ Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR = * If true, also render block's parent, grandparent, etc. Defaults to true. */ Blockly.BlockSvg.prototype.render = function(opt_bubble) { + Blockly.Field.startCache(); this.rendered = true; @@ -280,6 +281,8 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { var inputRows = this.renderCompute_(cursorX); this.renderDraw_(cursorX, inputRows); + this.renderMoveConnections(); + if (opt_bubble !== false) { // Render all blocks above this one (propagate a reflow). var parentBlock = this.getParent(); @@ -517,10 +520,6 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { } } - // Fetch the block's coordinates on the surface for use in anchoring - // the connections. - var connectionsXY = this.getRelativeToSurfaceXY(); - // Assemble the block's path. var steps = []; var inlineSteps = []; @@ -530,12 +529,11 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { var highlightSteps = []; var highlightInlineSteps = []; - this.renderDrawTop_(steps, highlightSteps, connectionsXY, - inputRows.rightEdge); + this.renderDrawTop_(steps, highlightSteps, inputRows.rightEdge); var cursorY = this.renderDrawRight_(steps, highlightSteps, inlineSteps, - highlightInlineSteps, connectionsXY, inputRows, iconWidth); - this.renderDrawBottom_(steps, highlightSteps, connectionsXY, cursorY); - this.renderDrawLeft_(steps, highlightSteps, connectionsXY); + highlightInlineSteps, inputRows, iconWidth); + this.renderDrawBottom_(steps, highlightSteps, cursorY); + this.renderDrawLeft_(steps, highlightSteps); var pathString = steps.join(' ') + '\n' + inlineSteps.join(' '); this.svgPath_.setAttribute('d', pathString); @@ -550,16 +548,51 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { } }; +/** + * Update all of the connections on this block with the new locations calculated + * in renderCompute. Also move all of the connected blocks based on the new + * connection locations. + */ +Blockly.BlockSvg.prototype.renderMoveConnections = function() { + var blockTL = this.getRelativeToSurfaceXY(); + if (this.previousConnection) { + this.previousConnection.moveToOffset(blockTL); + // Don't tighten the previous connection because it's an inferior + // connection. + } + + for (var i = 0; i < this.inputList.length; i++) { + var conn = this.inputList[i].connection; + if (conn) { + conn.moveToOffset(blockTL); + if (conn.isConnected()) { + conn.tighten_(); + } + } + } + + if (this.nextConnection) { + this.nextConnection.moveToOffset(blockTL); + if (this.nextConnection.isConnected()) { + this.nextConnection.tighten_(); + } + } + + if (this.outputConnection) { + this.outputConnection.moveToOffset(blockTL); + // Don't tighten the output connection because it's an inferior connection. + } +}; + /** * Render the top edge of the block. * @param {!Array.} steps Path of block outline. * @param {!Array.} highlightSteps Path of block highlights. - * @param {!Object} connectionsXY Location of block. * @param {number} rightEdge Minimum width of block. * @private */ Blockly.BlockSvg.prototype.renderDrawTop_ = - function(steps, highlightSteps, connectionsXY, rightEdge) { + function(steps, highlightSteps, rightEdge) { /* eslint-disable indent */ // Position the cursor at the top-left starting point. if (this.squareTopLeftCorner_) { @@ -587,12 +620,10 @@ Blockly.BlockSvg.prototype.renderDrawTop_ = highlightSteps.push('H', Blockly.BlockSvg.NOTCH_WIDTH - 15); steps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT); highlightSteps.push(Blockly.BlockSvg.NOTCH_PATH_LEFT_HIGHLIGHT); - // Create previous block connection. - var connectionX = connectionsXY.x + (this.RTL ? + + var connectionX = (this.RTL ? -Blockly.BlockSvg.NOTCH_WIDTH : Blockly.BlockSvg.NOTCH_WIDTH); - var connectionY = connectionsXY.y; - this.previousConnection.moveTo(connectionX, connectionY); - // This connection will be tightened when the parent renders. + this.previousConnection.setOffsetInBlock(connectionX, 0); } steps.push('H', rightEdge); highlightSteps.push('H', rightEdge - 0.5); @@ -605,7 +636,6 @@ Blockly.BlockSvg.prototype.renderDrawTop_ = * @param {!Array.} highlightSteps Path of block highlights. * @param {!Array.} inlineSteps Inline block outlines. * @param {!Array.} highlightInlineSteps Inline block highlights. - * @param {!Object} connectionsXY Location of block. * @param {!Array.>} inputRows 2D array of objects, each * containing position information. * @param {number} iconWidth Offset of first row due to icons. @@ -613,7 +643,7 @@ Blockly.BlockSvg.prototype.renderDrawTop_ = * @private */ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, - inlineSteps, highlightInlineSteps, connectionsXY, inputRows, iconWidth) { + inlineSteps, highlightInlineSteps, inputRows, iconWidth) { var cursorX; var cursorY = 0; var connectionX, connectionY; @@ -694,20 +724,16 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, } // Create inline input connection. if (this.RTL) { - connectionX = connectionsXY.x - cursorX - + connectionX = -cursorX - Blockly.BlockSvg.TAB_WIDTH + Blockly.BlockSvg.SEP_SPACE_X + input.renderWidth + 1; } else { - connectionX = connectionsXY.x + cursorX + + connectionX = cursorX + Blockly.BlockSvg.TAB_WIDTH - Blockly.BlockSvg.SEP_SPACE_X - input.renderWidth - 1; } - connectionY = connectionsXY.y + cursorY + - Blockly.BlockSvg.INLINE_PADDING_Y + 1; - input.connection.moveTo(connectionX, connectionY); - if (input.connection.isConnected()) { - input.connection.tighten_(); - } + connectionY = cursorY + Blockly.BlockSvg.INLINE_PADDING_Y + 1; + input.connection.setOffsetInBlock(connectionX, connectionY); } } @@ -749,12 +775,10 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, ',-2.1'); } // Create external input connection. - connectionX = connectionsXY.x + - (this.RTL ? -inputRows.rightEdge - 1 : inputRows.rightEdge + 1); - connectionY = connectionsXY.y + cursorY; - input.connection.moveTo(connectionX, connectionY); + connectionX = (this.RTL ? -inputRows.rightEdge - 1 : + inputRows.rightEdge + 1); + input.connection.setOffsetInBlock(connectionX, cursorY); if (input.connection.isConnected()) { - input.connection.tighten_(); this.width = Math.max(this.width, inputRows.rightEdge + input.connection.targetBlock().getHeightWidth().width - Blockly.BlockSvg.TAB_WIDTH + 1); @@ -832,11 +856,10 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, highlightSteps.push('H', inputRows.rightEdge - 0.5); } // Create statement connection. - connectionX = connectionsXY.x + (this.RTL ? -cursorX : cursorX + 1); - connectionY = connectionsXY.y + cursorY + 1; - input.connection.moveTo(connectionX, connectionY); + connectionX = (this.RTL ? -cursorX : cursorX + 1); + + input.connection.setOffsetInBlock(connectionX, cursorY + 1); if (input.connection.isConnected()) { - input.connection.tighten_(); this.width = Math.max(this.width, inputRows.statementEdge + input.connection.targetBlock().getHeightWidth().width); } @@ -867,12 +890,11 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, * Render the bottom edge of the block. * @param {!Array.} steps Path of block outline. * @param {!Array.} highlightSteps Path of block highlights. - * @param {!Object} connectionsXY Location of block. * @param {number} cursorY Height of block. * @private */ Blockly.BlockSvg.prototype.renderDrawBottom_ = - function(steps, highlightSteps, connectionsXY, cursorY) { + function(steps, highlightSteps, cursorY) { /* eslint-disable indent */ this.height += cursorY + 1; // Add one for the shadow. if (this.nextConnection) { @@ -881,15 +903,11 @@ Blockly.BlockSvg.prototype.renderDrawBottom_ = // Create next block connection. var connectionX; if (this.RTL) { - connectionX = connectionsXY.x - Blockly.BlockSvg.NOTCH_WIDTH; + connectionX = -Blockly.BlockSvg.NOTCH_WIDTH; } else { - connectionX = connectionsXY.x + Blockly.BlockSvg.NOTCH_WIDTH; - } - var connectionY = connectionsXY.y + cursorY + 1; - this.nextConnection.moveTo(connectionX, connectionY); - if (this.nextConnection.isConnected()) { - this.nextConnection.tighten_(); + connectionX = Blockly.BlockSvg.NOTCH_WIDTH; } + this.nextConnection.setOffsetInBlock(connectionX, cursorY + 1); this.height += 4; // Height of tab. } @@ -919,16 +937,12 @@ Blockly.BlockSvg.prototype.renderDrawBottom_ = * Render the left edge of the block. * @param {!Array.} steps Path of block outline. * @param {!Array.} highlightSteps Path of block highlights. - * @param {!Object} connectionsXY Location of block. * @private */ -Blockly.BlockSvg.prototype.renderDrawLeft_ = - function(steps, highlightSteps, connectionsXY) { - /* eslint-disable indent */ +Blockly.BlockSvg.prototype.renderDrawLeft_ = function(steps, highlightSteps) { if (this.outputConnection) { // Create output connection. - this.outputConnection.moveTo(connectionsXY.x, connectionsXY.y); - // This connection will be tightened when the parent renders. + this.outputConnection.setOffsetInBlock(0, 0); steps.push('V', Blockly.BlockSvg.TAB_HEIGHT); steps.push('c 0,-10 -' + Blockly.BlockSvg.TAB_WIDTH + ',8 -' + Blockly.BlockSvg.TAB_WIDTH + ',-7.5 s ' + Blockly.BlockSvg.TAB_WIDTH + @@ -954,4 +968,4 @@ Blockly.BlockSvg.prototype.renderDrawLeft_ = } } steps.push('z'); -}; /* eslint-enable indent */ +}; diff --git a/core/rendered_connection.js b/core/rendered_connection.js index 113639e61..29f6fc19e 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -37,6 +37,7 @@ goog.require('Blockly.Connection'); */ Blockly.RenderedConnection = function(source, type) { Blockly.RenderedConnection.superClass_.constructor.call(this, source, type); + this.offsetInBlock_ = new goog.math.Coordinate(0, 0); }; goog.inherits(Blockly.RenderedConnection, Blockly.Connection); @@ -123,6 +124,27 @@ Blockly.RenderedConnection.prototype.moveBy = function(dx, dy) { this.moveTo(this.x_ + dx, this.y_ + dy); }; +/** + * Move this connection to the location given by its offset within the block and + * the coordinate of the block's top left corner. + * @param {goog.math.Coordinate} blockTL The coordinate of the top left corner + * of the block. + */ +Blockly.RenderedConnection.prototype.moveToOffset = function(blockTL) { + this.moveTo(blockTL.x + this.offsetInBlock_.x, + blockTL.y + this.offsetInBlock_.y); +}; + +/** + * Set the offset of this connection relative to the top left of its block. + * @param {number} x The new relative x. + * @param {number} y The new relative y. + */ +Blockly.RenderedConnection.prototype.setOffsetInBlock = function(x, y) { + this.offsetInBlock_.x = x; + this.offsetInBlock_.y = y; +}; + /** * Move the blocks on either side of this connection right next to each other. * @private From dd205feda0fabd096a56a6758a42aff900c87ab4 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 31 May 2016 11:06:17 -0700 Subject: [PATCH 07/13] Add a button to randomly click and drag blocks in the playground. --- tests/playground.html | 63 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/tests/playground.html b/tests/playground.html index 6b6c81333..c74695d17 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -63,6 +63,7 @@ // Depending on the URL argument, render as LTR or RTL. var rtl = (document.location.search == '?rtl'); var workspace = null; +var fakeDragQueue = []; function start() { var toolbox = document.getElementById('toolbox'); @@ -178,6 +179,65 @@ function airstrike(n) { } } +function fakeDrag(id, dx, dy, opt_workspace) { + var ws = opt_workspace ? opt_workspace : Blockly.getMainWorkspace(); + var blockToDrag = ws.getBlockById(id); + + if (!blockToDrag) { + return; + } + var blockTop = blockToDrag.svgGroup_.getBoundingClientRect().top; + var blockLeft = blockToDrag.svgGroup_.getBoundingClientRect().left; + + // Click somewhere on the block. + var mouseDownEvent = new MouseEvent('mousedown', + {clientX: blockLeft + 5, clientY: blockTop + 5}); + blockToDrag.onMouseDown_(mouseDownEvent); + + // Throw in a move for good measure. + window.setTimeout( + function() { + var mouseMoveEvent = new MouseEvent('mousemove', + {clientX: blockLeft + dx, + clientY: blockTop + dy}); + blockToDrag.onMouseMove_(mouseMoveEvent); + + // Drop at dx, dy. + window.setTimeout( + function() { + var mouseUpEvent = new MouseEvent('mouseup', + {clientX: blockLeft + dx, + clientY: blockTop + dy}); + blockToDrag.onMouseUp_(mouseUpEvent); + + window.setTimeout(fakeDragWrapper(), 100); + }, 30); + }, 30); +}; + +function fakeDragWrapper() { + var dragInfo = fakeDragQueue.pop(); + if (dragInfo) { + fakeDrag(dragInfo.id, dragInfo.dx, dragInfo.dy, dragInfo.workspace); + } +} + +function fakeManyDrags() { + var ws = Blockly.getMainWorkspace(); + var blockList = ws.getAllBlocks(); + for (var i = 0; i < 2 * blockList.length; i++) { + fakeDragQueue.push( + { + id: blockList[Math.round(Math.random() * (blockList.length - 1))].id, + // Move some blocks up and to the left, but mostly down and to the right. + dx: Math.round((Math.random() - 0.25) * 200), + dy: Math.round((Math.random() - 0.25)* 200), + workspace: ws + }); + } + fakeDragWrapper(); +} + function spaghetti(n) { var xml = spaghettiXml; for(var i = 0; i < n; i++) { @@ -591,7 +651,8 @@ h1 { Stress test:   -

+ +

Log events:   From 66ca80c42f3b07018cf0c25732b14d3950f62a11 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Tue, 31 May 2016 15:48:32 -0700 Subject: [PATCH 08/13] Make function private --- core/block_render_svg.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/block_render_svg.js b/core/block_render_svg.js index cad98f062..328f4d849 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -260,7 +260,6 @@ Blockly.BlockSvg.INNER_BOTTOM_LEFT_CORNER_HIGHLIGHT_LTR = * If true, also render block's parent, grandparent, etc. Defaults to true. */ Blockly.BlockSvg.prototype.render = function(opt_bubble) { - Blockly.Field.startCache(); this.rendered = true; @@ -280,8 +279,7 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { var inputRows = this.renderCompute_(cursorX); this.renderDraw_(cursorX, inputRows); - - this.renderMoveConnections(); + this.renderMoveConnections_(); if (opt_bubble !== false) { // Render all blocks above this one (propagate a reflow). @@ -552,8 +550,9 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { * Update all of the connections on this block with the new locations calculated * in renderCompute. Also move all of the connected blocks based on the new * connection locations. + * @private */ -Blockly.BlockSvg.prototype.renderMoveConnections = function() { +Blockly.BlockSvg.prototype.renderMoveConnections_ = function() { var blockTL = this.getRelativeToSurfaceXY(); if (this.previousConnection) { this.previousConnection.moveToOffset(blockTL); From 53cf0be791bf4774c5787c34986af869175a4fd3 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 1 Jun 2016 15:24:23 -0700 Subject: [PATCH 09/13] lint --- tests/playground.html | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/playground.html b/tests/playground.html index c74695d17..69bb0eb84 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -63,7 +63,7 @@ // Depending on the URL argument, render as LTR or RTL. var rtl = (document.location.search == '?rtl'); var workspace = null; -var fakeDragQueue = []; +var fakeDragStack = []; function start() { var toolbox = document.getElementById('toolbox'); @@ -180,11 +180,11 @@ function airstrike(n) { } function fakeDrag(id, dx, dy, opt_workspace) { - var ws = opt_workspace ? opt_workspace : Blockly.getMainWorkspace(); + var ws = opt_workspace || Blockly.getMainWorkspace(); var blockToDrag = ws.getBlockById(id); if (!blockToDrag) { - return; + fakeDragWrapper(); } var blockTop = blockToDrag.svgGroup_.getBoundingClientRect().top; var blockLeft = blockToDrag.svgGroup_.getBoundingClientRect().left; @@ -195,7 +195,7 @@ function fakeDrag(id, dx, dy, opt_workspace) { blockToDrag.onMouseDown_(mouseDownEvent); // Throw in a move for good measure. - window.setTimeout( + setTimeout( function() { var mouseMoveEvent = new MouseEvent('mousemove', {clientX: blockLeft + dx, @@ -203,36 +203,35 @@ function fakeDrag(id, dx, dy, opt_workspace) { blockToDrag.onMouseMove_(mouseMoveEvent); // Drop at dx, dy. - window.setTimeout( + setTimeout( function() { var mouseUpEvent = new MouseEvent('mouseup', {clientX: blockLeft + dx, clientY: blockTop + dy}); blockToDrag.onMouseUp_(mouseUpEvent); - window.setTimeout(fakeDragWrapper(), 100); + setTimeout(fakeDragWrapper(), 100); }, 30); }, 30); }; function fakeDragWrapper() { - var dragInfo = fakeDragQueue.pop(); + var dragInfo = fakeDragStack.pop(); if (dragInfo) { fakeDrag(dragInfo.id, dragInfo.dx, dragInfo.dy, dragInfo.workspace); } } function fakeManyDrags() { - var ws = Blockly.getMainWorkspace(); - var blockList = ws.getAllBlocks(); + var blockList = workspace.getAllBlocks(); for (var i = 0; i < 2 * blockList.length; i++) { - fakeDragQueue.push( + fakeDragStack.push( { id: blockList[Math.round(Math.random() * (blockList.length - 1))].id, // Move some blocks up and to the left, but mostly down and to the right. dx: Math.round((Math.random() - 0.25) * 200), - dy: Math.round((Math.random() - 0.25)* 200), - workspace: ws + dy: Math.round((Math.random() - 0.25) * 200), + workspace: workspace }); } fakeDragWrapper(); @@ -651,8 +650,8 @@ h1 { Stress test:   - -

+ +

Log events:   From c3f27127d3444e1b040b68b2ec3e78aedafe4b5b Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 1 Jun 2016 15:31:43 -0700 Subject: [PATCH 10/13] lint --- core/block_render_svg.js | 19 +++++++++---------- core/rendered_connection.js | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/core/block_render_svg.js b/core/block_render_svg.js index 328f4d849..017bc3402 100644 --- a/core/block_render_svg.js +++ b/core/block_render_svg.js @@ -554,10 +554,13 @@ Blockly.BlockSvg.prototype.renderDraw_ = function(iconWidth, inputRows) { */ Blockly.BlockSvg.prototype.renderMoveConnections_ = function() { var blockTL = this.getRelativeToSurfaceXY(); + // Don't tighten previous or output connecitons because they are inferior + // connections. if (this.previousConnection) { this.previousConnection.moveToOffset(blockTL); - // Don't tighten the previous connection because it's an inferior - // connection. + } + if (this.outputConnection) { + this.outputConnection.moveToOffset(blockTL); } for (var i = 0; i < this.inputList.length; i++) { @@ -577,10 +580,6 @@ Blockly.BlockSvg.prototype.renderMoveConnections_ = function() { } } - if (this.outputConnection) { - this.outputConnection.moveToOffset(blockTL); - // Don't tighten the output connection because it's an inferior connection. - } }; /** @@ -774,8 +773,8 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, ',-2.1'); } // Create external input connection. - connectionX = (this.RTL ? -inputRows.rightEdge - 1 : - inputRows.rightEdge + 1); + connectionX = this.RTL ? -inputRows.rightEdge - 1 : + inputRows.rightEdge + 1; input.connection.setOffsetInBlock(connectionX, cursorY); if (input.connection.isConnected()) { this.width = Math.max(this.width, inputRows.rightEdge + @@ -855,9 +854,9 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps, highlightSteps.push('H', inputRows.rightEdge - 0.5); } // Create statement connection. - connectionX = (this.RTL ? -cursorX : cursorX + 1); - + connectionX = this.RTL ? -cursorX : cursorX + 1; input.connection.setOffsetInBlock(connectionX, cursorY + 1); + if (input.connection.isConnected()) { this.width = Math.max(this.width, inputRows.statementEdge + input.connection.targetBlock().getHeightWidth().width); diff --git a/core/rendered_connection.js b/core/rendered_connection.js index 29f6fc19e..20186b098 100644 --- a/core/rendered_connection.js +++ b/core/rendered_connection.js @@ -127,7 +127,7 @@ Blockly.RenderedConnection.prototype.moveBy = function(dx, dy) { /** * Move this connection to the location given by its offset within the block and * the coordinate of the block's top left corner. - * @param {goog.math.Coordinate} blockTL The coordinate of the top left corner + * @param {!goog.math.Coordinate} blockTL The coordinate of the top left corner * of the block. */ Blockly.RenderedConnection.prototype.moveToOffset = function(blockTL) { From 1f2107ee0862cdfb137b3c44c2adc2315ab8e123 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 1 Jun 2016 16:20:56 -0700 Subject: [PATCH 11/13] add missing return in fake drag --- tests/playground.html | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/playground.html b/tests/playground.html index 69bb0eb84..e27b403c4 100644 --- a/tests/playground.html +++ b/tests/playground.html @@ -185,6 +185,7 @@ function fakeDrag(id, dx, dy, opt_workspace) { if (!blockToDrag) { fakeDragWrapper(); + return; } var blockTop = blockToDrag.svgGroup_.getBoundingClientRect().top; var blockLeft = blockToDrag.svgGroup_.getBoundingClientRect().left; From 9709bd00886685a77538eb07da7af2b3a126ab16 Mon Sep 17 00:00:00 2001 From: Rachel Fenichel Date: Wed, 1 Jun 2016 16:23:49 -0700 Subject: [PATCH 12/13] get rid of drag_tests file: --- tests/drag_tests.html | 606 ------------------------------------------ 1 file changed, 606 deletions(-) delete mode 100644 tests/drag_tests.html diff --git a/tests/drag_tests.html b/tests/drag_tests.html deleted file mode 100644 index 06acef0f5..000000000 --- a/tests/drag_tests.html +++ /dev/null @@ -1,606 +0,0 @@ - - - - -Blockly Playground - - - - - - - - - - - - - - - - -

- - - -

Blockly Playground

- -

Show - - Hide

- - - -

- -   - - -

- -

- Stress test:   - - -

-

- Log events:   - -

- - - From 32192850addff36b8bdd47b96f1e402ba88e3526 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Wed, 1 Jun 2016 17:18:02 -0700 Subject: [PATCH 13/13] Generated JS helper functions should be camelCase. Complying with Google style guide. --- generators/javascript.js | 6 +++--- generators/javascript/colour.js | 6 +++--- generators/javascript/lists.js | 14 +++++++------- generators/javascript/math.js | 14 +++++++------- generators/javascript/text.js | 6 +++--- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/generators/javascript.js b/generators/javascript.js index 2860cf457..dad0c4931 100644 --- a/generators/javascript.js +++ b/generators/javascript.js @@ -196,9 +196,9 @@ Blockly.JavaScript.scrub_ = function(block, code) { } // Collect comments for all value arguments. // Don't collect comments for nested statements. - for (var x = 0; x < block.inputList.length; x++) { - if (block.inputList[x].type == Blockly.INPUT_VALUE) { - var childBlock = block.inputList[x].connection.targetBlock(); + for (var i = 0; i < block.inputList.length; i++) { + if (block.inputList[i].type == Blockly.INPUT_VALUE) { + var childBlock = block.inputList[i].connection.targetBlock(); if (childBlock) { var comment = Blockly.JavaScript.allNestedComments(childBlock); if (comment) { diff --git a/generators/javascript/colour.js b/generators/javascript/colour.js index 205bbf293..b8e290360 100644 --- a/generators/javascript/colour.js +++ b/generators/javascript/colour.js @@ -38,7 +38,7 @@ Blockly.JavaScript['colour_picker'] = function(block) { Blockly.JavaScript['colour_random'] = function(block) { // Generate a random colour. var functionName = Blockly.JavaScript.provideFunction_( - 'colour_random', + 'colourRandom', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '() {', ' var num = Math.floor(Math.random() * Math.pow(2, 24));', ' return \'#\' + (\'00000\' + num.toString(16)).substr(-6);', @@ -56,7 +56,7 @@ Blockly.JavaScript['colour_rgb'] = function(block) { var blue = Blockly.JavaScript.valueToCode(block, 'BLUE', Blockly.JavaScript.ORDER_COMMA) || 0; var functionName = Blockly.JavaScript.provideFunction_( - 'colour_rgb', + 'colourRgb', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b) {', ' r = Math.max(Math.min(Number(r), 100), 0) * 2.55;', @@ -80,7 +80,7 @@ Blockly.JavaScript['colour_blend'] = function(block) { var ratio = Blockly.JavaScript.valueToCode(block, 'RATIO', Blockly.JavaScript.ORDER_COMMA) || 0.5; var functionName = Blockly.JavaScript.provideFunction_( - 'colour_blend', + 'colourBlend', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(c1, c2, ratio) {', ' ratio = Math.max(Math.min(Number(ratio), 1), 0);', diff --git a/generators/javascript/lists.js b/generators/javascript/lists.js index c9115e9e4..2a06eab32 100644 --- a/generators/javascript/lists.js +++ b/generators/javascript/lists.js @@ -48,7 +48,7 @@ Blockly.JavaScript['lists_create_with'] = function(block) { Blockly.JavaScript['lists_repeat'] = function(block) { // Create a list with one element repeated. var functionName = Blockly.JavaScript.provideFunction_( - 'lists_repeat', + 'listsRepeat', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(value, n) {', ' var array = [];', @@ -145,7 +145,7 @@ Blockly.JavaScript['lists_getIndex'] = function(block) { return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; } else if (mode == 'GET_REMOVE' || mode == 'REMOVE') { var functionName = Blockly.JavaScript.provideFunction_( - 'lists_remove_from_end', + 'listsRemoveFromEnd', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(list, x) {', ' x = list.length - x;', @@ -160,7 +160,7 @@ Blockly.JavaScript['lists_getIndex'] = function(block) { } } else if (where == 'RANDOM') { var functionName = Blockly.JavaScript.provideFunction_( - 'lists_get_random_item', + 'listsGetRandomItem', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(list, remove) {', ' var x = Math.floor(Math.random() * list.length);', @@ -272,7 +272,7 @@ Blockly.JavaScript['lists_getSublist'] = function(block) { var code = list + '.concat()'; } else { var functionName = Blockly.JavaScript.provideFunction_( - 'lists_get_sublist', + 'listsGetSublist', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(list, where1, at1, where2, at2) {', ' function getAt(where, at) {', @@ -307,7 +307,7 @@ Blockly.JavaScript['lists_sort'] = function(block) { var direction = block.getFieldValue('DIRECTION') === '1' ? 1 : -1; var type = block.getFieldValue('TYPE'); var getCompareFunctionName = Blockly.JavaScript.provideFunction_( - 'lists_get_sort_compare', + 'listsGetSortCompare', ['function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(type, direction) {', ' var compareFuncs = {', @@ -322,8 +322,8 @@ Blockly.JavaScript['lists_sort'] = function(block) { ' var compare = compareFuncs[type];', ' return function(a, b) { return compare(a, b) * direction; }', '}']); - return ['(' + listCode + ').slice().sort(' + - getCompareFunctionName + '("' + type + '", ' + direction + '))', + return ['(' + listCode + ').slice().sort(' + + getCompareFunctionName + '("' + type + '", ' + direction + '))', Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/generators/javascript/math.js b/generators/javascript/math.js index f0ecc6255..17451a5b2 100644 --- a/generators/javascript/math.js +++ b/generators/javascript/math.js @@ -167,7 +167,7 @@ Blockly.JavaScript['math_number_property'] = function(block) { if (dropdown_property == 'PRIME') { // Prime is a special case as it is not a one-liner test. var functionName = Blockly.JavaScript.provideFunction_( - 'math_isPrime', + 'mathIsPrime', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(n) {', ' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', ' if (n == 2 || n == 3) {', @@ -253,7 +253,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { case 'AVERAGE': // math_median([null,null,1,3]) == 2.0. var functionName = Blockly.JavaScript.provideFunction_( - 'math_mean', + 'mathMean', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(myList) {', ' return myList.reduce(function(x, y) {return x + y;}) / ' + @@ -266,7 +266,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { case 'MEDIAN': // math_median([null,null,1,3]) == 2.0. var functionName = Blockly.JavaScript.provideFunction_( - 'math_median', + 'mathMedian', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(myList) {', ' var localList = myList.filter(function (x) ' + @@ -289,7 +289,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { // the returned result is provided as an array. // Mode of [3, 'x', 'x', 1, 1, 2, '3'] -> ['x', 1]. var functionName = Blockly.JavaScript.provideFunction_( - 'math_modes', + 'mathModes', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(values) {', ' var modes = [];', @@ -325,7 +325,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { break; case 'STD_DEV': var functionName = Blockly.JavaScript.provideFunction_( - 'math_standard_deviation', + 'mathStandardDeviation', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(numbers) {', ' var n = numbers.length;', @@ -344,7 +344,7 @@ Blockly.JavaScript['math_on_list'] = function(block) { break; case 'RANDOM': var functionName = Blockly.JavaScript.provideFunction_( - 'math_random_list', + 'mathRandomList', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(list) {', ' var x = Math.floor(Math.random() * list.length);', @@ -390,7 +390,7 @@ Blockly.JavaScript['math_random_int'] = function(block) { var argument1 = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_COMMA) || '0'; var functionName = Blockly.JavaScript.provideFunction_( - 'math_random_int', + 'mathRandomInt', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(a, b) {', ' if (a > b) {', diff --git a/generators/javascript/text.js b/generators/javascript/text.js index 481aed48a..78e94ed16 100644 --- a/generators/javascript/text.js +++ b/generators/javascript/text.js @@ -129,7 +129,7 @@ Blockly.JavaScript['text_charAt'] = function(block) { return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; case 'RANDOM': var functionName = Blockly.JavaScript.provideFunction_( - 'text_random_letter', + 'textRandomLetter', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(text) {', ' var x = Math.floor(Math.random() * text.length);', @@ -155,7 +155,7 @@ Blockly.JavaScript['text_getSubstring'] = function(block) { var code = text; } else { var functionName = Blockly.JavaScript.provideFunction_( - 'text_get_substring', + 'textGetSubstring', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(text, where1, at1, where2, at2) {', ' function getAt(where, at) {', @@ -199,7 +199,7 @@ Blockly.JavaScript['text_changeCase'] = function(block) { } else { // Title case is not a native JavaScript function. Define one. var functionName = Blockly.JavaScript.provideFunction_( - 'text_toTitleCase', + 'textToTitleCase', [ 'function ' + Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_ + '(str) {', ' return str.replace(/\\S+/g,',
+ +
LTR