diff --git a/core/block.js b/core/block.js index eeb4a0c48..43fe84f8d 100644 --- a/core/block.js +++ b/core/block.js @@ -39,6 +39,7 @@ goog.require('Blockly.Input'); goog.require('Blockly.Mutator'); goog.require('Blockly.utils'); goog.require('Blockly.utils.Coordinate'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.string'); goog.require('Blockly.Warning'); goog.require('Blockly.Workspace'); @@ -1636,18 +1637,13 @@ Blockly.Block.prototype.interpolate_ = function(message, args, lastDummyAlign) { default: // This should handle all field JSON parsing, including // options that can be applied to any field type. - field = Blockly.Field.fromJson(element); + field = Blockly.utils.fields.fromJson(element); // Unknown field. if (!field) { if (element['alt']) { element = element['alt']; altRepeat = true; - } else { - console.warn('Blockly could not create a field of type ' + - element['type'] + - '. You may need to register your custom field. See ' + - 'github.com/google/blockly/issues/1584'); } } } diff --git a/core/block_dragger.js b/core/block_dragger.js index fa6bc723d..7e64b8e8e 100644 --- a/core/block_dragger.js +++ b/core/block_dragger.js @@ -31,6 +31,7 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.BlockMove'); goog.require('Blockly.InsertionMarkerManager'); goog.require('Blockly.utils.Coordinate'); +goog.require('Blockly.utils.dom'); /** @@ -164,7 +165,7 @@ Blockly.BlockDragger.prototype.startBlockDrag = function(currentDragDeltaXY, // During a drag there may be a lot of rerenders, but not field changes. // Turn the cache on so we don't do spurious remeasures during the drag. - Blockly.Field.startCache(); + Blockly.utils.dom.startTextWidthCache(); this.workspace_.setResizesEnabled(false); Blockly.blockAnimations.disconnectUiStop(); @@ -225,7 +226,7 @@ Blockly.BlockDragger.prototype.endBlockDrag = function(e, currentDragDeltaXY) { this.dragBlock(e, currentDragDeltaXY); this.dragIconData_ = []; - Blockly.Field.stopCache(); + Blockly.utils.dom.stopTextWidthCache(); Blockly.blockAnimations.disconnectUiStop(); diff --git a/core/block_svg.js b/core/block_svg.js index 9db42e1b6..4c22e0fbe 100644 --- a/core/block_svg.js +++ b/core/block_svg.js @@ -270,9 +270,9 @@ Blockly.BlockSvg.prototype.setParent = function(newParent) { return; } - Blockly.Field.startCache(); + Blockly.utils.dom.startTextWidthCache(); Blockly.BlockSvg.superClass_.setParent.call(this, newParent); - Blockly.Field.stopCache(); + Blockly.utils.dom.stopTextWidthCache(); var svgRoot = this.getSvgRoot(); @@ -893,7 +893,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { return; } Blockly.Tooltip.hide(); - Blockly.Field.startCache(); + Blockly.utils.dom.startTextWidthCache(); // Save the block's workspace temporarily so we can resize the // contents once the block is disposed. var blockWorkspace = this.workspace; @@ -952,7 +952,7 @@ Blockly.BlockSvg.prototype.dispose = function(healStack, animate) { this.svgPath_ = null; this.svgPathLight_ = null; this.svgPathDark_ = null; - Blockly.Field.stopCache(); + Blockly.utils.dom.stopTextWidthCache(); }; /** @@ -1539,7 +1539,7 @@ Blockly.BlockSvg.prototype.positionNearConnection = function(sourceConnection, * If true, also render block's parent, grandparent, etc. Defaults to true. */ Blockly.BlockSvg.prototype.render = function(opt_bubble) { - Blockly.Field.startCache(); + Blockly.utils.dom.startTextWidthCache(); this.rendered = true; // TODO (#2702): Choose an API for picking the renderer. Blockly.blockRendering.render(this); @@ -1555,7 +1555,7 @@ Blockly.BlockSvg.prototype.render = function(opt_bubble) { this.workspace.resizeContents(); } } - Blockly.Field.stopCache(); + Blockly.utils.dom.stopTextWidthCache(); }; /** diff --git a/core/field.js b/core/field.js index ff34e0baa..54554129a 100644 --- a/core/field.js +++ b/core/field.js @@ -57,71 +57,6 @@ Blockly.Field = function(value, opt_validator) { opt_validator && this.setValidator(opt_validator); }; -/** - * The set of all registered fields, keyed by field type as used in the JSON - * definition of a block. - * @type {!Object} - * @private - */ -Blockly.Field.TYPE_MAP_ = {}; - -/** - * Registers a field type. May also override an existing field type. - * Blockly.Field.fromJson uses this registry to find the appropriate field. - * @param {string} type The field type name as used in the JSON definition. - * @param {!{fromJson: Function}} fieldClass The field class containing a - * fromJson function that can construct an instance of the field. - * @throws {Error} if the type name is empty, or the fieldClass is not an - * object containing a fromJson function. - */ -Blockly.Field.register = function(type, fieldClass) { - if ((typeof type != 'string') || (type.trim() == '')) { - throw Error('Invalid field type "' + type + '"'); - } - if (!fieldClass || (typeof fieldClass.fromJson != 'function')) { - throw Error('Field "' + fieldClass + '" must have a fromJson function'); - } - Blockly.Field.TYPE_MAP_[type] = fieldClass; -}; - -/** - * Construct a Field from a JSON arg object. - * Finds the appropriate registered field by the type name as registered using - * Blockly.Field.register. - * @param {!Object} options A JSON object with a type and options specific - * to the field type. - * @return {Blockly.Field} The new field instance or null if a field wasn't - * found with the given type name - * @package - */ -Blockly.Field.fromJson = function(options) { - var fieldClass = Blockly.Field.TYPE_MAP_[options['type']]; - if (fieldClass) { - var field = fieldClass.fromJson(options); - if (options['tooltip'] !== undefined) { - var rawValue = options['tooltip']; - var localizedText = Blockly.utils.replaceMessageReferences(rawValue); - field.setTooltip(localizedText); - } - return field; - } - return null; -}; - -/** - * Temporary cache of text widths. - * @type {Object} - * @private - */ -Blockly.Field.cacheWidths_ = null; - -/** - * Number of current references to cache. - * @type {number} - * @private - */ -Blockly.Field.cacheReference_ = 0; - /** * The default height of the border rect on any field. * @type {number} @@ -588,14 +523,14 @@ Blockly.Field.prototype.render_ = function() { /** * Updates the width of the field. Redirects to updateSize_(). * @deprecated May 2019 Use Blockly.Field.updateSize_() to force an update - * to the size of the field, or Blockly.Field.getCachedWidth() to check the - * size of the field.. + * to the size of the field, or Blockly.utils.dom.getTextWidth() to + * check the size of the field. */ Blockly.Field.prototype.updateWidth = function() { console.warn('Deprecated call to updateWidth, call' + ' Blockly.Field.updateSize_ to force an update to the size of the' + - ' field, or Blockly.Field.getCachedWidth() to check the size of the' + - ' field.'); + ' field, or Blockly.utils.dom.getTextWidth() to check the size' + + ' of the field.'); this.updateSize_(); }; @@ -604,7 +539,7 @@ Blockly.Field.prototype.updateWidth = function() { * @protected */ Blockly.Field.prototype.updateSize_ = function() { - var textWidth = Blockly.Field.getCachedWidth(this.textElement_); + var textWidth = Blockly.utils.dom.getTextWidth(this.textElement_); var totalWidth = textWidth; if (this.borderRect_) { totalWidth += Blockly.Field.X_PADDING; @@ -613,67 +548,6 @@ Blockly.Field.prototype.updateSize_ = function() { this.size_.width = totalWidth; }; -/** - * Gets the width of a text element, caching it in the process. - * @param {!Element} textElement An SVG 'text' element. - * @return {number} Width of element. - */ -Blockly.Field.getCachedWidth = function(textElement) { - var key = textElement.textContent + '\n' + textElement.className.baseVal; - var width; - - // Return the cached width if it exists. - if (Blockly.Field.cacheWidths_) { - width = Blockly.Field.cacheWidths_[key]; - if (width) { - return width; - } - } - - // Attempt to compute fetch the width of the SVG text element. - try { - if (Blockly.utils.userAgent.IE || Blockly.utils.userAgent.EDGE) { - width = textElement.getBBox().width; - } else { - width = textElement.getComputedTextLength(); - } - } catch (e) { - // In other cases where we fail to geth the computed text. Instead, use an - // approximation and do not cache the result. At some later point in time - // when the block is inserted into the visible DOM, this method will be - // called again and, at that point in time, will not throw an exception. - return textElement.textContent.length * 8; - } - - // Cache the computed width and return. - if (Blockly.Field.cacheWidths_) { - Blockly.Field.cacheWidths_[key] = width; - } - return width; -}; - -/** - * Start caching field widths. Every call to this function MUST also call - * stopCache. Caches must not survive between execution threads. - */ -Blockly.Field.startCache = function() { - Blockly.Field.cacheReference_++; - if (!Blockly.Field.cacheWidths_) { - Blockly.Field.cacheWidths_ = {}; - } -}; - -/** - * Stop caching field widths. Unless caching was already on when the - * corresponding call to startCache was made. - */ -Blockly.Field.stopCache = function() { - Blockly.Field.cacheReference_--; - if (!Blockly.Field.cacheReference_) { - Blockly.Field.cacheWidths_ = null; - } -}; - /** * Returns the height and width of the field. * diff --git a/core/field_angle.js b/core/field_angle.js index f4871ba7b..aeb91e536 100644 --- a/core/field_angle.js +++ b/core/field_angle.js @@ -29,6 +29,7 @@ goog.provide('Blockly.FieldAngle'); goog.require('Blockly.DropDownDiv'); goog.require('Blockly.FieldTextInput'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.math'); goog.require('Blockly.utils.userAgent'); @@ -353,4 +354,4 @@ Blockly.FieldAngle.prototype.doClassValidation_ = function(opt_newValue) { return n; }; -Blockly.Field.register('field_angle', Blockly.FieldAngle); +Blockly.utils.fields.register('field_angle', Blockly.FieldAngle); diff --git a/core/field_checkbox.js b/core/field_checkbox.js index f75f0a800..b8f9f1d82 100644 --- a/core/field_checkbox.js +++ b/core/field_checkbox.js @@ -30,6 +30,7 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Field'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); @@ -210,4 +211,4 @@ Blockly.FieldCheckbox.prototype.convertValueToBool_ = function(value) { } }; -Blockly.Field.register('field_checkbox', Blockly.FieldCheckbox); +Blockly.utils.fields.register('field_checkbox', Blockly.FieldCheckbox); diff --git a/core/field_colour.js b/core/field_colour.js index 2b5c529d2..13eff115e 100644 --- a/core/field_colour.js +++ b/core/field_colour.js @@ -31,6 +31,7 @@ goog.require('Blockly.Events'); goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Field'); goog.require('Blockly.utils.colour'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); @@ -346,4 +347,4 @@ Blockly.FieldColour.prototype.dropdownDispose_ = function() { Blockly.unbindEvent_(this.onUpWrapper_); }; -Blockly.Field.register('field_colour', Blockly.FieldColour); +Blockly.utils.fields.register('field_colour', Blockly.FieldColour); diff --git a/core/field_date.js b/core/field_date.js index d742b30f4..9b2ab30f4 100644 --- a/core/field_date.js +++ b/core/field_date.js @@ -29,6 +29,7 @@ goog.provide('Blockly.FieldDate'); goog.require('Blockly.Events'); goog.require('Blockly.Field'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.string'); goog.require('goog.date'); @@ -323,4 +324,4 @@ Blockly.FieldDate.CSS = [ '}' ]; -Blockly.Field.register('field_date', Blockly.FieldDate); +Blockly.utils.fields.register('field_date', Blockly.FieldDate); diff --git a/core/field_dropdown.js b/core/field_dropdown.js index 210e424ca..9d481bde4 100644 --- a/core/field_dropdown.js +++ b/core/field_dropdown.js @@ -33,6 +33,7 @@ goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.Field'); goog.require('Blockly.utils'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); goog.require('Blockly.utils.string'); goog.require('Blockly.utils.uiMenu'); @@ -492,7 +493,7 @@ Blockly.FieldDropdown.prototype.renderSelectedImage_ = function() { this.imageElement_.setAttribute('height', this.imageJson_.height); this.imageElement_.setAttribute('width', this.imageJson_.width); - var arrowWidth = Blockly.Field.getCachedWidth(this.arrow_); + var arrowWidth = Blockly.utils.dom.getTextWidth(this.arrow_); var imageHeight = Number(this.imageJson_.height); var imageWidth = Number(this.imageJson_.width); @@ -524,8 +525,8 @@ Blockly.FieldDropdown.prototype.renderSelectedText_ = function() { this.textElement_.setAttribute('x', Blockly.Field.DEFAULT_TEXT_OFFSET); // Height and width include the border rect. this.size_.height = Blockly.Field.BORDER_RECT_DEFAULT_HEIGHT; - this.size_.width = - Blockly.Field.getCachedWidth(this.textElement_) + Blockly.Field.X_PADDING; + this.size_.width = Blockly.utils.dom.getTextWidth(this.textElement_) + + Blockly.Field.X_PADDING; }; /** @@ -565,4 +566,4 @@ Blockly.FieldDropdown.validateOptions_ = function(options) { } }; -Blockly.Field.register('field_dropdown', Blockly.FieldDropdown); +Blockly.utils.fields.register('field_dropdown', Blockly.FieldDropdown); diff --git a/core/field_image.js b/core/field_image.js index b0a13ec0c..37dbf5765 100644 --- a/core/field_image.js +++ b/core/field_image.js @@ -30,6 +30,7 @@ goog.require('Blockly.Field'); goog.require('Blockly.Tooltip'); goog.require('Blockly.utils'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); @@ -222,4 +223,4 @@ Blockly.FieldImage.prototype.setOnClickHandler = function(func) { this.clickHandler_ = func; }; -Blockly.Field.register('field_image', Blockly.FieldImage); +Blockly.utils.fields.register('field_image', Blockly.FieldImage); diff --git a/core/field_label.js b/core/field_label.js index abfe0b438..8134feb09 100644 --- a/core/field_label.js +++ b/core/field_label.js @@ -31,6 +31,7 @@ goog.require('Blockly.Field'); goog.require('Blockly.Tooltip'); goog.require('Blockly.utils'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); @@ -100,4 +101,4 @@ Blockly.FieldLabel.prototype.doClassValidation_ = function(opt_newValue) { return String(opt_newValue); }; -Blockly.Field.register('field_label', Blockly.FieldLabel); +Blockly.utils.fields.register('field_label', Blockly.FieldLabel); diff --git a/core/field_label_serializable.js b/core/field_label_serializable.js index 833a4c61d..38f8ddb05 100644 --- a/core/field_label_serializable.js +++ b/core/field_label_serializable.js @@ -29,6 +29,7 @@ goog.provide('Blockly.FieldLabelSerializable'); goog.require('Blockly.FieldLabel'); goog.require('Blockly.utils'); +goog.require('Blockly.utils.fields'); /** @@ -75,5 +76,5 @@ Blockly.FieldLabelSerializable.prototype.EDITABLE = false; */ Blockly.FieldLabelSerializable.prototype.SERIALIZABLE = true; -Blockly.Field.register( +Blockly.utils.fields.register( 'field_label_serializable', Blockly.FieldLabelSerializable); diff --git a/core/field_number.js b/core/field_number.js index d430145c8..347ae60d7 100644 --- a/core/field_number.js +++ b/core/field_number.js @@ -27,6 +27,7 @@ goog.provide('Blockly.FieldNumber'); goog.require('Blockly.FieldTextInput'); +goog.require('Blockly.utils.fields'); /** @@ -138,4 +139,4 @@ Blockly.FieldNumber.prototype.doClassValidation_ = function(opt_newValue) { return n; }; -Blockly.Field.register('field_number', Blockly.FieldNumber); +Blockly.utils.fields.register('field_number', Blockly.FieldNumber); diff --git a/core/field_textinput.js b/core/field_textinput.js index 92dcc31a0..f9fa93f25 100644 --- a/core/field_textinput.js +++ b/core/field_textinput.js @@ -34,6 +34,7 @@ goog.require('Blockly.utils'); goog.require('Blockly.utils.aria'); goog.require('Blockly.utils.Coordinate'); goog.require('Blockly.utils.dom'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); goog.require('Blockly.utils.userAgent'); @@ -448,4 +449,4 @@ Blockly.FieldTextInput.nonnegativeIntegerValidator = function(text) { return n; }; -Blockly.Field.register('field_input', Blockly.FieldTextInput); +Blockly.utils.fields.register('field_input', Blockly.FieldTextInput); diff --git a/core/field_variable.js b/core/field_variable.js index 209d9be57..c4af8fb0d 100644 --- a/core/field_variable.js +++ b/core/field_variable.js @@ -31,6 +31,7 @@ goog.require('Blockly.Events.BlockChange'); goog.require('Blockly.FieldDropdown'); goog.require('Blockly.Msg'); goog.require('Blockly.utils'); +goog.require('Blockly.utils.fields'); goog.require('Blockly.utils.Size'); goog.require('Blockly.VariableModel'); goog.require('Blockly.Variables'); @@ -430,4 +431,4 @@ Blockly.FieldVariable.prototype.referencesVariables = function() { return true; }; -Blockly.Field.register('field_variable', Blockly.FieldVariable); +Blockly.utils.fields.register('field_variable', Blockly.FieldVariable); diff --git a/core/flyout_button.js b/core/flyout_button.js index bf4ae27ec..973e3a320 100644 --- a/core/flyout_button.js +++ b/core/flyout_button.js @@ -156,7 +156,7 @@ Blockly.FlyoutButton.prototype.createDom = function() { this.svgGroup_); svgText.textContent = Blockly.utils.replaceMessageReferences(this.text_); - this.width = Blockly.Field.getCachedWidth(svgText); + this.width = Blockly.utils.dom.getTextWidth(svgText); this.height = 20; // Can't compute it :( if (!this.isLabel_) { diff --git a/core/utils/dom.js b/core/utils/dom.js index 9897fb5da..694cdd9a2 100644 --- a/core/utils/dom.js +++ b/core/utils/dom.js @@ -63,6 +63,20 @@ Blockly.utils.dom.Node = { DOCUMENT_POSITION_CONTAINED_BY: 16 }; +/** + * Temporary cache of text widths. + * @type {Object} + * @private + */ +Blockly.utils.dom.cacheWidths_ = null; + +/** + * Number of current references to cache. + * @type {number} + * @private + */ +Blockly.utils.dom.cacheReference_ = 0; + /** * Helper method for creating SVG elements. * @param {string} name Element's tag name. @@ -197,3 +211,64 @@ Blockly.utils.dom.setCssTransform = function(element, transform) { element.style['transform'] = transform; element.style['-webkit-transform'] = transform; }; + +/** + * Start caching text widths. Every call to this function MUST also call + * stopTextWidthCache. Caches must not survive between execution threads. + */ +Blockly.utils.dom.startTextWidthCache = function() { + Blockly.utils.dom.cacheReference_++; + if (!Blockly.utils.dom.cacheWidths_) { + Blockly.utils.dom.cacheWidths_ = {}; + } +}; + +/** + * Stop caching field widths. Unless caching was already on when the + * corresponding call to startTextWidthCache was made. + */ +Blockly.utils.dom.stopTextWidthCache = function() { + Blockly.utils.dom.cacheReference_--; + if (!Blockly.utils.dom.cacheReference_) { + Blockly.utils.dom.cacheWidths_ = null; + } +}; + +/** + * Gets the width of a text element, caching it in the process. + * @param {!Element} textElement An SVG 'text' element. + * @return {number} Width of element. + */ +Blockly.utils.dom.getTextWidth = function(textElement) { + var key = textElement.textContent + '\n' + textElement.className.baseVal; + var width; + + // Return the cached width if it exists. + if (Blockly.utils.dom.cacheWidths_) { + width = Blockly.utils.dom.cacheWidths_[key]; + if (width) { + return width; + } + } + + // Attempt to compute fetch the width of the SVG text element. + try { + if (Blockly.utils.userAgent.IE || Blockly.utils.userAgent.EDGE) { + width = textElement.getBBox().width; + } else { + width = textElement.getComputedTextLength(); + } + } catch (e) { + // In other cases where we fail to get the computed text. Instead, use an + // approximation and do not cache the result. At some later point in time + // when the block is inserted into the visible DOM, this method will be + // called again and, at that point in time, will not throw an exception. + return textElement.textContent.length * 8; + } + + // Cache the computed width and return. + if (Blockly.utils.dom.cacheWidths_) { + Blockly.utils.dom.cacheWidths_[key] = width; + } + return width; +}; diff --git a/core/utils/field.js b/core/utils/field.js new file mode 100644 index 000000000..2b95cf2f8 --- /dev/null +++ b/core/utils/field.js @@ -0,0 +1,92 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2019 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. + */ + +// TODO: Files in this directory seem to not be specific to Blockly, yet +// this one is. Should it be moved to the core? +/** + * @fileoverview Utility methods for handling fields. + * @author bekawestberg@gmail.com (Beka Westberg) + */ +'use strict'; + +/** + * @name Blockly.utils + * @namespace + */ +goog.provide('Blockly.utils.fields'); + + +/** + * The set of all registered fields, keyed by field type as used in the JSON + * definition of a block. + * @type {!Object} + * @private + */ +Blockly.utils.fields.typeMap_ = {}; + +/** + * Registers a field type. May also override an existing field type. + * Blockly.utils.fields.fromJson uses this registry to + * find the appropriate field type. + * @param {string} type The field type name as used in the JSON definition. + * @param {!{fromJson: Function}} fieldClass The field class containing a + * fromJson function that can construct an instance of the field. + * @throws {Error} if the type name is empty, or the fieldClass is not an + * object containing a fromJson function. + */ +Blockly.utils.fields.register = function(type, fieldClass) { + if ((typeof type != 'string') || (type.trim() == '')) { + throw Error('Invalid field type "' + type + '"'); + } + if (!fieldClass || (typeof fieldClass.fromJson != 'function')) { + throw Error('Field "' + fieldClass + '" must have a fromJson function'); + } + Blockly.utils.fields.typeMap_[type] = fieldClass; +}; + +/** + * Construct a Field from a JSON arg object. + * Finds the appropriate registered field by the type name as registered using + * Blockly.utils.fields.register. + * @param {!Object} options A JSON object with a type and options specific + * to the field type. + * @return {Blockly.Field} The new field instance or null if a field wasn't + * found with the given type name + * @package + */ +Blockly.utils.fields.fromJson = function(options) { + var fieldClass = Blockly.utils.fields.typeMap_[options['type']]; + if (!fieldClass) { + console.warn('Blockly could not create a field of type ' + options['type'] + + '. The field is probably not being registered. This may be because the' + + ' file is not loaded, the field does not register itself (See:' + + ' github.com/google/blockly/issues/1584), or the registration is not' + + ' being reached.'); + return null; + } + + var field = fieldClass.fromJson(options); + if (options['tooltip'] !== undefined) { + var rawValue = options['tooltip']; + var localizedText = Blockly.utils.replaceMessageReferences(rawValue); + field.setTooltip(localizedText); + } + return field; +}; diff --git a/core/xml.js b/core/xml.js index 148ca3fd2..bc0e1fc0b 100644 --- a/core/xml.js +++ b/core/xml.js @@ -376,7 +376,7 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { width = workspace.getWidth(); } var newBlockIds = []; // A list of block IDs added by this call. - Blockly.Field.startCache(); + Blockly.utils.dom.startTextWidthCache(); // Safari 7.1.3 is known to provide node lists with extra references to // children beyond the lists' length. Trust the length, do not use the // looping pattern of checking the index for an object. @@ -433,7 +433,7 @@ Blockly.Xml.domToWorkspace = function(xml, workspace) { if (!existingGroup) { Blockly.Events.setGroup(false); } - Blockly.Field.stopCache(); + Blockly.utils.dom.stopTextWidthCache(); } // Re-enable workspace resizing. if (workspace.setResizesEnabled) { diff --git a/demos/custom-fields/field_turtle.js b/demos/custom-fields/field_turtle.js index da49d9a1a..dc7b8c8ea 100644 --- a/demos/custom-fields/field_turtle.js +++ b/demos/custom-fields/field_turtle.js @@ -533,7 +533,7 @@ CustomFields.FieldTurtle.prototype.fromXml = function(fieldElement) { // Blockly needs to know the JSON name of this field. Usually this is // registered at the bottom of the field class. -Blockly.Field.register('field_turtle', CustomFields.FieldTurtle); +Blockly.utils.fields.register('field_turtle', CustomFields.FieldTurtle); // Called by initView to create all of the SVGs. This is just used to keep // the code more organized. diff --git a/tests/mocha/field_test.js b/tests/mocha/field_test.js index 5493e93e2..6d5454434 100644 --- a/tests/mocha/field_test.js +++ b/tests/mocha/field_test.js @@ -19,7 +19,7 @@ */ suite('Abstract Fields', function() { - suite.skip('Is Serializable', function() { + suite('Is Serializable', function() { // Both EDITABLE and SERIALIZABLE are default. function FieldDefault() { this.name = 'NAME'; @@ -72,7 +72,7 @@ suite('Abstract Fields', function() { assertEquals(true, field.isSerializable()); }); }); - suite.skip('setValue', function() { + suite('setValue', function() { function addSpies(field) { if (!this.isSpying) { sinon.spy(field, 'doValueInvalid_');