2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
8 if(!dojo._hasResource["dijit.form.NumberTextBox"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo._hasResource["dijit.form.NumberTextBox"] = true;
10 dojo.provide("dijit.form.NumberTextBox");
11 dojo.require("dijit.form.ValidationTextBox");
12 dojo.require("dojo.number");
17 "dijit.form.NumberTextBox.__Constraints",
18 [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], {
20 // Specifies both the rules on valid/invalid values (minimum, maximum,
21 // number of required decimal places), and also formatting options for
22 // displaying the value when the field is not focused.
25 // To specify a field between 0 and 120:
27 // To specify a field that must be an integer:
28 // | {fractional:false}
29 // To specify a field where 0 to 3 decimal places are allowed on input:
34 dojo.declare("dijit.form.NumberTextBoxMixin",
38 // A mixin for all number textboxes
42 // Override ValidationTextBox.regExpGen().... we use a reg-ex generating function rather
43 // than a straight regexp to deal with locale (plus formatting options too?)
44 regExpGen: dojo.number.regexp,
47 // constraints: dijit.form.NumberTextBox.__Constraints
48 // Despite the name, this parameter specifies both constraints on the input
49 // (including minimum/maximum allowed values) as well as
50 // formatting options like places (the number of digits to display after
51 // the decimal point). See `dijit.form.NumberTextBox.__Constraints` for details.
56 // The value of this NumberTextBox as a Javascript Number (i.e., not a String).
57 // If the displayed value is blank, the value is NaN, and if the user types in
58 // an gibberish value (like "hello world"), the value is undefined
59 // (i.e. get('value') returns undefined).
61 // Symmetrically, set('value', NaN) will clear the displayed value,
62 // whereas set('value', undefined) will have no effect.
65 // editOptions: [protected] Object
66 // Properties to mix into constraints when the value is being edited.
67 // This is here because we edit the number in the format "12345", which is
68 // different than the display value (ex: "12,345")
69 editOptions: { pattern: '#.######' },
72 _formatter: function(value, options){
74 // _formatter() is called by format(). It's the base routine for formatting a number,
75 // as a string, for example converting 12345 into "12,345".
77 // The number to be converted into a string.
78 // options: dojo.number.__FormatOptions?
81 // protected extension
83 return "12345"; // String
86 _formatter: dojo.number.format,
88 _setConstraintsAttr: function(/*Object*/ constraints){
89 var places = typeof constraints.places == "number"? constraints.places : 0;
90 if(places){ places++; } // decimal rounding errors take away another digit of precision
91 if(typeof constraints.max != "number"){
92 constraints.max = 9 * Math.pow(10, 15-places);
94 if(typeof constraints.min != "number"){
95 constraints.min = -9 * Math.pow(10, 15-places);
97 this.inherited(arguments, [ constraints ]);
98 if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
99 this.set('value', this.value);
103 _onFocus: function(){
104 if(this.disabled){ return; }
105 var val = this.get('value');
106 if(typeof val == "number" && !isNaN(val)){
107 var formattedValue = this.format(val, this.constraints);
108 if(formattedValue !== undefined){
109 this.textbox.value = formattedValue;
112 this.inherited(arguments);
115 format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
117 // Formats the value as a Number, according to constraints.
121 var formattedValue = String(value);
122 if(typeof value != "number"){ return formattedValue; }
123 if(isNaN(value)){ return ""; }
124 // check for exponential notation that dojo.number.format chokes on
125 if(!("rangeCheck" in this && this.rangeCheck(value, constraints)) && constraints.exponent !== false && /\de[-+]?\d/i.test(formattedValue)){
126 return formattedValue;
128 if(this.editOptions && this._focused){
129 constraints = dojo.mixin({}, constraints, this.editOptions);
131 return this._formatter(value, constraints);
135 _parser: function(value, constraints){
137 // Parses the string value as a Number, according to constraints.
139 // String representing a number
140 // constraints: dojo.number.__ParseOptions
141 // Formatting options
145 return 123.45; // Number
148 _parser: dojo.number.parse,
150 parse: function(/*String*/ value, /*dojo.number.__FormatOptions*/ constraints){
152 // Replacable function to convert a formatted string to a number value
154 // protected extension
156 var v = this._parser(value, dojo.mixin({}, constraints, (this.editOptions && this._focused) ? this.editOptions : {}));
157 if(this.editOptions && this._focused && isNaN(v)){
158 v = this._parser(value, constraints); // parse w/o editOptions: not technically needed but is nice for the user
163 _getDisplayedValueAttr: function(){
164 var v = this.inherited(arguments);
165 return isNaN(v) ? this.textbox.value : v;
168 filter: function(/*Number*/ value){
170 // This is called with both the display value (string), and the actual value (a number).
171 // When called with the actual value it does corrections so that '' etc. are represented as NaN.
172 // Otherwise it dispatches to the superclass's filter() method.
174 // See `dijit.form.TextBox.filter` for more details.
175 return (value === null || value === '' || value === undefined) ? NaN : this.inherited(arguments); // set('value', null||''||undefined) should fire onChange(NaN)
178 serialize: function(/*Number*/ value, /*Object?*/ options){
180 // Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
183 return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
186 _setBlurValue: function(){
187 var val = dojo.hitch(dojo.mixin({}, this, { _focused: true }), "get")('value'); // parse with editOptions
188 this._setValueAttr(val, true);
191 _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
193 // Hook so set('value', ...) works.
194 if(value !== undefined && formattedValue === undefined){
195 formattedValue = String(value);
196 if(typeof value == "number"){
197 if(isNaN(value)){ formattedValue = '' }
198 // check for exponential notation that dojo.number.format chokes on
199 else if(("rangeCheck" in this && this.rangeCheck(value, this.constraints)) || this.constraints.exponent === false || !/\de[-+]?\d/i.test(formattedValue)){
200 formattedValue = undefined; // lets format comnpute a real string value
202 }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here
205 }else{ // non-numeric values
209 this.inherited(arguments, [value, priorityChange, formattedValue]);
212 _getValueAttr: function(){
214 // Hook so get('value') works.
215 // Returns Number, NaN for '', or undefined for unparsable text
216 var v = this.inherited(arguments); // returns Number for all values accepted by parse() or NaN for all other displayed values
218 // If the displayed value of the textbox is gibberish (ex: "hello world"), this.inherited() above
219 // returns NaN; this if() branch converts the return value to undefined.
220 // Returning undefined prevents user text from being overwritten when doing _setValueAttr(_getValueAttr()).
221 // A blank displayed value is still returned as NaN.
222 if(isNaN(v) && this.textbox.value !== ''){
223 if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value) && (new RegExp("^"+dojo.number._realNumberRegexp(dojo.mixin({}, this.constraints))+"$").test(this.textbox.value))){ // check for exponential notation that parse() rejected (erroneously?)
224 var n = Number(this.textbox.value);
225 return isNaN(n) ? undefined : n; // return exponential Number or undefined for random text (may not be possible to do with the above RegExp check)
227 return undefined; // gibberish
230 return v; // Number or NaN for ''
234 isValid: function(/*Boolean*/ isFocused){
235 // Overrides dijit.form.RangeBoundTextBox.isValid to check that the editing-mode value is valid since
236 // it may not be formatted according to the regExp vaidation rules
237 if(!this._focused || this._isEmpty(this.textbox.value)){
238 return this.inherited(arguments);
240 var v = this.get('value');
241 if(!isNaN(v) && this.rangeCheck(v, this.constraints)){
242 if(this.constraints.exponent !== false && /\de[-+]?\d/i.test(this.textbox.value)){ // exponential, parse doesn't like it
243 return true; // valid exponential number in range
245 return this.inherited(arguments);
255 dojo.declare("dijit.form.NumberTextBox",
256 [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
259 // A TextBox for entering numbers, with formatting and range checking
261 // NumberTextBox is a textbox for entering and displaying numbers, supporting
262 // the following main features:
264 // 1. Enforce minimum/maximum allowed values (as well as enforcing that the user types
265 // a number rather than a random string)
266 // 2. NLS support (altering roles of comma and dot as "thousands-separator" and "decimal-point"
267 // depending on locale).
268 // 3. Separate modes for editing the value and displaying it, specifically that
269 // the thousands separator character (typically comma) disappears when editing
270 // but reappears after the field is blurred.
271 // 4. Formatting and constraints regarding the number of places (digits after the decimal point)
272 // allowed on input, and number of places displayed when blurred (see `constraints` parameter).
274 baseClass: "dijitTextBox dijitNumberTextBox"