mirror of
https://github.com/google/blockly.git
synced 2026-01-09 10:00:09 +01:00
Merge branch 'develop' of https://github.com/google/blockly into develop
This commit is contained in:
@@ -279,6 +279,7 @@ 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).
|
||||
@@ -517,10 +518,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 +527,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 +546,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.
|
||||
* @private
|
||||
*/
|
||||
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);
|
||||
}
|
||||
if (this.outputConnection) {
|
||||
this.outputConnection.moveToOffset(blockTL);
|
||||
}
|
||||
|
||||
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_();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Render the top edge of the block.
|
||||
* @param {!Array.<string>} steps Path of block outline.
|
||||
* @param {!Array.<string>} 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 +618,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 +634,6 @@ Blockly.BlockSvg.prototype.renderDrawTop_ =
|
||||
* @param {!Array.<string>} highlightSteps Path of block highlights.
|
||||
* @param {!Array.<string>} inlineSteps Inline block outlines.
|
||||
* @param {!Array.<string>} highlightInlineSteps Inline block highlights.
|
||||
* @param {!Object} connectionsXY Location of block.
|
||||
* @param {!Array.<!Array.<!Object>>} inputRows 2D array of objects, each
|
||||
* containing position information.
|
||||
* @param {number} iconWidth Offset of first row due to icons.
|
||||
@@ -613,7 +641,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 +722,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 +773,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 +854,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 +888,11 @@ Blockly.BlockSvg.prototype.renderDrawRight_ = function(steps, highlightSteps,
|
||||
* Render the bottom edge of the block.
|
||||
* @param {!Array.<string>} steps Path of block outline.
|
||||
* @param {!Array.<string>} 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 +901,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 +935,12 @@ Blockly.BlockSvg.prototype.renderDrawBottom_ =
|
||||
* Render the left edge of the block.
|
||||
* @param {!Array.<string>} steps Path of block outline.
|
||||
* @param {!Array.<string>} 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 +966,4 @@ Blockly.BlockSvg.prototype.renderDrawLeft_ =
|
||||
}
|
||||
}
|
||||
steps.push('z');
|
||||
}; /* eslint-enable indent */
|
||||
};
|
||||
|
||||
@@ -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!';
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
37
core/xml.js
37
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;
|
||||
@@ -520,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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);',
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {',
|
||||
|
||||
@@ -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,',
|
||||
|
||||
@@ -15,49 +15,79 @@
|
||||
<script src="../blocks/procedures.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
// True if we're showing toolboxes, false if showing open flyouts.
|
||||
var showingToolbox = true;
|
||||
// The workspaces we're showing.
|
||||
var workspaces = [];
|
||||
|
||||
function start() {
|
||||
startBlocklyInstance('VertStartLTR', false, false, 'start');
|
||||
startBlocklyInstance('VertStartRTL', true, false, 'start');
|
||||
|
||||
startBlocklyInstance('VertEndLTR', false, false, 'end');
|
||||
startBlocklyInstance('VertEndRTL', true, false, 'end');
|
||||
|
||||
|
||||
startBlocklyInstance('HorizontalStartLTR', false, true, 'start');
|
||||
startBlocklyInstance('HorizontalStartRTL', true, true, 'start');
|
||||
|
||||
startBlocklyInstance('HorizontalEndLTR', false, true, 'end');
|
||||
startBlocklyInstance('HorizontalEndRTL', true, true, 'end');
|
||||
}
|
||||
|
||||
function startBlocklyInstance(suffix, rtl, horizontalLayout, position) {
|
||||
var toolbox = document.getElementById('toolbox_categoriesScroll');
|
||||
var options = {
|
||||
comments: false,
|
||||
disable: false,
|
||||
collapse: false,
|
||||
maxBlocks: Infinity,
|
||||
media: '../media/',
|
||||
readOnly: false,
|
||||
rtl: rtl,
|
||||
scrollbars: true,
|
||||
toolbox: toolbox,
|
||||
trashcan: true,
|
||||
horizontalLayout: horizontalLayout,
|
||||
toolboxPosition: position,
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: false,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
var options = {
|
||||
comments: false,
|
||||
disable: false,
|
||||
collapse: false,
|
||||
maxBlocks: Infinity,
|
||||
media: '../media/',
|
||||
readOnly: false,
|
||||
rtl: false,
|
||||
scrollbars: true,
|
||||
trashcan: true,
|
||||
horizontalLayout: false,
|
||||
toolboxPosition: 'start',
|
||||
zoom: {
|
||||
controls: true,
|
||||
wheel: false,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
},
|
||||
};
|
||||
Blockly.inject('blocklyDiv' + suffix, options);
|
||||
|
||||
function start(toolboxId) {
|
||||
startBlocklyInstance('VertStartLTR', false, false, 'start', toolboxId);
|
||||
startBlocklyInstance('VertStartRTL', true, false, 'start', toolboxId);
|
||||
|
||||
startBlocklyInstance('VertEndLTR', false, false, 'end', toolboxId);
|
||||
startBlocklyInstance('VertEndRTL', true, false, 'end', toolboxId);
|
||||
|
||||
startBlocklyInstance('HorizontalStartLTR', false, true, 'start', toolboxId);
|
||||
startBlocklyInstance('HorizontalStartRTL', true, true, 'start', toolboxId);
|
||||
|
||||
startBlocklyInstance('HorizontalEndLTR', false, true, 'end', toolboxId);
|
||||
startBlocklyInstance('HorizontalEndRTL', true, true, 'end', toolboxId);
|
||||
}
|
||||
|
||||
function startBlocklyInstance(suffix, rtl, horizontalLayout, position,
|
||||
toolboxId) {
|
||||
var toolbox = document.getElementById(toolboxId);
|
||||
options.rtl = rtl;
|
||||
options.toolbox = toolbox;
|
||||
options.horizontalLayout = horizontalLayout;
|
||||
options.toolboxPosition = position;
|
||||
workspaces.push(Blockly.inject('blocklyDiv' + suffix, options));
|
||||
}
|
||||
|
||||
function swapFlyoutToolbox() {
|
||||
var button = document.getElementById('swap');
|
||||
var toolboxId;
|
||||
if (showingToolbox) {
|
||||
swap.textContent = 'Show toolboxes';
|
||||
toolboxId = 'toolbox_alwaysOpen';
|
||||
} else {
|
||||
toolboxId = 'toolbox_categoriesScroll';
|
||||
swap.textContent = 'Show flyouts';
|
||||
}
|
||||
|
||||
// Dispose of all existing workspace stuff so
|
||||
// we can repopulate.
|
||||
for (var i = 0, ws; ws = workspaces[i]; i++) {
|
||||
ws.dispose();
|
||||
}
|
||||
workspaces = [];
|
||||
|
||||
// Re-inject all the workspaces with new ids.
|
||||
start(toolboxId);
|
||||
showingToolbox = !showingToolbox;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -92,12 +122,22 @@ h1 {
|
||||
#importExport {
|
||||
font-family: monospace;
|
||||
}
|
||||
#swap {
|
||||
background-color: #4CAF50; /* Green */
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="start()">
|
||||
|
||||
<body onload="start('toolbox_categoriesScroll')">
|
||||
<div id="collaborators"></div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<button id="swap" type="button" valaue="Swap toolbox" onclick="swapFlyoutToolbox()">Show Flyouts</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td/>
|
||||
<td>LTR</td>
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
// Depending on the URL argument, render as LTR or RTL.
|
||||
var rtl = (document.location.search == '?rtl');
|
||||
var workspace = null;
|
||||
var fakeDragStack = [];
|
||||
|
||||
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 || Blockly.getMainWorkspace();
|
||||
var blockToDrag = ws.getBlockById(id);
|
||||
|
||||
if (!blockToDrag) {
|
||||
fakeDragWrapper();
|
||||
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.
|
||||
setTimeout(
|
||||
function() {
|
||||
var mouseMoveEvent = new MouseEvent('mousemove',
|
||||
{clientX: blockLeft + dx,
|
||||
clientY: blockTop + dy});
|
||||
blockToDrag.onMouseMove_(mouseMoveEvent);
|
||||
|
||||
// Drop at dx, dy.
|
||||
setTimeout(
|
||||
function() {
|
||||
var mouseUpEvent = new MouseEvent('mouseup',
|
||||
{clientX: blockLeft + dx,
|
||||
clientY: blockTop + dy});
|
||||
blockToDrag.onMouseUp_(mouseUpEvent);
|
||||
|
||||
setTimeout(fakeDragWrapper(), 100);
|
||||
}, 30);
|
||||
}, 30);
|
||||
};
|
||||
|
||||
function fakeDragWrapper() {
|
||||
var dragInfo = fakeDragStack.pop();
|
||||
if (dragInfo) {
|
||||
fakeDrag(dragInfo.id, dragInfo.dx, dragInfo.dy, dragInfo.workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function fakeManyDrags() {
|
||||
var blockList = workspace.getAllBlocks();
|
||||
for (var i = 0; i < 2 * blockList.length; i++) {
|
||||
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: workspace
|
||||
});
|
||||
}
|
||||
fakeDragWrapper();
|
||||
}
|
||||
|
||||
function spaghetti(n) {
|
||||
var xml = spaghettiXml;
|
||||
for(var i = 0; i < n; i++) {
|
||||
@@ -591,6 +651,7 @@ h1 {
|
||||
Stress test:
|
||||
<input type="button" value="Airstrike!" onclick="airstrike(100)">
|
||||
<input type="button" value="Spaghetti!" onclick="spaghetti(8)">
|
||||
<input type="button" value="Fake some drags!" onclick="fakeManyDrags()">
|
||||
</p>
|
||||
<p>
|
||||
Log events:
|
||||
|
||||
Reference in New Issue
Block a user