Files
blockly/core/field_number.js
Neil Fraser e77a9b7e81 Change parseFloat() to Number()
Number() is a bit less forgiving than parseFloat() and is more likely to generate NaN rather than some random number.  An audit of each case shows nowhere that parseFloat()’s features are needed.
2019-08-01 11:36:58 -07:00

142 lines
4.8 KiB
JavaScript

/**
* @license
* Visual Blocks Editor
*
* Copyright 2016 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.
*/
/**
* @fileoverview Number input field
* @author fenichel@google.com (Rachel Fenichel)
*/
'use strict';
goog.provide('Blockly.FieldNumber');
goog.require('Blockly.FieldTextInput');
/**
* Class for an editable number field.
* @param {string|number=} opt_value The initial value of the field. Should cast
* to a number. Defaults to 0.
* @param {(string|number)=} opt_min Minimum value.
* @param {(string|number)=} opt_max Maximum value.
* @param {(string|number)=} opt_precision Precision for value.
* @param {Function=} opt_validator A function that is called to validate
* changes to the field's value. Takes in a number & returns a validated
* number, or null to abort the change.
* @extends {Blockly.FieldTextInput}
* @constructor
*/
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;
}
Blockly.FieldNumber.superClass_.constructor.call(
this, opt_value, opt_validator);
};
goog.inherits(Blockly.FieldNumber, Blockly.FieldTextInput);
/**
* Construct a FieldNumber from a JSON arg object.
* @param {!Object} options A JSON object with options (value, min, max, and
* precision).
* @return {!Blockly.FieldNumber} The new field instance.
* @package
* @nocollapse
*/
Blockly.FieldNumber.fromJson = function(options) {
return new Blockly.FieldNumber(options['value'],
options['min'], options['max'], options['precision']);
};
/**
* Serializable fields are saved by the XML renderer, non-serializable fields
* are not. Editable fields should also be serializable.
* @type {boolean}
* @const
*/
Blockly.FieldNumber.prototype.SERIALIZABLE = true;
/**
* Set the maximum, minimum and precision constraints on this field.
* Any of these properties may be undefined or NaN to be disabled.
* Setting precision (usually a power of 10) enforces a minimum step between
* values. That is, the user's value will rounded to the closest multiple of
* precision. The least significant digit place is inferred from the precision.
* Integers values can be enforces by choosing an integer precision.
* @param {number|string|undefined} min Minimum value.
* @param {number|string|undefined} max Maximum value.
* @param {number|string|undefined} precision Precision for value.
*/
Blockly.FieldNumber.prototype.setConstraints = function(min, max, precision) {
precision = Number(precision);
this.precision_ = isNaN(precision) ? 0 : 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;
max = Number(max);
this.max_ = isNaN(max) ? Infinity : 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.
* @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) {
return null;
}
// Clean up text.
var newValue = String(opt_newValue);
// TODO: Handle cases like 'ten', '1.203,14', etc.
// 'O' is sometimes mistaken for '0' by inexperienced users.
newValue = newValue.replace(/O/ig, '0');
// Strip out thousands separators.
newValue = newValue.replace(/,/g, '');
// Clean up number.
var n = Number(newValue || 0);
if (isNaN(n)) {
// Invalid number.
return null;
}
// Get the value in range.
n = Math.min(Math.max(n, this.min_), this.max_);
// Round to nearest multiple of precision.
if (this.precision_ && isFinite(n)) {
n = Math.round(n / this.precision_) * this.precision_;
}
// Clean up floating point errors.
n = (this.fractionalDigits_ == -1) ? n :
Number(n.toFixed(this.fractionalDigits_));
return n;
};
Blockly.Field.register('field_number', Blockly.FieldNumber);