Roo/bootstrap/NumberField.js
[roojs1] / Roo / bootstrap / NumberField.js
1 /*
2  * - LGPL
3  *
4  * Input
5  * 
6  */
7
8 /**
9  * @class Roo.bootstrap.NumberField
10  * @extends Roo.bootstrap.Input
11  * Bootstrap NumberField class
12  * 
13  * 
14  * 
15  * 
16  * @constructor
17  * Create a new NumberField
18  * @param {Object} config The config object
19  */
20
21 Roo.bootstrap.NumberField = function(config){
22     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
23 };
24
25 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
26     
27     /**
28      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
29      */
30     allowDecimals : true,
31     /**
32      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33      */
34     decimalSeparator : ".",
35     /**
36      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
37      */
38     decimalPrecision : 2,
39     /**
40      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41      */
42     allowNegative : true,
43     /**
44      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
45      */
46     minValue : Number.NEGATIVE_INFINITY,
47     /**
48      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
49      */
50     maxValue : Number.MAX_VALUE,
51     /**
52      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
53      */
54     minText : "The minimum value for this field is {0}",
55     /**
56      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
57      */
58     maxText : "The maximum value for this field is {0}",
59     /**
60      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
61      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
62      */
63     nanText : "{0} is not a valid number",
64     /**
65      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
66      */
67     castInt : true,
68     /**
69      * @cfg {Boolean} allowThousandsDelimiter (true|false) display thousands delimiter if true (e.g. "100,000") (defalut false)
70      */
71     allowThousandsDelimiter : false,
72     /**
73      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
74      */
75     thousandsDelimiter : ",",
76     /**
77      * @cfg {String} valueAlign alignment of value
78      */
79     valueAlign : "left",
80
81     getAutoCreate : function()
82     {
83         var hiddenInput = {
84             tag: 'input',
85             type: 'hidden',
86             id: Roo.id(),
87             cls: 'hidden-number-input'
88         };
89         
90         if (this.name) {
91             hiddenInput.name = this.name;
92         }
93         
94         //display field
95         var inputConfig = this;
96         
97         inputConfig.name = '';
98         
99         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
100         
101         this.name = hiddenInput.name;
102         
103         if(cfg.cn.length > 0) {
104             cfg.cn.push(hiddenInput);
105         }
106         
107         return cfg;
108     },
109
110     // private
111     initEvents : function()
112     {   
113         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
114         
115         var allowed = "0123456789";
116         
117         if(this.allowDecimals){
118             allowed += this.decimalSeparator;
119         }
120         
121         if(this.allowNegative){
122             allowed += "-";
123         }
124         
125         if(this.allowThousandsDelimiter) {
126             allowed += this.thousandsDelimiter;
127         }
128         
129         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
130         
131         var keyPress = function(e){
132             
133             var k = e.getKey();
134             
135             var c = e.getCharCode();
136             
137             if(
138                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
139                     allowed.indexOf(String.fromCharCode(c)) === -1
140             ){
141                 e.stopEvent();
142                 return;
143             }
144             
145             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
146                 return;
147             }
148             
149             if(allowed.indexOf(String.fromCharCode(c)) === -1){
150                 e.stopEvent();
151             }
152         };
153         
154         this.el.on("keypress", keyPress, this);
155     },
156     
157     validateValue : function(value)
158     {
159         
160         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
161             return false;
162         }
163         
164         var num = this.parseValue(value);
165         
166         if(isNaN(num)){
167             this.markInvalid(String.format(this.nanText, value));
168             return false;
169         }
170         
171         if(num < this.minValue){
172             this.markInvalid(String.format(this.minText, this.minValue));
173             return false;
174         }
175         
176         if(num > this.maxValue){
177             this.markInvalid(String.format(this.maxText, this.maxValue));
178             return false;
179         }
180         
181         return true;
182     },
183
184     getValue : function()
185     {
186         var v = this.hiddenEl().getValue();
187         
188         return this.fixPrecision(this.parseValue(v));
189     },
190
191     parseValue : function(value)
192     {
193         if(this.allowThousandsDelimiter) {
194             value += "";
195             r = new RegExp(this.thousandsDelimiter, "g");
196             value = value.replace(r, "");
197         }
198         
199         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
200         return isNaN(value) ? '' : value;
201     },
202
203     fixPrecision : function(value)
204     {
205         if(this.allowThousandsDelimiter) {
206             value += "";
207             r = new RegExp(this.thousandsDelimiter, "g");
208             value = value.replace(r, "");
209         }
210         
211         var nan = isNaN(value);
212         
213         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
214             return nan ? '' : value;
215         }
216         return parseFloat(value).toFixed(this.decimalPrecision);
217     },
218
219     setValue : function(v)
220     {
221         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
222         
223         this.value = v;
224         
225         if(this.rendered){
226             
227             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
228             
229             this.inputEl().dom.value = (this.allowThousandsDelimiter ? this.addThousandsDelimiter(v) : v);
230             
231             this.validate();
232         }
233     },
234
235     decimalPrecisionFcn : function(v)
236     {
237         return Math.floor(v);
238     },
239
240     beforeBlur : function()
241     {
242         if(!this.castInt){
243             return;
244         }
245         
246         var v = this.parseValue(this.getRawValue());
247         if(v){
248             this.setValue(v);
249         }
250     },
251     
252     addThousandsDelimiter : function(v)
253     {
254         if(!this.allowThousandsDelimiter) {
255             return v;
256         }
257         
258         v += "";
259         
260         var x = v.split(".");
261         
262         var x1 = x[0];
263         
264         var x2 = x.length > 1 ? "." + x[1] : "";
265         
266         var rgx = /(\d+)(\d{3})/;
267         
268         while (rgx.test(x1)) {
269             x1 = x1.replace(rgx, "$1" + this.thousandsDelimiter + "$2");
270         }
271         
272         return x1 + x2;
273     },
274     
275     hiddenEl : function()
276     {
277         return this.el.select('input.hidden-number-input',true).first();
278     }
279     
280 });
281
282