Commit 3b3ef79f authored by Neil Fraser's avatar Neil Fraser

Create block id database.

parent b2bbde04
...@@ -46,11 +46,16 @@ goog.require('goog.string'); ...@@ -46,11 +46,16 @@ goog.require('goog.string');
* @param {!Blockly.Workspace} workspace The block's workspace. * @param {!Blockly.Workspace} workspace The block's workspace.
* @param {?string} prototypeName Name of the language object containing * @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block. * type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @constructor * @constructor
*/ */
Blockly.Block = function(workspace, prototypeName) { Blockly.Block = function(workspace, prototypeName, opt_id) {
/** @type {string} */ /** @type {string} */
this.id = Blockly.genUid(); this.id = opt_id || Blockly.genUid();
goog.asserts.assert(!Blockly.Block.getById(this.id),
'Error: Block "%s" already exists.', this.id);
Blockly.Block.BlockDB_[this.id] = this;
/** @type {Blockly.Connection} */ /** @type {Blockly.Connection} */
this.outputConnection = null; this.outputConnection = null;
/** @type {Blockly.Connection} */ /** @type {Blockly.Connection} */
...@@ -140,20 +145,6 @@ Blockly.Block.obtain = function(workspace, prototypeName) { ...@@ -140,20 +145,6 @@ Blockly.Block.obtain = function(workspace, prototypeName) {
*/ */
Blockly.Block.prototype.data = null; Blockly.Block.prototype.data = null;
/**
* Get an existing block.
* @param {string} id The block's id.
* @param {!Blockly.Workspace} workspace The block's workspace.
* @return {Blockly.Block} The found block, or null if not found.
*/
Blockly.Block.getById = function(id, workspace) {
if (Blockly.Realtime.isEnabled()) {
return Blockly.Realtime.getBlockById(id);
} else {
return workspace.getBlockById(id);
}
};
/** /**
* Dispose of this block. * Dispose of this block.
* @param {boolean} healStack If true, then try to heal any gap by connecting * @param {boolean} healStack If true, then try to heal any gap by connecting
...@@ -201,10 +192,8 @@ Blockly.Block.prototype.dispose = function(healStack, animate, ...@@ -201,10 +192,8 @@ Blockly.Block.prototype.dispose = function(healStack, animate,
} }
connections[i].dispose(); connections[i].dispose();
} }
// Remove from Realtime set of blocks. // Remove from block database.
if (Blockly.Realtime.isEnabled() && !Blockly.Realtime.withinSync) { delete Blockly.Block.BlockDB_[this.id];
Blockly.Realtime.removeBlock(this);
}
}; };
/** /**
...@@ -1240,3 +1229,19 @@ Blockly.Block.prototype.getRelativeToSurfaceXY = function() { ...@@ -1240,3 +1229,19 @@ Blockly.Block.prototype.getRelativeToSurfaceXY = function() {
Blockly.Block.prototype.moveBy = function(dx, dy) { Blockly.Block.prototype.moveBy = function(dx, dy) {
this.xy_.translate(dx, dy); this.xy_.translate(dx, dy);
}; };
/**
* Database of all blocks.
* @private
*/
Blockly.Block.BlockDB_ = Object.create(null);
/**
* Find the block with the specified ID.
* @param {string} id ID of block to find.
* @return {Blockly.Block} The sought after block or null if not found.
*/
Blockly.Block.getById = function(id) {
return Blockly.Block.BlockDB_[id] || null;
};
...@@ -40,11 +40,14 @@ goog.require('goog.math.Coordinate'); ...@@ -40,11 +40,14 @@ goog.require('goog.math.Coordinate');
* @param {!Blockly.Workspace} workspace The block's workspace. * @param {!Blockly.Workspace} workspace The block's workspace.
* @param {?string} prototypeName Name of the language object containing * @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block. * type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @extends {Blockly.Block} * @extends {Blockly.Block}
* @constructor * @constructor
*/ */
Blockly.BlockSvg = function(workspace, prototypeName) { Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
Blockly.BlockSvg.superClass_.constructor.call(this, workspace, prototypeName); Blockly.BlockSvg.superClass_.constructor.call(this,
workspace, prototypeName, opt_id);
// Create core elements for the block. // Create core elements for the block.
/** @type {SVGElement} */ /** @type {SVGElement} */
this.svgGroup_ = Blockly.createSvgElement('g', {}, null); this.svgGroup_ = Blockly.createSvgElement('g', {}, null);
......
...@@ -202,6 +202,8 @@ Blockly.Generator.prototype.valueToCode = function(block, name, order) { ...@@ -202,6 +202,8 @@ Blockly.Generator.prototype.valueToCode = function(block, name, order) {
} }
// Value blocks must return code and order of operations info. // Value blocks must return code and order of operations info.
// Statement blocks must only return code. // Statement blocks must only return code.
goog.asserts.assertArray(tuple, 'Expecting tuple from value block "%s".',
targetBlock.type);
var code = tuple[0]; var code = tuple[0];
var innerOrder = tuple[1]; var innerOrder = tuple[1];
if (isNaN(innerOrder)) { if (isNaN(innerOrder)) {
...@@ -237,6 +239,7 @@ Blockly.Generator.prototype.statementToCode = function(block, name) { ...@@ -237,6 +239,7 @@ Blockly.Generator.prototype.statementToCode = function(block, name) {
var code = this.blockToCode(targetBlock); var code = this.blockToCode(targetBlock);
// Value blocks must return code and order of operations info. // Value blocks must return code and order of operations info.
// Statement blocks must only return code. // Statement blocks must only return code.
goog.asserts.assertString(code, 'Expecting code from statement block "%s".',
targetBlock && targetBlock.type); targetBlock && targetBlock.type);
if (code) { if (code) {
code = this.prefixLines(/** @type {string} */ (code), this.INDENT); code = this.prefixLines(/** @type {string} */ (code), this.INDENT);
......
...@@ -152,10 +152,12 @@ Blockly.Workspace.prototype.getWidth = function() { ...@@ -152,10 +152,12 @@ Blockly.Workspace.prototype.getWidth = function() {
* Obtain a newly created block. * Obtain a newly created block.
* @param {?string} prototypeName Name of the language object containing * @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block. * type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @return {!Blockly.Block} The created block. * @return {!Blockly.Block} The created block.
*/ */
Blockly.Workspace.prototype.newBlock = function(prototypeName) { Blockly.Workspace.prototype.newBlock = function(prototypeName, opt_id) {
return new Blockly.Block(this, prototypeName); return new Blockly.Block(this, prototypeName, opt_id);
}; };
/** /**
......
...@@ -250,10 +250,12 @@ Blockly.WorkspaceSvg.prototype.dispose = function() { ...@@ -250,10 +250,12 @@ Blockly.WorkspaceSvg.prototype.dispose = function() {
* Obtain a newly created block. * Obtain a newly created block.
* @param {?string} prototypeName Name of the language object containing * @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block. * type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @return {!Blockly.BlockSvg} The created block. * @return {!Blockly.BlockSvg} The created block.
*/ */
Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName) { Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName, opt_id) {
return new Blockly.BlockSvg(this, prototypeName); return new Blockly.BlockSvg(this, prototypeName, opt_id);
}; };
/** /**
...@@ -445,7 +447,7 @@ Blockly.WorkspaceSvg.prototype.highlightBlock = function(id) { ...@@ -445,7 +447,7 @@ Blockly.WorkspaceSvg.prototype.highlightBlock = function(id) {
} }
var block = null; var block = null;
if (id) { if (id) {
block = this.getBlockById(id); block = Blockly.Block.getById(id);
if (!block) { if (!block) {
return; return;
} }
......
...@@ -295,14 +295,11 @@ Blockly.Xml.domToWorkspace = function(workspace, xml) { ...@@ -295,14 +295,11 @@ Blockly.Xml.domToWorkspace = function(workspace, xml) {
* workspace. * workspace.
* @param {!Blockly.Workspace} workspace The workspace. * @param {!Blockly.Workspace} workspace The workspace.
* @param {!Element} xmlBlock XML block element. * @param {!Element} xmlBlock XML block element.
* @param {boolean=} opt_reuseBlock Optional arg indicating whether to
* reinitialize an existing block.
* @return {!Blockly.Block} The root block created. * @return {!Blockly.Block} The root block created.
*/ */
Blockly.Xml.domToBlock = function(workspace, xmlBlock, opt_reuseBlock) { Blockly.Xml.domToBlock = function(workspace, xmlBlock) {
// Create top-level block. // Create top-level block.
var topBlock = Blockly.Xml.domToBlockHeadless_(workspace, xmlBlock, var topBlock = Blockly.Xml.domToBlockHeadless_(workspace, xmlBlock);
opt_reuseBlock);
if (workspace.rendered) { if (workspace.rendered) {
// Hide connections to speed up assembly. // Hide connections to speed up assembly.
topBlock.setConnectionsHidden(true); topBlock.setConnectionsHidden(true);
...@@ -334,37 +331,17 @@ Blockly.Xml.domToBlock = function(workspace, xmlBlock, opt_reuseBlock) { ...@@ -334,37 +331,17 @@ Blockly.Xml.domToBlock = function(workspace, xmlBlock, opt_reuseBlock) {
* workspace. * workspace.
* @param {!Blockly.Workspace} workspace The workspace. * @param {!Blockly.Workspace} workspace The workspace.
* @param {!Element} xmlBlock XML block element. * @param {!Element} xmlBlock XML block element.
* @param {boolean=} opt_reuseBlock Optional arg indicating whether to
* reinitialize an existing block.
* @return {!Blockly.Block} The root block created. * @return {!Blockly.Block} The root block created.
* @private * @private
*/ */
Blockly.Xml.domToBlockHeadless_ = Blockly.Xml.domToBlockHeadless_ = function(workspace, xmlBlock) {
function(workspace, xmlBlock, opt_reuseBlock) {
var block = null; var block = null;
var prototypeName = xmlBlock.getAttribute('type'); var prototypeName = xmlBlock.getAttribute('type');
if (!prototypeName) { if (!prototypeName) {
throw 'Block type unspecified: \n' + xmlBlock.outerHTML; throw 'Block type unspecified: \n' + xmlBlock.outerHTML;
} }
var id = xmlBlock.getAttribute('id'); var id = xmlBlock.getAttribute('id');
if (opt_reuseBlock && id) { block = workspace.newBlock(prototypeName, id);
// Only used by realtime.
block = Blockly.Block.getById(id, workspace);
// TODO: The following is for debugging. It should never actually happen.
if (!block) {
throw 'Couldn\'t get Block with id: ' + id;
}
var parentBlock = block.getParent();
// If we've already filled this block then we will dispose of it and then
// re-fill it.
if (block.workspace) {
block.dispose(true, false, true);
}
block.fill(workspace, prototypeName);
block.parent_ = parentBlock;
} else {
block = workspace.newBlock(prototypeName);
}
var blockChild = null; var blockChild = null;
for (var i = 0, xmlChild; xmlChild = xmlBlock.childNodes[i]; i++) { for (var i = 0, xmlChild; xmlChild = xmlBlock.childNodes[i]; i++) {
...@@ -453,7 +430,7 @@ Blockly.Xml.domToBlockHeadless_ = ...@@ -453,7 +430,7 @@ Blockly.Xml.domToBlockHeadless_ =
} }
if (childBlockNode) { if (childBlockNode) {
blockChild = Blockly.Xml.domToBlockHeadless_(workspace, blockChild = Blockly.Xml.domToBlockHeadless_(workspace,
childBlockNode, opt_reuseBlock); childBlockNode);
if (blockChild.outputConnection) { if (blockChild.outputConnection) {
input.connection.connect(blockChild.outputConnection); input.connection.connect(blockChild.outputConnection);
} else if (blockChild.previousConnection) { } else if (blockChild.previousConnection) {
...@@ -475,7 +452,7 @@ Blockly.Xml.domToBlockHeadless_ = ...@@ -475,7 +452,7 @@ Blockly.Xml.domToBlockHeadless_ =
throw 'Next statement is already connected.'; throw 'Next statement is already connected.';
} }
blockChild = Blockly.Xml.domToBlockHeadless_(workspace, blockChild = Blockly.Xml.domToBlockHeadless_(workspace,
childBlockNode, opt_reuseBlock); childBlockNode);
if (!blockChild.previousConnection) { if (!blockChild.previousConnection) {
throw 'Next block does not have previous statement.'; throw 'Next block does not have previous statement.';
} }
......
/**
* @license
* Blockly Tests
*
* Copyright 2015 Google Inc.
* https://developers.google.com/blockly/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
function test_getById() {
var workspace = new Blockly.Workspace();
var blockA = workspace.newBlock('');
var blockB = workspace.newBlock('');
assertEquals('Find blockA.', blockA, Blockly.Block.getById(blockA.id));
assertEquals('Find blockB.', blockB, Blockly.Block.getById(blockB.id));
assertEquals('No block found.', null,
Blockly.Block.getById('I do not exist.'));
blockA.dispose();
assertEquals('Can\'t find blockA.', null, Blockly.Block.getById(blockA.id));
assertEquals('BlockB exists.', blockB, Blockly.Block.getById(blockB.id));
workspace.clear();
assertEquals('Can\'t find blockB.', null, Blockly.Block.getById(blockB.id));
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
</head> </head>
<body> <body>
<script src="blockly_test.js"></script> <script src="blockly_test.js"></script>
<script src="block_test.js"></script>
<script src="connection_test.js"></script> <script src="connection_test.js"></script>
<script src="generator_test.js"></script> <script src="generator_test.js"></script>
<script src="names_test.js"></script> <script src="names_test.js"></script>
......
...@@ -66,17 +66,3 @@ function test_maxBlocksWorkspace() { ...@@ -66,17 +66,3 @@ function test_maxBlocksWorkspace() {
workspace.clear(); workspace.clear();
assertEquals('Cleared capacity.', 0, workspace.remainingCapacity()); assertEquals('Cleared capacity.', 0, workspace.remainingCapacity());
} }
function test_getByIdWorkspace() {
var workspace = new Blockly.Workspace();
var blockA = workspace.newBlock('');
var blockB = workspace.newBlock('');
assertEquals('Find blockA.', blockA, workspace.getBlockById(blockA.id));
assertEquals('Find blockB.', blockB, workspace.getBlockById(blockB.id));
assertEquals('No block found.', null, workspace.getBlockById('I do not exist.'));
blockA.dispose();
assertEquals('Can\'t find blockA.', null, workspace.getBlockById(blockA.id));
assertEquals('BlockB exists.', blockB, workspace.getBlockById(blockB.id));
workspace.clear();
assertEquals('Can\'t find blockB.', null, workspace.getBlockById(blockB.id));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment