diff --git a/core/block.js b/core/block.js index 8d3163b37..75f41f1c3 100644 --- a/core/block.js +++ b/core/block.js @@ -52,9 +52,8 @@ goog.require('goog.string'); */ Blockly.Block = function(workspace, prototypeName, opt_id) { /** @type {string} */ - this.id = opt_id || Blockly.genUid(); - goog.asserts.assert(!Blockly.Block.getById(this.id), - 'Error: Block "%s" already exists.', this.id); + this.id = (opt_id && !Blockly.Block.getById(opt_id)) ? + opt_id : Blockly.genUid(); Blockly.Block.BlockDB_[this.id] = this; /** @type {Blockly.Connection} */ this.outputConnection = null; @@ -158,16 +157,13 @@ Blockly.Block.prototype.colour_ = '#000000'; * the next statement with the previous statement. Otherwise, dispose of * all children of this block. * @param {boolean} animate If true, show a disposal animation and sound. - * @param {boolean=} opt_dontRemoveFromWorkspace If true, don't remove this - * block from the workspace's list of top blocks. */ -Blockly.Block.prototype.dispose = function(healStack, animate, - opt_dontRemoveFromWorkspace) { +Blockly.Block.prototype.dispose = function(healStack, animate) { this.unplug(healStack, false); // This block is now at the top of the workspace. // Remove this block from the workspace's list of top-most blocks. - if (this.workspace && !opt_dontRemoveFromWorkspace) { + if (this.workspace) { this.workspace.removeTopBlock(this); this.workspace = null; } @@ -333,18 +329,16 @@ Blockly.Block.prototype.getParent = function() { */ Blockly.Block.prototype.getSurroundParent = function() { var block = this; - while (true) { - do { - var prevBlock = block; - block = block.getParent(); - if (!block) { - // Ran off the top. - return null; - } - } while (block.getNextBlock() == prevBlock); - // This block is an enclosing parent, not just a statement in a stack. - return block; - } + do { + var prevBlock = block; + block = block.getParent(); + if (!block) { + // Ran off the top. + return null; + } + } while (block.getNextBlock() == prevBlock); + // This block is an enclosing parent, not just a statement in a stack. + return block; }; /** @@ -485,6 +479,9 @@ Blockly.Block.prototype.isShadow = function() { * @param {boolean} shadow True if a shadow. */ Blockly.Block.prototype.setShadow = function(shadow) { + if (this.isShadow_ == shadow) { + return; // No change. + } this.isShadow_ = shadow; }; @@ -814,7 +811,9 @@ Blockly.Block.prototype.isCollapsed = function() { * @param {boolean} collapsed True if collapsed. */ Blockly.Block.prototype.setCollapsed = function(collapsed) { - this.collapsed_ = collapsed; + if (this.collapsed_ != collapsed) { + this.collapsed_ = collapsed; + } }; /** @@ -1207,7 +1206,9 @@ Blockly.Block.prototype.getCommentText = function() { * @param {?string} text The text, or null to delete. */ Blockly.Block.prototype.setCommentText = function(text) { - this.comment = text; + if (this.comment != text) { + this.comment = text; + } }; /** diff --git a/core/blockly.js b/core/blockly.js index ca9aafeaf..9946d4006 100644 --- a/core/blockly.js +++ b/core/blockly.js @@ -434,7 +434,7 @@ Blockly.longStop_ = function() { * @private */ Blockly.copy_ = function(block) { - var xmlBlock = Blockly.Xml.blockToDom_(block); + var xmlBlock = Blockly.Xml.blockToDom(block); if (Blockly.dragMode_ != 2) { Blockly.Xml.deleteNext(xmlBlock); } diff --git a/core/comment.js b/core/comment.js index e308994b2..4210d1df6 100644 --- a/core/comment.js +++ b/core/comment.js @@ -176,7 +176,6 @@ Blockly.Comment.prototype.setVisible = function(visible) { this.width_, this.height_); this.bubble_.registerResizeEvent(this, this.resizeBubble_); this.updateColour(); - this.text_ = null; } else { // Dispose of the bubble. this.bubble_.dispose(); @@ -243,10 +242,11 @@ Blockly.Comment.prototype.getText = function() { * @param {string} text Comment text. */ Blockly.Comment.prototype.setText = function(text) { + if (this.text_ != text) { + this.text_ = text; + } if (this.textarea_) { this.textarea_.value = text; - } else { - this.text_ = text; } }; diff --git a/core/events.js b/core/events.js index 21e9813ff..984ee51c0 100644 --- a/core/events.js +++ b/core/events.js @@ -26,6 +26,7 @@ goog.provide('Blockly.Events'); + /** * Allow change events to be created and fired. * @type {boolean} diff --git a/core/field.js b/core/field.js index 9dfed2455..6891aa86d 100644 --- a/core/field.js +++ b/core/field.js @@ -19,7 +19,7 @@ */ /** - * @fileoverview Input field. Used for editable titles, variables, etc. + * @fileoverview Field. Used for editable titles, variables, etc. * This is an abstract class that defines the UI on the block. Actual * instances would be Blockly.FieldTextInput, Blockly.FieldDropdown, etc. * @author fraser@google.com (Neil Fraser) @@ -36,7 +36,7 @@ goog.require('goog.userAgent'); /** - * Class for an editable field. + * Abstract class for an editable field. * @param {string} text The initial content of the field. * @constructor */ @@ -59,11 +59,25 @@ Blockly.Field.cacheWidths_ = null; */ Blockly.Field.cacheReference_ = 0; + +/** + * Name of field. Unique within each block. + * Static labels are usually unnamed. + * @type {string=} + */ +Blockly.Field.prototype.name = undefined; + /** * Maximum characters of text to display before adding an ellipsis. */ Blockly.Field.prototype.maxDisplayLength = 50; +/** + * Visible text to display. + * @private + */ +Blockly.Field.prototype.text_ = ''; + /** * Block this field is attached to. Starts as null, then in set in init. * @private @@ -84,6 +98,7 @@ Blockly.Field.prototype.changeHandler_ = null; /** * Non-breaking space. + * @const */ Blockly.Field.NBSP = '\u00A0'; @@ -241,8 +256,6 @@ Blockly.Field.prototype.render_ = function() { /** * Start caching field widths. Every call to this function MUST also call * stopCache. Caches must not survive between execution threads. - * @type {Object} - * @private */ Blockly.Field.startCache = function() { Blockly.Field.cacheReference_++; @@ -254,8 +267,6 @@ Blockly.Field.startCache = function() { /** * Stop caching field widths. Unless caching was already on when the * corresponding call to startCache was made. - * @type {number} - * @private */ Blockly.Field.stopCache = function() { Blockly.Field.cacheReference_--; @@ -364,10 +375,18 @@ Blockly.Field.prototype.getValue = function() { /** * By default there is no difference between the human-readable text and * the language-neutral values. Subclasses (such as dropdown) may define this. - * @param {string} text New text. + * @param {string} newText New text. */ -Blockly.Field.prototype.setValue = function(text) { - this.setText(text); +Blockly.Field.prototype.setValue = function(newText) { + if (newText === null) { + // No change if null. + return; + } + var oldText = this.getValue(); + if (oldText == newText) { + return; + } + this.setText(newText); }; /** diff --git a/core/field_colour.js b/core/field_colour.js index 69fdb93d4..fe3b1f0e3 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -118,6 +118,7 @@ Blockly.FieldColour.prototype.setValue = function(colour) { */ Blockly.FieldColour.prototype.getText = function() { var colour = this.colour_; + // Try to use #rgb format if possible, rather than #rrggbb. var m = colour.match(/^#(.)\1(.)\2(.)\3$/); if (m) { colour = '#' + m[1] + m[2] + m[3]; diff --git a/core/field_dropdown.js b/core/field_dropdown.js index b773d12f7..fc8e4647c 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -265,6 +265,9 @@ Blockly.FieldDropdown.prototype.getValue = function() { * @param {string} newValue New value to set. */ Blockly.FieldDropdown.prototype.setValue = function(newValue) { + if (newValue === null || newValue === this.value_) { + return; // No change if null. + } this.value_ = newValue; // Look up and display the human-readable text. var options = this.getOptions_(); diff --git a/core/field_textinput.js b/core/field_textinput.js index 4cac181b2..fd606609c 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -80,8 +80,7 @@ Blockly.FieldTextInput.prototype.dispose = function() { */ Blockly.FieldTextInput.prototype.setText = function(text) { if (text === null) { - // No change if null. - return; + return; // No change if null. } if (this.sourceBlock_ && this.changeHandler_) { var validated = this.changeHandler_(text); @@ -189,20 +188,17 @@ Blockly.FieldTextInput.prototype.onHtmlInputKeyDown_ = function(e) { */ Blockly.FieldTextInput.prototype.onHtmlInputChange_ = function(e) { var htmlInput = Blockly.FieldTextInput.htmlInput_; - var escKey = 27; - if (e.keyCode != escKey) { - // Update source block. - var text = htmlInput.value; - if (text !== htmlInput.oldValue_) { - this.sourceBlock_.setShadow(false); - htmlInput.oldValue_ = text; - this.setText(text); - this.validate_(); - } else if (goog.userAgent.WEBKIT) { - // Cursor key. Render the source block to show the caret moving. - // Chrome only (version 26, OS X). - this.sourceBlock_.render(); - } + // Update source block. + var text = htmlInput.value; + if (text !== htmlInput.oldValue_) { + this.sourceBlock_.setShadow(false); + htmlInput.oldValue_ = text; + this.setValue(text); + this.validate_(); + } else if (goog.userAgent.WEBKIT) { + // Cursor key. Render the source block to show the caret moving. + // Chrome only (version 26, OS X). + this.sourceBlock_.render(); } }; diff --git a/core/field_variable.js b/core/field_variable.js index a03cfe6ec..4ab1d2895 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -90,11 +90,8 @@ Blockly.FieldVariable.prototype.init = function(block) { if (!this.getValue()) { // Variables without names get uniquely named for this workspace. - if (block.isInFlyout) { - var workspace = block.workspace.targetWorkspace; - } else { - var workspace = block.workspace; - } + var workspace = + block.isInFlyout ? block.workspace.targetWorkspace : block.workspace; this.setValue(Blockly.Variables.generateUniqueName(workspace)); } Blockly.FieldVariable.superClass_.init.call(this, block); @@ -111,11 +108,10 @@ Blockly.FieldVariable.prototype.getValue = function() { /** * Set the variable name. - * @param {string} text New text. + * @param {string} newValue New text. */ -Blockly.FieldVariable.prototype.setValue = function(text) { - this.value_ = text; - this.setText(text); + this.value_ = newValue; + this.setText(newValue); }; /** diff --git a/core/flyout.js b/core/flyout.js index a3b81de1c..75b72687a 100644 --- a/core/flyout.js +++ b/core/flyout.js @@ -622,7 +622,7 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) { return; } // Create the new block by cloning the block in the flyout (via XML). - var xml = Blockly.Xml.blockToDom_(originBlock); + var xml = Blockly.Xml.blockToDom(originBlock); var block = Blockly.Xml.domToBlock(workspace, xml); // Place it in the same spot as the flyout copy. var svgRootOld = originBlock.getSvgRoot(); diff --git a/core/xml.js b/core/xml.js index fc696d725..efa24997d 100644 --- a/core/xml.js +++ b/core/xml.js @@ -44,7 +44,7 @@ Blockly.Xml.workspaceToDom = function(workspace) { var xml = goog.dom.createDom('xml'); var blocks = workspace.getTopBlocks(true); for (var i = 0, block; block = blocks[i]; i++) { - var element = Blockly.Xml.blockToDom_(block); + var element = Blockly.Xml.blockToDom(block); var xy = block.getRelativeToSurfaceXY(); element.setAttribute('x', Math.round(workspace.RTL ? width - xy.x : xy.x)); element.setAttribute('y', Math.round(xy.y)); @@ -57,15 +57,11 @@ Blockly.Xml.workspaceToDom = function(workspace) { * Encode a block subtree as XML. * @param {!Blockly.Block} block The root block to encode. * @return {!Element} Tree of XML elements. - * @private */ -Blockly.Xml.blockToDom_ = function(block) { +Blockly.Xml.blockToDom = function(block) { var element = goog.dom.createDom(block.isShadow() ? 'shadow' : 'block'); element.setAttribute('type', block.type); - if (false) { - // Only used by realtime. - element.setAttribute('id', block.id); - } + element.setAttribute('id', block.id); if (block.mutationToDom) { // Custom data for an advanced block. var mutation = block.mutationToDom(); @@ -120,7 +116,7 @@ Blockly.Xml.blockToDom_ = function(block) { container.appendChild(Blockly.Xml.cloneShadow_(shadow)); } if (childBlock) { - container.appendChild(Blockly.Xml.blockToDom_(childBlock)); + container.appendChild(Blockly.Xml.blockToDom(childBlock)); empty = false; } } @@ -151,7 +147,7 @@ Blockly.Xml.blockToDom_ = function(block) { var nextBlock = block.getNextBlock(); if (nextBlock) { var container = goog.dom.createDom('next', null, - Blockly.Xml.blockToDom_(nextBlock)); + Blockly.Xml.blockToDom(nextBlock)); element.appendChild(container); } var shadow = block.nextConnection && block.nextConnection.getShadowDom();