3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @docauthor Jason Johnston <jason@sencha.com>
18 * A numeric text field that provides automatic keystroke filtering to disallow non-numeric characters,
19 * and numeric validation to limit the value to a range of valid numbers. The range of acceptable number
20 * values can be controlled by setting the {@link #minValue} and {@link #maxValue} configs, and fractional
21 * decimals can be disallowed by setting {@link #allowDecimals} to `false`.
23 * By default, the number field is also rendered with a set of up/down spinner buttons and has
24 * up/down arrow key and mouse wheel event listeners attached for incrementing/decrementing the value by the
25 * {@link #step} value. To hide the spinner buttons set `{@link #hideTrigger hideTrigger}:true`; to disable
26 * the arrow key and mouse wheel handlers set `{@link #keyNavEnabled keyNavEnabled}:false` and
27 * `{@link #mouseWheelEnabled mouseWheelEnabled}:false`. See the example below.
32 * Ext.create('Ext.form.Panel', {
33 * title: 'On The Wall',
36 * renderTo: Ext.getBody(),
38 * xtype: 'numberfield',
41 * fieldLabel: 'Bottles of Beer',
47 * text: 'Take one down, pass it around',
48 * handler: function() {
49 * this.up('form').down('[name=bottles]').spinDown();
54 * # Removing UI Enhancements
57 * Ext.create('Ext.form.Panel', {
58 * title: 'Personal Info',
61 * renderTo: Ext.getBody(),
63 * xtype: 'numberfield',
67 * minValue: 0, //prevents negative numbers
69 * // Remove spinner buttons, and arrow key and mouse wheel listeners
71 * keyNavEnabled: false,
72 * mouseWheelEnabled: false
79 * Ext.create('Ext.form.Panel', {
80 * renderTo: Ext.getBody(),
85 * xtype: 'numberfield',
88 * fieldLabel: 'Even Numbers',
90 * // Set step so it skips every other number
94 * // Add change handler to force user-entered numbers to evens
96 * change: function(field, value) {
97 * value = parseInt(value, 10);
98 * field.setValue(value + value % 2);
104 Ext.define('Ext.form.field.Number', {
105 extend:'Ext.form.field.Spinner',
106 alias: 'widget.numberfield',
107 alternateClassName: ['Ext.form.NumberField', 'Ext.form.Number'],
110 * @cfg {RegExp} stripCharsRe @hide
113 * @cfg {RegExp} maskRe @hide
117 * @cfg {Boolean} allowDecimals
118 * False to disallow decimal values
120 allowDecimals : true,
123 * @cfg {String} decimalSeparator
124 * Character(s) to allow as the decimal separator
126 decimalSeparator : '.',
129 * @cfg {Number} decimalPrecision
130 * The maximum precision to display after the decimal separator
132 decimalPrecision : 2,
135 * @cfg {Number} minValue
136 * The minimum allowed value (defaults to Number.NEGATIVE_INFINITY). Will be used by the field's validation logic,
137 * and for {@link Ext.form.field.Spinner#setSpinUpEnabled enabling/disabling the down spinner button}.
139 minValue: Number.NEGATIVE_INFINITY,
142 * @cfg {Number} maxValue
143 * The maximum allowed value (defaults to Number.MAX_VALUE). Will be used by the field's validation logic, and for
144 * {@link Ext.form.field.Spinner#setSpinUpEnabled enabling/disabling the up spinner button}.
146 maxValue: Number.MAX_VALUE,
150 * Specifies a numeric interval by which the field's value will be incremented or decremented when the user invokes
156 * @cfg {String} minText
157 * Error text to display if the minimum value validation fails.
159 minText : 'The minimum value for this field is {0}',
162 * @cfg {String} maxText
163 * Error text to display if the maximum value validation fails.
165 maxText : 'The maximum value for this field is {0}',
168 * @cfg {String} nanText
169 * Error text to display if the value is not a valid number. For example, this can happen if a valid character like
170 * '.' or '-' is left in the field with no number.
172 nanText : '{0} is not a valid number',
175 * @cfg {String} negativeText
176 * Error text to display if the value is negative and {@link #minValue} is set to 0. This is used instead of the
177 * {@link #minText} in that circumstance only.
179 negativeText : 'The value cannot be negative',
182 * @cfg {String} baseChars
183 * The base set of characters to evaluate as valid numbers.
185 baseChars : '0123456789',
188 * @cfg {Boolean} autoStripChars
189 * True to automatically strip not allowed characters from the field.
191 autoStripChars: false,
193 initComponent: function() {
199 me.setMinValue(me.minValue);
200 me.setMaxValue(me.maxValue);
202 // Build regexes for masking and stripping based on the configured options
203 if (me.disableKeyFilter !== true) {
204 allowed = me.baseChars + '';
205 if (me.allowDecimals) {
206 allowed += me.decimalSeparator;
208 if (me.minValue < 0) {
211 allowed = Ext.String.escapeRegex(allowed);
212 me.maskRe = new RegExp('[' + allowed + ']');
213 if (me.autoStripChars) {
214 me.stripCharsRe = new RegExp('[^' + allowed + ']', 'gi');
220 * Runs all of Number's validations and returns an array of any errors. Note that this first runs Text's
221 * validations, so the returned array is an amalgamation of all field errors. The additional validations run test
222 * that the value is a number, and that it is within the configured min and max values.
223 * @param {Object} [value] The value to get errors for (defaults to the current field value)
224 * @return {String[]} All validation errors for this field
226 getErrors: function(value) {
228 errors = me.callParent(arguments),
229 format = Ext.String.format,
232 value = Ext.isDefined(value) ? value : this.processRawValue(this.getRawValue());
234 if (value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
238 value = String(value).replace(me.decimalSeparator, '.');
241 errors.push(format(me.nanText, value));
244 num = me.parseValue(value);
246 if (me.minValue === 0 && num < 0) {
247 errors.push(this.negativeText);
249 else if (num < me.minValue) {
250 errors.push(format(me.minText, me.minValue));
253 if (num > me.maxValue) {
254 errors.push(format(me.maxText, me.maxValue));
261 rawToValue: function(rawValue) {
262 var value = this.fixPrecision(this.parseValue(rawValue));
263 if (value === null) {
264 value = rawValue || null;
269 valueToRaw: function(value) {
271 decimalSeparator = me.decimalSeparator;
272 value = me.parseValue(value);
273 value = me.fixPrecision(value);
274 value = Ext.isNumber(value) ? value : parseFloat(String(value).replace(decimalSeparator, '.'));
275 value = isNaN(value) ? '' : String(value).replace('.', decimalSeparator);
279 onChange: function() {
281 value = me.getValue(),
282 valueIsNull = value === null;
284 me.callParent(arguments);
286 // Update the spinner buttons
287 me.setSpinUpEnabled(valueIsNull || value < me.maxValue);
288 me.setSpinDownEnabled(valueIsNull || value > me.minValue);
292 * Replaces any existing {@link #minValue} with the new value.
293 * @param {Number} value The minimum value
295 setMinValue : function(value) {
296 this.minValue = Ext.Number.from(value, Number.NEGATIVE_INFINITY);
300 * Replaces any existing {@link #maxValue} with the new value.
301 * @param {Number} value The maximum value
303 setMaxValue: function(value) {
304 this.maxValue = Ext.Number.from(value, Number.MAX_VALUE);
308 parseValue : function(value) {
309 value = parseFloat(String(value).replace(this.decimalSeparator, '.'));
310 return isNaN(value) ? null : value;
316 fixPrecision : function(value) {
319 precision = me.decimalPrecision;
322 return nan ? '' : value;
323 } else if (!me.allowDecimals || precision <= 0) {
327 return parseFloat(Ext.Number.toFixed(parseFloat(value), precision));
330 beforeBlur : function() {
332 v = me.parseValue(me.getRawValue());
334 if (!Ext.isEmpty(v)) {
339 onSpinUp: function() {
342 me.setValue(Ext.Number.constrain(me.getValue() + me.step, me.minValue, me.maxValue));
346 onSpinDown: function() {
349 me.setValue(Ext.Number.constrain(me.getValue() - me.step, me.minValue, me.maxValue));