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

Create block id database.

parent b2bbde04
......@@ -46,11 +46,16 @@ goog.require('goog.string');
* @param {!Blockly.Workspace} workspace The block's workspace.
* @param {?string} prototypeName Name of the language object containing
* type-specific functions for this block.
* @param {=string} opt_id Optional ID. Use this ID if provided, otherwise
* create a new id.
* @constructor
*/
Blockly.Block = function(workspace, prototypeName) {
Blockly.Block = function(workspace, prototypeName, opt_id) {
/** @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} */
this.outputConnection = null;
/** @type {Blockly.Connection} */
......@@ -140,20 +145,6 @@ Blockly.Block.obtain = function(workspace, prototypeName) {
*/
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.
* @param {boolean} healStack If true, then try to heal any gap by connecting
......@@ -201,10 +192,8 @@ Blockly.Block.prototype.dispose = function(healStack, animate,
}
connections[i].dispose();
}
// Remove from Realtime set of blocks.
if (Blockly.Realtime.isEnabled() && !Blockly.Realtime.withinSync) {
Blockly.Realtime.removeBlock(this);
}
// Remove from block database.
delete Blockly.Block.BlockDB_[this.id];
};
/**
......@@ -1240,3 +1229,19 @@ Blockly.Block.prototype.getRelativeToSurfaceXY = function() {
Blockly.Block.prototype.moveBy = function(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');
* @param {!Blockly.Workspace} workspace The block's workspace.
* @param {?string} prototypeName Name of the language object containing
* 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}
* @constructor
*/
Blockly.BlockSvg = function(workspace, prototypeName) {
Blockly.BlockSvg.superClass_.constructor.call(this, workspace, prototypeName);
Blockly.BlockSvg = function(workspace, prototypeName, opt_id) {
Blockly.BlockSvg.superClass_.constructor.call(this,
workspace, prototypeName, opt_id);
// Create core elements for the block.
/** @type {SVGElement} */
this.svgGroup_ = Blockly.createSvgElement('g', {}, null);
......
......@@ -202,6 +202,8 @@ Blockly.Generator.prototype.valueToCode = function(block, name, order) {
}
// Value blocks must return code and order of operations info.
// Statement blocks must only return code.
goog.asserts.assertArray(tuple, 'Expecting tuple from value block "%s".',
targetBlock.type);
var code = tuple[0];
var innerOrder = tuple[1];
if (isNaN(innerOrder)) {
......@@ -237,6 +239,7 @@ Blockly.Generator.prototype.statementToCode = function(block, name) {
var code = this.blockToCode(targetBlock);
// Value blocks must return code and order of operations info.
// Statement blocks must only return code.
goog.asserts.assertString(code, 'Expecting code from statement block "%s".',
targetBlock && targetBlock.type);
if (code) {
code = this.prefixLines(/** @type {string} */ (code), this.INDENT);
......
......@@ -152,10 +152,12 @@ Blockly.Workspace.prototype.getWidth = function() {
* Obtain a newly created block.
* @param {?string} prototypeName Name of the language object containing
* 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.
*/
Blockly.Workspace.prototype.newBlock = function(prototypeName) {
return new Blockly.Block(this, prototypeName);
Blockly.Workspace.prototype.newBlock = function(prototypeName, opt_id) {
return new Blockly.Block(this, prototypeName, opt_id);
};
/**
......
......@@ -250,10 +250,12 @@ Blockly.WorkspaceSvg.prototype.dispose = function() {
* Obtain a newly created block.
* @param {?string} prototypeName Name of the language object containing
* 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.
*/
Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName) {
return new Blockly.BlockSvg(this, prototypeName);
Blockly.WorkspaceSvg.prototype.newBlock = function(prototypeName, opt_id) {
return new Blockly.BlockSvg(this, prototypeName, opt_id);
};
/**
......@@ -445,7 +447,7 @@ Blockly.WorkspaceSvg.prototype.highlightBlock = function(id) {
}
var block = null;
if (id) {
block = this.getBlockById(id);
block = Blockly.Block.getById(id);
if (!block) {
return;
}
......
......@@ -295,14 +295,11 @@ Blockly.Xml.domToWorkspace = function(workspace, xml) {
* workspace.
* @param {!Blockly.Workspace} workspace The workspace.
* @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.
*/
Blockly.Xml.domToBlock = function(workspace, xmlBlock, opt_reuseBlock) {
Blockly.Xml.domToBlock = function(workspace, xmlBlock) {
// Create top-level block.
var topBlock = Blockly.Xml.domToBlockHeadless_(workspace, xmlBlock,
opt_reuseBlock);
var topBlock = Blockly.Xml.domToBlockHeadless_(workspace, xmlBlock);
if (workspace.rendered) {
// Hide connections to speed up assembly.
topBlock.setConnectionsHidden(true);
......@@ -334,37 +331,17 @@ Blockly.Xml.domToBlock = function(workspace, xmlBlock, opt_reuseBlock) {
* workspace.
* @param {!Blockly.Workspace} workspace The workspace.
* @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.
* @private
*/
Blockly.Xml.domToBlockHeadless_ =
function(workspace, xmlBlock, opt_reuseBlock) {
Blockly.Xml.domToBlockHeadless_ = function(workspace, xmlBlock) {
var block = null;
var prototypeName = xmlBlock.getAttribute('type');
if (!prototypeName) {
throw 'Block type unspecified: \n' + xmlBlock.outerHTML;
}
var id = xmlBlock.getAttribute('id');
if (opt_reuseBlock && 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);
}
block = workspace.newBlock(prototypeName, id);
var blockChild = null;
for (var i = 0, xmlChild; xmlChild = xmlBlock.childNodes[i]; i++) {
......@@ -453,7 +430,7 @@ Blockly.Xml.domToBlockHeadless_ =
}
if (childBlockNode) {
blockChild = Blockly.Xml.domToBlockHeadless_(workspace,
childBlockNode, opt_reuseBlock);
childBlockNode);
if (blockChild.outputConnection) {
input.connection.connect(blockChild.outputConnection);
} else if (blockChild.previousConnection) {
......@@ -475,7 +452,7 @@ Blockly.Xml.domToBlockHeadless_ =
throw 'Next statement is already connected.';
}
blockChild = Blockly.Xml.domToBlockHeadless_(workspace,
childBlockNode, opt_reuseBlock);
childBlockNode);
if (!blockChild.previousConnection) {
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 @@
</head>
<body>
<script src="blockly_test.js"></script>
<script src="block_test.js"></script>
<script src="connection_test.js"></script>
<script src="generator_test.js"></script>
<script src="names_test.js"></script>
......
......@@ -66,17 +66,3 @@ function test_maxBlocksWorkspace() {
workspace.clear();
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