Merge branch 'develop' of https://github.com/google/blockly into develop

This commit is contained in:
Sean Lip
2016-06-02 15:52:15 -07:00
12 changed files with 287 additions and 133 deletions

View File

@@ -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 */
};

View File

@@ -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!';
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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);',

View File

@@ -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];
};

View File

@@ -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) {',

View File

@@ -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,',

View File

@@ -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>

View File

@@ -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: &nbsp;
<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: &nbsp;