]> git.wh0rd.org Git - tt-rss.git/blob - lib/dijit/form/NumberTextBox.js
upgrade Dojo to 1.6.1
[tt-rss.git] / lib / dijit / form / NumberTextBox.js
1 /*
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
5 */
6
7
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");
13
14
15 /*=====
16 dojo.declare(
17         "dijit.form.NumberTextBox.__Constraints",
18         [dijit.form.RangeBoundTextBox.__Constraints, dojo.number.__FormatOptions, dojo.number.__ParseOptions], {
19         // summary:
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.
23         // example:
24         //              Minimum/maximum:
25         //              To specify a field between 0 and 120:
26         //      |               {min:0,max: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:
30         //      |               {places:'0,3'}
31 });
32 =====*/
33
34 dojo.declare("dijit.form.NumberTextBoxMixin",
35         null,
36         {
37                 // summary:
38                 //              A mixin for all number textboxes
39                 // tags:
40                 //              protected
41
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,
45
46                 /*=====
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.
52                 constraints: {},
53                 ======*/
54
55                 // value: Number
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).
60                 //
61                 //              Symmetrically, set('value', NaN) will clear the displayed value,
62                 //              whereas set('value', undefined) will have no effect.
63                 value: NaN,
64
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: '#.######' },
70
71                 /*=====
72                 _formatter: function(value, options){
73                         // summary:
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".
76                         // value: Number
77                         //              The number to be converted into a string.
78                         // options: dojo.number.__FormatOptions?
79                         //              Formatting options
80                         // tags:
81                         //              protected extension
82
83                         return "12345";         // String
84                 },
85                  =====*/
86                 _formatter: dojo.number.format,
87
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);
93                         }
94                         if(typeof constraints.min != "number"){
95                                 constraints.min = -9 * Math.pow(10, 15-places);
96                         }
97                         this.inherited(arguments, [ constraints ]);
98                         if(this.focusNode && this.focusNode.value && !isNaN(this.value)){
99                                 this.set('value', this.value);
100                         }
101                 },
102
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;
110                                 }
111                         }
112                         this.inherited(arguments);
113                 },
114
115                 format: function(/*Number*/ value, /*dojo.number.__FormatOptions*/ constraints){
116                         // summary:
117                         //              Formats the value as a Number, according to constraints.
118                         // tags:
119                         //              protected
120
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;
127                         }
128                         if(this.editOptions && this._focused){
129                                 constraints = dojo.mixin({}, constraints, this.editOptions);
130                         }
131                         return this._formatter(value, constraints);
132                 },
133
134                 /*=====
135                 _parser: function(value, constraints){
136                         // summary:
137                         //              Parses the string value as a Number, according to constraints.
138                         // value: String
139                         //              String representing a number
140                         // constraints: dojo.number.__ParseOptions
141                         //              Formatting options
142                         // tags:
143                         //              protected
144
145                         return 123.45;          // Number
146                 },
147                 =====*/
148                 _parser: dojo.number.parse,
149
150                 parse: function(/*String*/ value, /*dojo.number.__FormatOptions*/ constraints){
151                         // summary:
152                         //              Replacable function to convert a formatted string to a number value
153                         // tags:
154                         //              protected extension
155
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
159                         }
160                         return v;
161                 },
162
163                 _getDisplayedValueAttr: function(){
164                         var v = this.inherited(arguments);
165                         return isNaN(v) ? this.textbox.value : v;
166                 },
167
168                 filter: function(/*Number*/ value){
169                         // summary:
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.
173                         //
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)
176                 },
177
178                 serialize: function(/*Number*/ value, /*Object?*/ options){
179                         // summary:
180                         //              Convert value (a Number) into a canonical string (ie, how the number literal is written in javascript/java/C/etc.)
181                         // tags:
182                         //              protected
183                         return (typeof value != "number" || isNaN(value)) ? '' : this.inherited(arguments);
184                 },
185
186                 _setBlurValue: function(){
187                         var val = dojo.hitch(dojo.mixin({}, this, { _focused: true }), "get")('value'); // parse with editOptions
188                         this._setValueAttr(val, true);
189                 },
190
191                 _setValueAttr: function(/*Number*/ value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
192                         // summary:
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
201                                         }
202                                 }else if(!value){ // 0 processed in if branch above, ''|null|undefined flow thru here
203                                         formattedValue = '';
204                                         value = NaN;
205                                 }else{ // non-numeric values
206                                         value = undefined;
207                                 }
208                         }
209                         this.inherited(arguments, [value, priorityChange, formattedValue]);
210                 },
211
212                 _getValueAttr: function(){
213                         // summary:
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
217
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)
226                                 }else{
227                                         return undefined; // gibberish
228                                 }
229                         }else{
230                                 return v; // Number or NaN for ''
231                         }
232                 },
233
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);
239                         }else{
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
244                                         }else{
245                                                 return this.inherited(arguments);
246                                         }
247                                 }else{
248                                         return false;
249                                 }
250                         }
251                 }
252         }
253 );
254
255 dojo.declare("dijit.form.NumberTextBox",
256         [dijit.form.RangeBoundTextBox,dijit.form.NumberTextBoxMixin],
257         {
258                 // summary:
259                 //              A TextBox for entering numbers, with formatting and range checking
260                 // description:
261                 //              NumberTextBox is a textbox for entering and displaying numbers, supporting
262                 //              the following main features:
263                 //
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).
273
274                 baseClass: "dijitTextBox dijitNumberTextBox"
275         }
276 );
277
278 }