Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Rachel Fenichel
2016-06-01 15:35:23 -07:00
12 changed files with 293 additions and 126 deletions

View File

@@ -12,8 +12,8 @@ What Does Accessible Blockly Do?
Use Accessible Blockly in Your Web App
-----------
1. see the basic demo under blockly/demos/accessible. This covers the absolute minimum required to import Accessible Blockly into your own web app.
1. See the basic demo under blockly/demos/accessible. This covers the absolute minimum required to import Accessible Blockly into your own web app.
2. You will need to import the files in the same order as in the demo: utils.service.js will need to be the first Angular file imported.
3. When the DOMContentLoaded event fires, call ng.platform.browser.bootstrap() on the main component to be loaded. This will usually be blocklyApp.AppView, but if you have another component that wraps it, use that one instead.
4. You will need to implement a runCode() function in the global scope. This function will be called when the user presses the Run Code button in the Accessible Blockly app.
4. If you want to customize the toolbar, you will need an ACCESSIBLE_GLOBALS object in the global scope. It should have a 'toolbarButtonConfig' key, whose value is an Array. Each element in this array should be an Object with two keys: 'text' (the text to display on the button) and 'action' (the function that gets run when the button is clicked).
5. Note that we do not support having multiple Accessible Blockly apps in a single webpage.

View File

@@ -78,6 +78,8 @@ blocklyApp.FieldView = ng.core
}],
ngOnInit: function() {
var elementsNeedingIds = this.generateElementNames(this.field);
// Warning: this assumes that the elements returned by
// this.generateElementNames() are unique.
this.idMap = this.utilsService.generateIds(elementsNeedingIds);
},
generateAriaLabelledByAttr: function() {

File diff suppressed because one or more lines are too long

View File

@@ -32,10 +32,13 @@ blocklyApp.WorkspaceView = ng.core
<h3 #workspaceTitle id="blockly-workspace-title">{{stringMap['WORKSPACE']}}</h3>
</label>
<div id="blockly-workspace-toolbar" (keydown)="onWorkspaceToolbarKeypress($event, getActiveElementId())">
<button id='run-code' (click)='runCode()' disabled={{disableRunCode()}}
[attr.aria-disabled]='disableRunCode()' class='blocklyTree'>{{stringMap['RUN_CODE']}}</button>
<button id='clear-workspace' (click)='workspace.clear()' disabled={{disableRunCode()}}
[attr.aria-disabled]='disableRunCode()' class='blocklyTree'>{{stringMap['CLEAR_WORKSPACE']}}</button>
<span *ngFor="#buttonConfig of toolbarButtonConfig">
<button (click)='buttonConfig.action()' class='blocklyTree'>
{{buttonConfig.text}}
</button>
</span>
<button id='clear-workspace' (click)='workspace.clear()' disabled={{disableClearWorkspace()}}
[attr.aria-disabled]='disableClearWorkspace()' class='blocklyTree'>{{stringMap['CLEAR_WORKSPACE']}}</button>
</div>
<div *ngIf="workspace">
<ol #tree id={{makeId(i)}} *ngFor="#block of workspace.topBlocks_; #i=index"
@@ -55,9 +58,16 @@ blocklyApp.WorkspaceView = ng.core
this.workspace = blocklyApp.workspace;
this.treeService = _treeService;
}
// ACCESSIBLE_GLOBALS is a global variable defined by the containing
// page. It should contain a key, toolbarButtonConfig, whose
// corresponding value is an Array with two keys: 'text' and 'action'.
// The first is the text to display on the button, and the second is the
// function that gets run when the button is clicked.
this.toolbarButtonConfig =
ACCESSIBLE_GLOBALS && ACCESSIBLE_GLOBALS.toolbarButtonConfig ?
ACCESSIBLE_GLOBALS.toolbarButtonConfig : [];
this.stringMap = {
'WORKSPACE': Blockly.Msg.WORKSPACE,
'RUN_CODE': Blockly.Msg.RUN_CODE,
'CLEAR_WORKSPACE': Blockly.Msg.CLEAR_WORKSPACE
};
}],
@@ -73,10 +83,7 @@ blocklyApp.WorkspaceView = ng.core
makeId: function(index) {
return 'blockly-workspace-tree' + index;
},
runCode: function() {
runCode();
},
disableRunCode: function() {
disableClearWorkspace: function() {
if (blocklyApp.workspace.topBlocks_.length){
return undefined;
} else {

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

@@ -52,6 +52,11 @@
<blockly-app></blockly-app>
<script>
var ACCESSIBLE_GLOBALS = {
// Additional buttons for the workspace toolbar that
// go before the "Clear Workspace" button.
toolbarButtonConfig: []
};
document.addEventListener('DOMContentLoaded', function() {
ng.platform.browser.bootstrap(blocklyApp.AppView);
});

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,64 @@ function airstrike(n) {
}
}
function fakeDrag(id, dx, dy, opt_workspace) {
var ws = opt_workspace || Blockly.getMainWorkspace();
var blockToDrag = ws.getBlockById(id);
if (!blockToDrag) {
fakeDragWrapper();
}
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 +650,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;