mirror of
https://github.com/google/blockly.git
synced 2026-01-15 04:47:10 +01:00
103
core/field.js
103
core/field.js
@@ -51,15 +51,38 @@ goog.require('Blockly.utils.style');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.Field = function(value, opt_validator, opt_config) {
|
||||
/**
|
||||
* A generic value possessed by the field.
|
||||
* Should generally be non-null, only null when the field is created.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
this.value_ = null;
|
||||
|
||||
/**
|
||||
* Validation function called when user edits an editable field.
|
||||
* @type {Function}
|
||||
* @protected
|
||||
*/
|
||||
this.validator_ = null;
|
||||
|
||||
/**
|
||||
* Used to cache the field's tooltip value if setTooltip is called when the
|
||||
* field is not yet initialized. Is *not* guaranteed to be accurate.
|
||||
* @type {string|Function|!Element}
|
||||
* @private
|
||||
*/
|
||||
this.tooltip_ = null;
|
||||
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @type {!Blockly.utils.Size}
|
||||
* @protected
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(0, 0);
|
||||
opt_config && this.configure_(opt_config);
|
||||
this.setValue(value);
|
||||
this.setValidator(opt_validator);
|
||||
this.configure_(opt_config);
|
||||
opt_validator && this.setValidator(opt_validator);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -117,22 +140,6 @@ Blockly.Field.prototype.disposed = false;
|
||||
*/
|
||||
Blockly.Field.prototype.maxDisplayLength = 50;
|
||||
|
||||
/**
|
||||
* A generic value possessed by the field.
|
||||
* Should generally be non-null, only null when the field is created.
|
||||
* @type {*}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Field.prototype.value_ = null;
|
||||
|
||||
/**
|
||||
* Used to cache the field's tooltip value if setTooltip is called when the
|
||||
* field is not yet initialized. Is *not* guaranteed to be accurate.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Field.prototype.tooltip_ = null;
|
||||
|
||||
/**
|
||||
* Block this field is attached to. Starts as null, then set in init.
|
||||
* @type {Blockly.Block}
|
||||
@@ -143,7 +150,7 @@ Blockly.Field.prototype.sourceBlock_ = null;
|
||||
/**
|
||||
* Does this block need to be re-rendered?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Field.prototype.isDirty_ = true;
|
||||
|
||||
@@ -154,16 +161,9 @@ Blockly.Field.prototype.isDirty_ = true;
|
||||
*/
|
||||
Blockly.Field.prototype.visible_ = true;
|
||||
|
||||
/**
|
||||
* Validation function called when user edits an editable field.
|
||||
* @type {Function}
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Field.prototype.validator_ = null;
|
||||
|
||||
/**
|
||||
* The element the click handler is bound to.
|
||||
* @type {!Element}
|
||||
* @type {Element}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Field.prototype.clickTarget_ = null;
|
||||
@@ -203,20 +203,17 @@ Blockly.Field.prototype.EDITABLE = true;
|
||||
Blockly.Field.prototype.SERIALIZABLE = false;
|
||||
|
||||
/**
|
||||
* Configure the field based on the given map of options.
|
||||
* @param {Object} opt_config The map of options to configure the field
|
||||
* based on.
|
||||
* @private
|
||||
* Process the configuration map passed to the field.
|
||||
* @param {!Object} config A map of options used to configure the field. See
|
||||
* the individual field's documentation for a list of properties this
|
||||
* parameter supports.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.Field.prototype.configure_ = function(opt_config) {
|
||||
if (!opt_config) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tooltip = opt_config['tooltip'];
|
||||
Blockly.Field.prototype.configure_ = function(config) {
|
||||
var tooltip = config['tooltip'];
|
||||
if (typeof tooltip == 'string') {
|
||||
tooltip = Blockly.utils.replaceMessageReferences(
|
||||
opt_config['tooltip']);
|
||||
config['tooltip']);
|
||||
}
|
||||
tooltip && this.setTooltip(tooltip);
|
||||
|
||||
@@ -274,6 +271,14 @@ Blockly.Field.prototype.initView = function() {
|
||||
this.createTextElement_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the model of the field after it has been installed on a block.
|
||||
* No-op by default.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Field.prototype.initModel = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a field border rect element. Not to be overridden by subclasses.
|
||||
* Instead modify the result of the function inside initView, or create a
|
||||
@@ -327,14 +332,6 @@ Blockly.Field.prototype.bindEvents_ = function() {
|
||||
this.getClickTarget_(), 'mousedown', this, this.onMouseDown_);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes the model of the field after it has been installed on a block.
|
||||
* No-op by default.
|
||||
* @package
|
||||
*/
|
||||
Blockly.Field.prototype.initModel = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the field's value based on the given XML element. Should only be
|
||||
* called by Blockly.Xml.
|
||||
@@ -772,14 +769,18 @@ Blockly.Field.prototype.getValue = function() {
|
||||
/**
|
||||
* Used to validate a value. Returns input by default. Can be overridden by
|
||||
* subclasses, see FieldDropdown.
|
||||
* @param {*} newValue The value to be validated.
|
||||
* @param {*=} opt_newValue The value to be validated.
|
||||
* @return {*} The validated value, same as input by default.
|
||||
* @protected
|
||||
* @suppress {deprecated}
|
||||
*/
|
||||
Blockly.Field.prototype.doClassValidation_ = function(newValue) {
|
||||
Blockly.Field.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
if (opt_newValue === null || opt_newValue === undefined) {
|
||||
return null;
|
||||
}
|
||||
// For backwards compatibility.
|
||||
newValue = this.classValidator(newValue);
|
||||
return newValue;
|
||||
opt_newValue = this.classValidator(/** @type {string} */ (opt_newValue));
|
||||
return opt_newValue;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,12 +46,8 @@ goog.require('Blockly.utils.userAgent');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldAngle = function(opt_value, opt_validator) {
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (opt_value === null) {
|
||||
opt_value = 0;
|
||||
}
|
||||
Blockly.FieldAngle.superClass_.constructor.call(
|
||||
this, opt_value, opt_validator);
|
||||
this, opt_value || 0, opt_validator);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldAngle, Blockly.FieldTextInput);
|
||||
|
||||
@@ -373,7 +369,7 @@ Blockly.FieldAngle.prototype.onHtmlInputKeyDown_ = function(e) {
|
||||
|
||||
/**
|
||||
* Ensure that the input value is a valid angle.
|
||||
* @param {string|number=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?number} A valid angle, or null if invalid.
|
||||
* @protected
|
||||
* @override
|
||||
|
||||
@@ -50,15 +50,6 @@ goog.require('Blockly.utils.Size');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldCheckbox = function(opt_value, opt_validator, opt_config) {
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (opt_value === null) {
|
||||
opt_value = 'FALSE';
|
||||
}
|
||||
Blockly.FieldCheckbox.superClass_.constructor.call(
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
|
||||
this.size_.width = Blockly.FieldCheckbox.WIDTH;
|
||||
|
||||
/**
|
||||
* Character for the check mark. Used to apply a different check mark
|
||||
* character to individual fields.
|
||||
@@ -67,7 +58,14 @@ Blockly.FieldCheckbox = function(opt_value, opt_validator, opt_config) {
|
||||
*/
|
||||
this.checkChar_ = null;
|
||||
|
||||
this.configure_(opt_config);
|
||||
if (opt_value == null) {
|
||||
opt_value = 'FALSE';
|
||||
}
|
||||
Blockly.FieldCheckbox.superClass_.constructor.call(
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
|
||||
this.size_.width = Blockly.FieldCheckbox.WIDTH;
|
||||
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldCheckbox, Blockly.Field);
|
||||
|
||||
@@ -128,20 +126,19 @@ Blockly.FieldCheckbox.prototype.CURSOR = 'default';
|
||||
* rendered. Checkbox fields are statically sized, and only need to be
|
||||
* rendered at initialization.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldCheckbox.prototype.isDirty_ = false;
|
||||
|
||||
/**
|
||||
* Configure the field based on the given map of options.
|
||||
* @param {Object} opt_config A map of options to configure the field based on.
|
||||
* @param {!Object} config A map of options to configure the field based on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldCheckbox.prototype.configure_ = function(opt_config) {
|
||||
if (opt_config) {
|
||||
if (opt_config['checkCharacter']) {
|
||||
this.checkChar_ = opt_config['checkCharacter'];
|
||||
}
|
||||
Blockly.FieldCheckbox.prototype.configure_ = function(config) {
|
||||
Blockly.FieldCheckbox.superClass_.configure_.call(this, config);
|
||||
if (config['checkCharacter']) {
|
||||
this.checkChar_ = config['checkCharacter'];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -183,7 +180,7 @@ Blockly.FieldCheckbox.prototype.showEditor_ = function() {
|
||||
|
||||
/**
|
||||
* Ensure that the input value is valid ('TRUE' or 'FALSE').
|
||||
* @param {string|boolean=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A valid value ('TRUE' or 'FALSE), or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
@@ -220,10 +217,10 @@ Blockly.FieldCheckbox.prototype.getValue = function() {
|
||||
|
||||
/**
|
||||
* Get the boolean value of this field.
|
||||
* @return {string} The boolean value of this field.
|
||||
* @return {boolean} The boolean value of this field.
|
||||
*/
|
||||
Blockly.FieldCheckbox.prototype.getValueBoolean = function() {
|
||||
return this.value_;
|
||||
return /** @type {boolean} */ (this.value_);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,14 +53,18 @@ goog.require('Blockly.utils.Size');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldColour = function(opt_value, opt_validator, opt_config) {
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (opt_value === null) {
|
||||
opt_value = Blockly.FieldColour.COLOURS[0];
|
||||
}
|
||||
Blockly.FieldColour.superClass_.constructor.call(
|
||||
this, opt_value, opt_validator, opt_config);
|
||||
this, opt_value || Blockly.FieldColour.COLOURS[0],
|
||||
opt_validator, opt_config);
|
||||
|
||||
this.configure_(opt_config);
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(Blockly.FieldColour.DEFAULT_WIDTH,
|
||||
Blockly.FieldColour.DEFAULT_HEIGHT);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldColour, Blockly.Field);
|
||||
|
||||
@@ -109,7 +113,7 @@ Blockly.FieldColour.prototype.CURSOR = 'default';
|
||||
* rendered. Colour fields are statically sized, and only need to be
|
||||
* rendered at initialization.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldColour.prototype.isDirty_ = false;
|
||||
|
||||
@@ -153,19 +157,17 @@ Blockly.FieldColour.prototype.DROPDOWN_BACKGROUND_COLOUR = 'white';
|
||||
|
||||
/**
|
||||
* Configure the field based on the given map of options.
|
||||
* @param {Object} opt_config A map of options to configure the field based on.
|
||||
* @param {!Object} config A map of options to configure the field based on.
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldColour.prototype.configure_ = function(opt_config) {
|
||||
if (!opt_config) {
|
||||
return;
|
||||
Blockly.FieldColour.prototype.configure_ = function(config) {
|
||||
Blockly.FieldColour.superClass_.configure_.call(this, config);
|
||||
if (config['colourOptions']) {
|
||||
this.colours_ = config['colourOptions'];
|
||||
this.titles_ = config['colourTitles'];
|
||||
}
|
||||
|
||||
if (opt_config['colourOptions']) {
|
||||
this.setColours(opt_config['colourOptions'], opt_config['colourTitles']);
|
||||
}
|
||||
if (opt_config['columns']) {
|
||||
this.setColumns(opt_config['columns']);
|
||||
if (config['columns']) {
|
||||
this.columns_ = config['columns'];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -174,8 +176,6 @@ Blockly.FieldColour.prototype.configure_ = function(opt_config) {
|
||||
* @package
|
||||
*/
|
||||
Blockly.FieldColour.prototype.initView = function() {
|
||||
this.size_ = new Blockly.utils.Size(Blockly.FieldColour.DEFAULT_WIDTH,
|
||||
Blockly.FieldColour.DEFAULT_HEIGHT);
|
||||
this.createBorderRect_();
|
||||
this.borderRect_.style['fillOpacity'] = 1;
|
||||
this.borderRect_.style.fill = this.value_;
|
||||
@@ -183,7 +183,7 @@ Blockly.FieldColour.prototype.initView = function() {
|
||||
|
||||
/**
|
||||
* Ensure that the input value is a valid colour.
|
||||
* @param {string=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A valid colour, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
@@ -211,7 +211,7 @@ Blockly.FieldColour.prototype.doValueUpdate_ = function(newValue) {
|
||||
* @return {string} Text representing the value of this field.
|
||||
*/
|
||||
Blockly.FieldColour.prototype.getText = function() {
|
||||
var colour = this.value_;
|
||||
var colour = /** @type {string} */ (this.value_);
|
||||
// Try to use #rgb format if possible, rather than #rrggbb.
|
||||
if (/^#(.)\1(.)\2(.)\3$/.test(colour)) {
|
||||
colour = '#' + colour[1] + colour[3] + colour[5];
|
||||
|
||||
@@ -52,12 +52,8 @@ goog.require('goog.ui.DatePicker');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldDate = function(opt_value, opt_validator) {
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (!opt_value) {
|
||||
opt_value = new goog.date.Date().toIsoString(true);
|
||||
}
|
||||
Blockly.FieldDate.superClass_.constructor.call(this,
|
||||
opt_value, opt_validator);
|
||||
opt_value || new goog.date.Date().toIsoString(true), opt_validator);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldDate, Blockly.Field);
|
||||
|
||||
@@ -103,7 +99,7 @@ Blockly.FieldDate.prototype.DROPDOWN_BACKGROUND_COLOUR = 'white';
|
||||
|
||||
/**
|
||||
* Ensure that the input value is a valid date.
|
||||
* @param {string=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A valid date, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
|
||||
@@ -59,11 +59,14 @@ Blockly.FieldDropdown = function(menuGenerator, opt_validator) {
|
||||
Blockly.FieldDropdown.validateOptions_(menuGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of options for a dropdown list,
|
||||
* or a function which generates these options.
|
||||
* @type {(!Array.<!Array>|!Function)}
|
||||
* @protected
|
||||
*/
|
||||
this.menuGenerator_ = menuGenerator;
|
||||
|
||||
this.trimOptions_();
|
||||
var firstTuple = this.getOptions()[0];
|
||||
|
||||
/**
|
||||
* The currently selected index. A value of -1 indicates no option
|
||||
* has been selected.
|
||||
@@ -72,10 +75,20 @@ Blockly.FieldDropdown = function(menuGenerator, opt_validator) {
|
||||
*/
|
||||
this.selectedIndex_ = -1;
|
||||
|
||||
this.trimOptions_();
|
||||
var firstTuple = this.getOptions()[0];
|
||||
|
||||
// Call parent's constructor.
|
||||
Blockly.FieldDropdown.superClass_.constructor.call(this, firstTuple[1],
|
||||
opt_validator);
|
||||
|
||||
/**
|
||||
* SVG image element if currently selected option is an image, or null.
|
||||
* @type {SVGElement}
|
||||
* @private
|
||||
*/
|
||||
this.imageElement_ = null;
|
||||
|
||||
/**
|
||||
* A reference to the currently selected menu item.
|
||||
* @type {Blockly.MenuItem}
|
||||
@@ -154,13 +167,6 @@ Blockly.FieldDropdown.ARROW_CHAR =
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.CURSOR = 'default';
|
||||
|
||||
/**
|
||||
* SVG image element if currently selected option is an image, or null.
|
||||
* @type {SVGElement}
|
||||
* @private
|
||||
*/
|
||||
Blockly.FieldDropdown.prototype.imageElement_ = null;
|
||||
|
||||
/**
|
||||
* Create the block UI for this dropdown.
|
||||
* @package
|
||||
@@ -430,7 +436,7 @@ Blockly.FieldDropdown.prototype.getOptions = function() {
|
||||
|
||||
/**
|
||||
* Ensure that the input value is a valid language-neutral option.
|
||||
* @param {string=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A valid language-neutral option, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
@@ -452,7 +458,7 @@ Blockly.FieldDropdown.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return opt_newValue;
|
||||
return /** @type {string} */ (opt_newValue);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,7 +48,6 @@ goog.require('Blockly.utils.Size');
|
||||
*/
|
||||
Blockly.FieldImage = function(src, width, height,
|
||||
opt_alt, opt_onClick, opt_flipRtl) {
|
||||
this.sourceBlock_ = null;
|
||||
|
||||
if (!src) {
|
||||
throw Error('Src value of an image field is required');
|
||||
@@ -66,10 +65,13 @@ Blockly.FieldImage = function(src, width, height,
|
||||
throw Error('Height and width values of an image field must be greater' +
|
||||
' than 0.');
|
||||
}
|
||||
// Store the image height, since it is different from the field height.
|
||||
|
||||
/**
|
||||
* Store the image height, since it is different from the field height.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.imageHeight_ = imageHeight;
|
||||
this.size_ = new Blockly.utils.Size(imageWidth,
|
||||
imageHeight + Blockly.FieldImage.Y_PADDING);
|
||||
|
||||
/**
|
||||
* Whether to flip this image in RTL.
|
||||
@@ -85,11 +87,21 @@ Blockly.FieldImage = function(src, width, height,
|
||||
*/
|
||||
this.altText_ = opt_alt || '';
|
||||
|
||||
this.setValue(src || '');
|
||||
|
||||
if (typeof opt_onClick == 'function') {
|
||||
this.clickHandler_ = opt_onClick;
|
||||
}
|
||||
|
||||
Blockly.FieldImage.superClass_.constructor.call(
|
||||
this, src || '', null);
|
||||
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(imageWidth,
|
||||
imageHeight + Blockly.FieldImage.Y_PADDING);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldImage, Blockly.Field);
|
||||
|
||||
@@ -133,7 +145,7 @@ Blockly.FieldImage.prototype.EDITABLE = false;
|
||||
* rendered. Image fields are statically sized, and only need to be
|
||||
* rendered at initialization.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldImage.prototype.isDirty_ = false;
|
||||
|
||||
@@ -151,12 +163,12 @@ Blockly.FieldImage.prototype.initView = function() {
|
||||
},
|
||||
this.fieldGroup_);
|
||||
this.imageElement_.setAttributeNS(Blockly.utils.dom.XLINK_NS,
|
||||
'xlink:href', this.value_);
|
||||
'xlink:href', /** @type {string} */ (this.value_));
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that the input value (the source URL) is a string.
|
||||
* @param {string=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A string, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
|
||||
@@ -44,13 +44,26 @@ goog.require('Blockly.utils.Size');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldLabel = function(opt_value, opt_class) {
|
||||
this.size_ = new Blockly.utils.Size(0, Blockly.Field.TEXT_DEFAULT_HEIGHT);
|
||||
this.class_ = opt_class;
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (opt_value === null) {
|
||||
/**
|
||||
* The html class name to use for this field.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
this.class_ = opt_class || null;
|
||||
|
||||
if (opt_value == null) {
|
||||
opt_value = '';
|
||||
}
|
||||
this.setValue(opt_value);
|
||||
Blockly.FieldLabel.superClass_.constructor.call(
|
||||
this, opt_value, null);
|
||||
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(0, Blockly.Field.TEXT_DEFAULT_HEIGHT);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldLabel, Blockly.Field);
|
||||
|
||||
@@ -90,7 +103,7 @@ Blockly.FieldLabel.prototype.initView = function() {
|
||||
|
||||
/**
|
||||
* Ensure that the input value casts to a valid string.
|
||||
* @param {string=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?string} A valid string, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
|
||||
@@ -46,13 +46,32 @@ goog.require('Blockly.utils.object');
|
||||
*/
|
||||
Blockly.FieldNumber = function(opt_value, opt_min, opt_max, opt_precision,
|
||||
opt_validator) {
|
||||
this.setConstraints(opt_min, opt_max, opt_precision);
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (opt_value === null) {
|
||||
opt_value = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The minimum value constraint.
|
||||
* @type {number}
|
||||
* @protected
|
||||
*/
|
||||
this.min_ = -Infinity;
|
||||
|
||||
/**
|
||||
* The maximum value constraint.
|
||||
* @type {number}
|
||||
* @protected
|
||||
*/
|
||||
this.max_ = Infinity;
|
||||
|
||||
/**
|
||||
* The precision constraint for the value.
|
||||
* @type {number}
|
||||
* @protected
|
||||
*/
|
||||
this.precision_ = 0;
|
||||
|
||||
Blockly.FieldNumber.superClass_.constructor.call(
|
||||
this, opt_value, opt_validator);
|
||||
this, opt_value || 0, opt_validator);
|
||||
|
||||
this.setConstraints(opt_min, opt_max, opt_precision);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldNumber, Blockly.FieldTextInput);
|
||||
|
||||
@@ -90,28 +109,34 @@ Blockly.FieldNumber.prototype.SERIALIZABLE = true;
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.setConstraints = function(min, max, precision) {
|
||||
precision = Number(precision);
|
||||
this.precision_ = isNaN(precision) ? 0 : precision;
|
||||
if (!isNaN(precision)) {
|
||||
this.precision_ = precision;
|
||||
}
|
||||
var precisionString = this.precision_.toString();
|
||||
var decimalIndex = precisionString.indexOf('.');
|
||||
this.fractionalDigits_ = (decimalIndex == -1) ? -1 :
|
||||
precisionString.length - (decimalIndex + 1);
|
||||
min = Number(min);
|
||||
this.min_ = isNaN(min) ? -Infinity : min;
|
||||
if (!isNaN(min)) {
|
||||
this.min_ = min;
|
||||
}
|
||||
max = Number(max);
|
||||
this.max_ = isNaN(max) ? Infinity : max;
|
||||
if (!isNaN(max)) {
|
||||
this.max_ = max;
|
||||
}
|
||||
this.setValue(this.getValue());
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that the input value is a valid number (must fulfill the
|
||||
* constraints placed on the field).
|
||||
* @param {string|number=} opt_newValue The input value.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {?number} A valid number, or null if invalid.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
Blockly.FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
if (opt_newValue === null || opt_newValue === undefined) {
|
||||
if (opt_newValue === null) {
|
||||
return null;
|
||||
}
|
||||
// Clean up text.
|
||||
@@ -142,7 +167,7 @@ Blockly.FieldNumber.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
|
||||
/**
|
||||
* Create the number input editor widget.
|
||||
* @return {!HTMLInputElement} The newly created number input editor.
|
||||
* @return {!HTMLElement} The newly created number input editor.
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
|
||||
@@ -51,12 +51,11 @@ goog.require('Blockly.utils.userAgent');
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.FieldTextInput = function(opt_value, opt_validator) {
|
||||
opt_value = this.doClassValidation_(opt_value);
|
||||
if (opt_value === null) {
|
||||
if (opt_value == null) {
|
||||
opt_value = '';
|
||||
}
|
||||
Blockly.FieldTextInput.superClass_.constructor.call(this, opt_value,
|
||||
opt_validator);
|
||||
Blockly.FieldTextInput.superClass_.constructor.call(this,
|
||||
opt_value, opt_validator);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldTextInput, Blockly.Field);
|
||||
|
||||
@@ -92,7 +91,8 @@ Blockly.FieldTextInput.prototype.SERIALIZABLE = true;
|
||||
Blockly.FieldTextInput.FONTSIZE = 11;
|
||||
|
||||
/**
|
||||
* Pixel size of input border radius. Should match blocklyText's border-radius in CSS.
|
||||
* Pixel size of input border radius.
|
||||
* Should match blocklyText's border-radius in CSS.
|
||||
*/
|
||||
Blockly.FieldTextInput.BORDERRADIUS = 4;
|
||||
|
||||
@@ -109,8 +109,8 @@ Blockly.FieldTextInput.prototype.spellcheck_ = true;
|
||||
|
||||
/**
|
||||
* Ensure that the input value casts to a valid string.
|
||||
* @param {string=} opt_newValue The input value.
|
||||
* @return {?string} A valid string, or null if invalid.
|
||||
* @param {*=} opt_newValue The input value.
|
||||
* @return {*} A valid string, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldTextInput.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
|
||||
@@ -59,12 +59,19 @@ Blockly.FieldVariable = function(varname, opt_validator, opt_variableTypes,
|
||||
// The FieldDropdown constructor would call setValue, which might create a
|
||||
// spurious variable. Just do the relevant parts of the constructor.
|
||||
this.menuGenerator_ = Blockly.FieldVariable.dropdownCreate;
|
||||
this.size_ = new Blockly.utils.Size(0, Blockly.BlockSvg.MIN_BLOCK_Y);
|
||||
opt_validator && this.setValidator(opt_validator);
|
||||
this.defaultVariableName = varname || '';
|
||||
|
||||
this.setTypes_(opt_variableTypes, opt_defaultType);
|
||||
this.value_ = null;
|
||||
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(0, Blockly.BlockSvg.MIN_BLOCK_Y);
|
||||
};
|
||||
Blockly.utils.object.inherits(Blockly.FieldVariable, Blockly.FieldDropdown);
|
||||
|
||||
@@ -224,11 +231,15 @@ Blockly.FieldVariable.prototype.getValidator = function() {
|
||||
|
||||
/**
|
||||
* Ensure that the id belongs to a valid variable of an allowed type.
|
||||
* @param {string} newId The id of the new variable to set.
|
||||
* @param {*=} opt_newValue The id of the new variable to set.
|
||||
* @return {?string} The validated id, or null if invalid.
|
||||
* @protected
|
||||
*/
|
||||
Blockly.FieldVariable.prototype.doClassValidation_ = function(newId) {
|
||||
Blockly.FieldVariable.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
if (opt_newValue === null) {
|
||||
return null;
|
||||
}
|
||||
var newId = /** @type {string} */ (opt_newValue);
|
||||
var variable = Blockly.Variables.getVariable(this.workspace_, newId);
|
||||
if (!variable) {
|
||||
console.warn('Variable id doesn\'t point to a real variable! ' +
|
||||
|
||||
@@ -48,24 +48,21 @@ CustomFields.FieldTurtle = function(
|
||||
// The turtle field contains an object as its value, so we need to compile
|
||||
// the parameters into an object.
|
||||
var value = {};
|
||||
value.pattern = opt_pattern;
|
||||
value.hat = opt_hat;
|
||||
value.turtleName = opt_turtleName;
|
||||
|
||||
var valid = this.doClassValidation_(value);
|
||||
if (valid === null) {
|
||||
// See the doClassValidation_ function for information on the
|
||||
// cachedValidatedValue_ property.
|
||||
value = this.cachedValidatedValue_;
|
||||
value.pattern = value.pattern || CustomFields.FieldTurtle.PATTERNS[0];
|
||||
value.hat = value.hat || CustomFields.FieldTurtle.HATS[0];
|
||||
value.turtleName = value.turtleName || CustomFields.FieldTurtle.NAMES[0];
|
||||
} // Else the original value is fine.
|
||||
value.pattern = opt_pattern || CustomFields.FieldTurtle.PATTERNS[0];
|
||||
value.hat = opt_hat || CustomFields.FieldTurtle.HATS[0];
|
||||
value.turtleName = opt_turtleName || CustomFields.FieldTurtle.NAMES[0];
|
||||
|
||||
// A field constructor should always call its parent constructor, because
|
||||
// that helps keep the code organized and DRY.
|
||||
CustomFields.FieldTurtle.superClass_.constructor.call(
|
||||
this, value, opt_validator);
|
||||
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(0, 0);
|
||||
};
|
||||
Blockly.utils.object.inherits(CustomFields.FieldTurtle, Blockly.Field);
|
||||
|
||||
@@ -988,55 +988,6 @@ Blockly.Blocks['test_validators_colour_red_null'] = {
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['test_validators_date_null'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField("always null")
|
||||
.appendField(new Blockly.FieldDate("2020-02-20", this.validate), "INPUT");
|
||||
this.setColour(230);
|
||||
this.setCommentText('All input validates to null (invalid). This means' +
|
||||
' the field value should not change.');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
// We should be able to expect validators to like their initial values.
|
||||
if (newValue != '2020-02-20') {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
Blockly.Blocks['test_validators_date_force_20s'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField("force day 20s")
|
||||
.appendField(new Blockly.FieldDate("2020-02-20", this.validate), "INPUT");
|
||||
this.setColour(230);
|
||||
this.setCommentText('The input\'s date will change to always be in the' +
|
||||
' 20s.');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
return newValue.substr(0, 8) + '2' + newValue.substr(9, 1);
|
||||
}
|
||||
};
|
||||
Blockly.Blocks['test_validators_date_20s_null'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField("not 20s -> null")
|
||||
.appendField(new Blockly.FieldDate("2020-02-20", this.validate), "INPUT");
|
||||
this.setColour(230);
|
||||
this.setCommentText('If the input is not in the 20s, the input will' +
|
||||
' validate to null (invalid). Otherwise it will return the input value.');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
if (newValue.charAt(8) != '2') {
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['test_validators_dropdown_null'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
|
||||
@@ -39,10 +39,6 @@ suite('Angle Fields', function() {
|
||||
var angleField = new Blockly.FieldAngle(undefined);
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var angleField = new Blockly.FieldAngle('bad');
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
test('NaN', function() {
|
||||
var angleField = new Blockly.FieldAngle(NaN);
|
||||
assertValueDefault(angleField);
|
||||
@@ -67,14 +63,6 @@ suite('Angle Fields', function() {
|
||||
var angleField = new Blockly.FieldAngle(362);
|
||||
assertValue(angleField, 2);
|
||||
});
|
||||
test('Infinity', function() {
|
||||
var angleField = new Blockly.FieldAngle(Infinity);
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
test('Negative Infinity String', function() {
|
||||
var angleField = new Blockly.FieldAngle('-Infinity');
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
});
|
||||
suite('fromJson', function() {
|
||||
test('Empty', function() {
|
||||
@@ -85,10 +73,6 @@ suite('Angle Fields', function() {
|
||||
var angleField = Blockly.FieldAngle.fromJson({ angle:undefined });
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var angleField = Blockly.FieldAngle.fromJson({ angle:'bad' });
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
test('NaN', function() {
|
||||
var angleField = Blockly.FieldAngle.fromJson({ angle:NaN });
|
||||
assertValueDefault(angleField);
|
||||
@@ -113,14 +97,6 @@ suite('Angle Fields', function() {
|
||||
var angleField = Blockly.FieldAngle.fromJson({ angle:362 });
|
||||
assertValue(angleField, 2);
|
||||
});
|
||||
test('Infinity', function() {
|
||||
var angleField = Blockly.FieldAngle.fromJson({ angle:Infinity });
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
test('Negative Infinity String', function() {
|
||||
var angleField = Blockly.FieldAngle.fromJson({ angle:'-Infinity' });
|
||||
assertValueDefault(angleField);
|
||||
});
|
||||
});
|
||||
suite('setValue', function() {
|
||||
suite('Empty -> New Value', function() {
|
||||
|
||||
@@ -37,10 +37,6 @@ suite('Checkbox Fields', function() {
|
||||
var checkboxField = new Blockly.FieldCheckbox(undefined);
|
||||
assertValueDefault(checkboxField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var checkboxField = new Blockly.FieldCheckbox('bad');
|
||||
assertValueDefault(checkboxField);
|
||||
});
|
||||
test('True', function() {
|
||||
var checkboxField = new Blockly.FieldCheckbox(true);
|
||||
assertValue(checkboxField, 'TRUE', 'true');
|
||||
@@ -67,10 +63,6 @@ suite('Checkbox Fields', function() {
|
||||
var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: undefined});
|
||||
assertValueDefault(checkboxField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: 'bad'});
|
||||
assertValueDefault(checkboxField);
|
||||
});
|
||||
test('True', function() {
|
||||
var checkboxField = Blockly.FieldCheckbox.fromJson({ checked: true});
|
||||
assertValue(checkboxField, 'TRUE', 'true');
|
||||
|
||||
@@ -53,10 +53,6 @@ suite('Colour Fields', function() {
|
||||
var colourField = new Blockly.FieldColour(undefined);
|
||||
assertValueDefault(colourField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var colourField = new Blockly.FieldColour('not_a_colour');
|
||||
assertValueDefault(colourField);
|
||||
});
|
||||
test('#AAAAAA', function() {
|
||||
var colourField = new Blockly.FieldColour('#AAAAAA');
|
||||
assertValue(colourField, '#aaaaaa', '#aaa');
|
||||
@@ -107,11 +103,6 @@ suite('Colour Fields', function() {
|
||||
var colourField = new Blockly.FieldColour.fromJson({ colour:undefined });
|
||||
assertValueDefault(colourField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var colourField = new Blockly.FieldColour.fromJson(
|
||||
{ colour:'not_a_colour' });
|
||||
assertValueDefault(colourField);
|
||||
});
|
||||
test('#AAAAAA', function() {
|
||||
var colourField = Blockly.FieldColour.fromJson({ colour: '#AAAAAA' });
|
||||
assertValue(colourField, '#aaaaaa', '#aaa');
|
||||
|
||||
@@ -59,10 +59,6 @@ suite('Number Fields', function() {
|
||||
var numberField = createNumberFieldSameValuesConstructor(undefined);
|
||||
assertNumberFieldDefault(numberField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var numberField = createNumberFieldSameValuesConstructor('bad');
|
||||
assertNumberFieldDefault(numberField);
|
||||
});
|
||||
test('NaN', function() {
|
||||
var numberField = createNumberFieldSameValuesConstructor(NaN);
|
||||
assertNumberFieldDefault(numberField);
|
||||
@@ -101,10 +97,6 @@ suite('Number Fields', function() {
|
||||
var numberField = createNumberFieldSameValuesJson(undefined);
|
||||
assertNumberFieldDefault(numberField);
|
||||
});
|
||||
test('Non-Parsable String', function() {
|
||||
var numberField = createNumberFieldSameValuesJson('bad');
|
||||
assertNumberFieldDefault(numberField);
|
||||
});
|
||||
test('NaN', function() {
|
||||
var numberField = createNumberFieldSameValuesJson(NaN);
|
||||
assertNumberFieldDefault(numberField);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
|
||||
goog.require('Blockly.Blocks.procedures');
|
||||
goog.require('Blockly.Msg.en');
|
||||
goog.require('Blockly.Msg');
|
||||
|
||||
suite('Procedures', function() {
|
||||
setup(function() {
|
||||
|
||||
@@ -1443,13 +1443,6 @@ h1 {
|
||||
<block type="test_validators_colour_force_red"></block>
|
||||
<sep gap="12"></sep>
|
||||
<block type="test_validators_colour_red_null"></block>
|
||||
<label text="Dates"></label>
|
||||
<sep gap="12"></sep>
|
||||
<block type="test_validators_date_null"></block>
|
||||
<sep gap="12"></sep>
|
||||
<block type="test_validators_date_force_20s"></block>
|
||||
<sep gap="12"></sep>
|
||||
<block type="test_validators_date_20s_null"></block>
|
||||
<label text="Dropdowns"></label>
|
||||
<sep gap="12"></sep>
|
||||
<block type="test_validators_dropdown_null"></block>
|
||||
|
||||
Reference in New Issue
Block a user