Roo/bootstrap/Input.js
[roojs1] / Roo / bootstrap / Input.js
1 /*
2  * - LGPL
3  *
4  * Input
5  * 
6  */
7
8 /**
9  * @class Roo.bootstrap.Input
10  * @extends Roo.bootstrap.Component
11  * Bootstrap Input class
12  * @cfg {Boolean} disabled is it disabled
13  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
14  * @cfg {String} name name of the input
15  * @cfg {string} fieldLabel - the label associated
16  * @cfg {string} placeholder - placeholder to put in text.
17  * @cfg {string}  before - input group add on before
18  * @cfg {string} after - input group add on after
19  * @cfg {string} size - (lg|sm) or leave empty..
20  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
21  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
22  * @cfg {Number} md colspan out of 12 for computer-sized screens
23  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
24  * @cfg {string} value default value of the input
25  * @cfg {Number} labelWidth set the width of label 
26  * @cfg {Number} labellg set the width of label (1-12)
27  * @cfg {Number} labelmd set the width of label (1-12)
28  * @cfg {Number} labelsm set the width of label (1-12)
29  * @cfg {Number} labelxs set the width of label (1-12)
30  * @cfg {String} labelAlign (top|left)
31  * @cfg {Boolean} readOnly Specifies that the field should be read-only
32  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
33  * @cfg {String} indicatorpos (left|right) default left
34  * @cfg {String} capture (user|camera) use for file input only. (default empty)
35
36  * @cfg {String} align (left|center|right) Default left
37  * @cfg {Boolean} forceFeedback (true|false) Default false
38  * 
39  * @constructor
40  * Create a new Input
41  * @param {Object} config The config object
42  */
43
44 Roo.bootstrap.Input = function(config){
45     
46     Roo.bootstrap.Input.superclass.constructor.call(this, config);
47     
48     this.addEvents({
49         /**
50          * @event focus
51          * Fires when this field receives input focus.
52          * @param {Roo.form.Field} this
53          */
54         focus : true,
55         /**
56          * @event blur
57          * Fires when this field loses input focus.
58          * @param {Roo.form.Field} this
59          */
60         blur : true,
61         /**
62          * @event specialkey
63          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
64          * {@link Roo.EventObject#getKey} to determine which key was pressed.
65          * @param {Roo.form.Field} this
66          * @param {Roo.EventObject} e The event object
67          */
68         specialkey : true,
69         /**
70          * @event change
71          * Fires just before the field blurs if the field value has changed.
72          * @param {Roo.form.Field} this
73          * @param {Mixed} newValue The new value
74          * @param {Mixed} oldValue The original value
75          */
76         change : true,
77         /**
78          * @event invalid
79          * Fires after the field has been marked as invalid.
80          * @param {Roo.form.Field} this
81          * @param {String} msg The validation message
82          */
83         invalid : true,
84         /**
85          * @event valid
86          * Fires after the field has been validated with no errors.
87          * @param {Roo.form.Field} this
88          */
89         valid : true,
90          /**
91          * @event keyup
92          * Fires after the key up
93          * @param {Roo.form.Field} this
94          * @param {Roo.EventObject}  e The event Object
95          */
96         keyup : true
97     });
98 };
99
100 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
101      /**
102      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
103       automatic validation (defaults to "keyup").
104      */
105     validationEvent : "keyup",
106      /**
107      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
108      */
109     validateOnBlur : true,
110     /**
111      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
112      */
113     validationDelay : 250,
114      /**
115      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
116      */
117     focusClass : "x-form-focus",  // not needed???
118     
119        
120     /**
121      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
122      */
123     invalidClass : "has-warning",
124     
125     /**
126      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
127      */
128     validClass : "has-success",
129     
130     /**
131      * @cfg {Boolean} hasFeedback (true|false) default true
132      */
133     hasFeedback : true,
134     
135     /**
136      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
137      */
138     invalidFeedbackClass : "glyphicon-warning-sign",
139     
140     /**
141      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
142      */
143     validFeedbackClass : "glyphicon-ok",
144     
145     /**
146      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
147      */
148     selectOnFocus : false,
149     
150      /**
151      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
152      */
153     maskRe : null,
154        /**
155      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
156      */
157     vtype : null,
158     
159       /**
160      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
161      */
162     disableKeyFilter : false,
163     
164        /**
165      * @cfg {Boolean} disabled True to disable the field (defaults to false).
166      */
167     disabled : false,
168      /**
169      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
170      */
171     allowBlank : true,
172     /**
173      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
174      */
175     blankText : "Please complete this mandatory field",
176     
177      /**
178      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
179      */
180     minLength : 0,
181     /**
182      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
183      */
184     maxLength : Number.MAX_VALUE,
185     /**
186      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
187      */
188     minLengthText : "The minimum length for this field is {0}",
189     /**
190      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
191      */
192     maxLengthText : "The maximum length for this field is {0}",
193   
194     
195     /**
196      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
197      * If available, this function will be called only after the basic validators all return true, and will be passed the
198      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
199      */
200     validator : null,
201     /**
202      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
203      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
204      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
205      */
206     regex : null,
207     /**
208      * @cfg {String} regexText -- Depricated - use Invalid Text
209      */
210     regexText : "",
211     
212     /**
213      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
214      */
215     invalidText : "",
216     
217     
218     
219     autocomplete: false,
220     
221     
222     fieldLabel : '',
223     inputType : 'text',
224     
225     name : false,
226     placeholder: false,
227     before : false,
228     after : false,
229     size : false,
230     hasFocus : false,
231     preventMark: false,
232     isFormField : true,
233     value : '',
234     labelWidth : 2,
235     labelAlign : false,
236     readOnly : false,
237     align : false,
238     formatedValue : false,
239     forceFeedback : false,
240     
241     indicatorpos : 'left',
242     
243     labellg : 0,
244     labelmd : 0,
245     labelsm : 0,
246     labelxs : 0,
247     
248     capture : '',
249     
250     parentLabelAlign : function()
251     {
252         var parent = this;
253         while (parent.parent()) {
254             parent = parent.parent();
255             if (typeof(parent.labelAlign) !='undefined') {
256                 return parent.labelAlign;
257             }
258         }
259         return 'left';
260         
261     },
262     
263     getAutoCreate : function()
264     {
265         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
266         
267         var id = Roo.id();
268         
269         var cfg = {};
270         
271         if(this.inputType != 'hidden'){
272             cfg.cls = 'form-group' //input-group
273         }
274         
275         var input =  {
276             tag: 'input',
277             id : id,
278             type : this.inputType,
279             value : this.value,
280             cls : 'form-control',
281             placeholder : this.placeholder || '',
282             autocomplete : this.autocomplete || 'new-password'
283         };
284         
285         if(this.capture.length){
286             input.capture = this.capture;
287         }
288         
289         if(this.align){
290             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
291         }
292         
293         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
294             input.maxLength = this.maxLength;
295         }
296         
297         if (this.disabled) {
298             input.disabled=true;
299         }
300         
301         if (this.readOnly) {
302             input.readonly=true;
303         }
304         
305         if (this.name) {
306             input.name = this.name;
307         }
308         
309         if (this.size) {
310             input.cls += ' input-' + this.size;
311         }
312         
313         var settings=this;
314         ['xs','sm','md','lg'].map(function(size){
315             if (settings[size]) {
316                 cfg.cls += ' col-' + size + '-' + settings[size];
317             }
318         });
319         
320         var inputblock = input;
321         
322         var feedback = {
323             tag: 'span',
324             cls: 'glyphicon form-control-feedback'
325         };
326             
327         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
328             
329             inputblock = {
330                 cls : 'has-feedback',
331                 cn :  [
332                     input,
333                     feedback
334                 ] 
335             };  
336         }
337         
338         if (this.before || this.after) {
339             
340             inputblock = {
341                 cls : 'input-group',
342                 cn :  [] 
343             };
344             
345             if (this.before && typeof(this.before) == 'string') {
346                 
347                 inputblock.cn.push({
348                     tag :'span',
349                     cls : 'roo-input-before input-group-addon',
350                     html : this.before
351                 });
352             }
353             if (this.before && typeof(this.before) == 'object') {
354                 this.before = Roo.factory(this.before);
355                 
356                 inputblock.cn.push({
357                     tag :'span',
358                     cls : 'roo-input-before input-group-' +
359                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
360                 });
361             }
362             
363             inputblock.cn.push(input);
364             
365             if (this.after && typeof(this.after) == 'string') {
366                 inputblock.cn.push({
367                     tag :'span',
368                     cls : 'roo-input-after input-group-addon',
369                     html : this.after
370                 });
371             }
372             if (this.after && typeof(this.after) == 'object') {
373                 this.after = Roo.factory(this.after);
374                 
375                 inputblock.cn.push({
376                     tag :'span',
377                     cls : 'roo-input-after input-group-' +
378                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
379                 });
380             }
381             
382             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
383                 inputblock.cls += ' has-feedback';
384                 inputblock.cn.push(feedback);
385             }
386         };
387         
388         if (align ==='left' && this.fieldLabel.length) {
389             
390             cfg.cls += ' roo-form-group-label-left';
391             
392             cfg.cn = [
393                 {
394                     tag : 'i',
395                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
396                     tooltip : 'This field is required'
397                 },
398                 {
399                     tag: 'label',
400                     'for' :  id,
401                     cls : 'control-label',
402                     html : this.fieldLabel
403
404                 },
405                 {
406                     cls : "", 
407                     cn: [
408                         inputblock
409                     ]
410                 }
411             ];
412             
413             var labelCfg = cfg.cn[1];
414             var contentCfg = cfg.cn[2];
415             
416             if(this.indicatorpos == 'right'){
417                 cfg.cn = [
418                     {
419                         tag: 'label',
420                         'for' :  id,
421                         cls : 'control-label',
422                         cn : [
423                             {
424                                 tag : 'span',
425                                 html : this.fieldLabel
426                             },
427                             {
428                                 tag : 'i',
429                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
430                                 tooltip : 'This field is required'
431                             }
432                         ]
433                     },
434                     {
435                         cls : "",
436                         cn: [
437                             inputblock
438                         ]
439                     }
440
441                 ];
442                 
443                 labelCfg = cfg.cn[0];
444                 contentCfg = cfg.cn[1];
445             
446             }
447             
448             if(this.labelWidth > 12){
449                 labelCfg.style = "width: " + this.labelWidth + 'px';
450             }
451             
452             if(this.labelWidth < 13 && this.labelmd == 0){
453                 this.labelmd = this.labelWidth;
454             }
455             
456             if(this.labellg > 0){
457                 labelCfg.cls += ' col-lg-' + this.labellg;
458                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
459             }
460             
461             if(this.labelmd > 0){
462                 labelCfg.cls += ' col-md-' + this.labelmd;
463                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
464             }
465             
466             if(this.labelsm > 0){
467                 labelCfg.cls += ' col-sm-' + this.labelsm;
468                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
469             }
470             
471             if(this.labelxs > 0){
472                 labelCfg.cls += ' col-xs-' + this.labelxs;
473                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
474             }
475             
476             
477         } else if ( this.fieldLabel.length) {
478                 
479             cfg.cn = [
480                 {
481                     tag : 'i',
482                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
483                     tooltip : 'This field is required'
484                 },
485                 {
486                     tag: 'label',
487                    //cls : 'input-group-addon',
488                     html : this.fieldLabel
489
490                 },
491
492                inputblock
493
494            ];
495            
496            if(this.indicatorpos == 'right'){
497                 
498                 cfg.cn = [
499                     {
500                         tag: 'label',
501                        //cls : 'input-group-addon',
502                         html : this.fieldLabel
503
504                     },
505                     {
506                         tag : 'i',
507                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
508                         tooltip : 'This field is required'
509                     },
510
511                    inputblock
512
513                ];
514
515             }
516
517         } else {
518             
519             cfg.cn = [
520
521                     inputblock
522
523             ];
524                 
525                 
526         };
527         
528         if (this.parentType === 'Navbar' &&  this.parent().bar) {
529            cfg.cls += ' navbar-form';
530         }
531         
532         if (this.parentType === 'NavGroup') {
533            cfg.cls += ' navbar-form';
534            cfg.tag = 'li';
535         }
536         
537         return cfg;
538         
539     },
540     /**
541      * return the real input element.
542      */
543     inputEl: function ()
544     {
545         return this.el.select('input.form-control',true).first();
546     },
547     
548     tooltipEl : function()
549     {
550         return this.inputEl();
551     },
552     
553     indicatorEl : function()
554     {
555         var indicator = this.el.select('i.roo-required-indicator',true).first();
556         
557         if(!indicator){
558             return false;
559         }
560         
561         return indicator;
562         
563     },
564     
565     setDisabled : function(v)
566     {
567         var i  = this.inputEl().dom;
568         if (!v) {
569             i.removeAttribute('disabled');
570             return;
571             
572         }
573         i.setAttribute('disabled','true');
574     },
575     initEvents : function()
576     {
577           
578         this.inputEl().on("keydown" , this.fireKey,  this);
579         this.inputEl().on("focus", this.onFocus,  this);
580         this.inputEl().on("blur", this.onBlur,  this);
581         
582         this.inputEl().relayEvent('keyup', this);
583         
584         this.indicator = this.indicatorEl();
585         
586         if(this.indicator){
587             this.indicator.addClass('invisible');
588         }
589  
590         // reference to original value for reset
591         this.originalValue = this.getValue();
592         //Roo.form.TextField.superclass.initEvents.call(this);
593         if(this.validationEvent == 'keyup'){
594             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
595             this.inputEl().on('keyup', this.filterValidation, this);
596         }
597         else if(this.validationEvent !== false){
598             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
599         }
600         
601         if(this.selectOnFocus){
602             this.on("focus", this.preFocus, this);
603             
604         }
605         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
606             this.inputEl().on("keypress", this.filterKeys, this);
607         } else {
608             this.inputEl().relayEvent('keypress', this);
609         }
610        /* if(this.grow){
611             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
612             this.el.on("click", this.autoSize,  this);
613         }
614         */
615         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
616             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
617         }
618         
619         if (typeof(this.before) == 'object') {
620             this.before.render(this.el.select('.roo-input-before',true).first());
621         }
622         if (typeof(this.after) == 'object') {
623             this.after.render(this.el.select('.roo-input-after',true).first());
624         }
625         
626         this.inputEl().on('change', this.onChange, this);
627         
628     },
629     filterValidation : function(e){
630         if(!e.isNavKeyPress()){
631             this.validationTask.delay(this.validationDelay);
632         }
633     },
634      /**
635      * Validates the field value
636      * @return {Boolean} True if the value is valid, else false
637      */
638     validate : function(){
639         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
640         if(this.disabled || this.validateValue(this.getRawValue())){
641             this.markValid();
642             return true;
643         }
644         
645         this.markInvalid();
646         return false;
647     },
648     
649     
650     /**
651      * Validates a value according to the field's validation rules and marks the field as invalid
652      * if the validation fails
653      * @param {Mixed} value The value to validate
654      * @return {Boolean} True if the value is valid, else false
655      */
656     validateValue : function(value)
657     {
658         if(this.getVisibilityEl().hasClass('hidden')){
659             return true;
660         }
661         
662         if(value.length < 1)  { // if it's blank
663             if(this.allowBlank){
664                 return true;
665             }
666             return false;
667         }
668         
669         if(value.length < this.minLength){
670             return false;
671         }
672         if(value.length > this.maxLength){
673             return false;
674         }
675         if(this.vtype){
676             var vt = Roo.form.VTypes;
677             if(!vt[this.vtype](value, this)){
678                 return false;
679             }
680         }
681         if(typeof this.validator == "function"){
682             var msg = this.validator(value);
683             if(msg !== true){
684                 return false;
685             }
686             if (typeof(msg) == 'string') {
687                 this.invalidText = msg;
688             }
689         }
690         
691         if(this.regex && !this.regex.test(value)){
692             return false;
693         }
694         
695         return true;
696     },
697     
698      // private
699     fireKey : function(e){
700         //Roo.log('field ' + e.getKey());
701         if(e.isNavKeyPress()){
702             this.fireEvent("specialkey", this, e);
703         }
704     },
705     focus : function (selectText){
706         if(this.rendered){
707             this.inputEl().focus();
708             if(selectText === true){
709                 this.inputEl().dom.select();
710             }
711         }
712         return this;
713     } ,
714     
715     onFocus : function(){
716         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
717            // this.el.addClass(this.focusClass);
718         }
719         if(!this.hasFocus){
720             this.hasFocus = true;
721             this.startValue = this.getValue();
722             this.fireEvent("focus", this);
723         }
724     },
725     
726     beforeBlur : Roo.emptyFn,
727
728     
729     // private
730     onBlur : function(){
731         this.beforeBlur();
732         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
733             //this.el.removeClass(this.focusClass);
734         }
735         this.hasFocus = false;
736         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
737             this.validate();
738         }
739         var v = this.getValue();
740         if(String(v) !== String(this.startValue)){
741             this.fireEvent('change', this, v, this.startValue);
742         }
743         this.fireEvent("blur", this);
744     },
745     
746     onChange : function(e)
747     {
748         var v = this.getValue();
749         if(String(v) !== String(this.startValue)){
750             this.fireEvent('change', this, v, this.startValue);
751         }
752         
753         this.startValue = v;
754         
755     },
756     
757     /**
758      * Resets the current field value to the originally loaded value and clears any validation messages
759      */
760     reset : function(){
761         this.setValue(this.originalValue);
762         this.validate();
763     },
764      /**
765      * Returns the name of the field
766      * @return {Mixed} name The name field
767      */
768     getName: function(){
769         return this.name;
770     },
771      /**
772      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
773      * @return {Mixed} value The field value
774      */
775     getValue : function(){
776         
777         var v = this.inputEl().getValue();
778         
779         return v;
780     },
781     /**
782      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
783      * @return {Mixed} value The field value
784      */
785     getRawValue : function(){
786         var v = this.inputEl().getValue();
787         
788         return v;
789     },
790     
791     /**
792      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
793      * @param {Mixed} value The value to set
794      */
795     setRawValue : function(v){
796         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
797     },
798     
799     selectText : function(start, end){
800         var v = this.getRawValue();
801         if(v.length > 0){
802             start = start === undefined ? 0 : start;
803             end = end === undefined ? v.length : end;
804             var d = this.inputEl().dom;
805             if(d.setSelectionRange){
806                 d.setSelectionRange(start, end);
807             }else if(d.createTextRange){
808                 var range = d.createTextRange();
809                 range.moveStart("character", start);
810                 range.moveEnd("character", v.length-end);
811                 range.select();
812             }
813         }
814     },
815     
816     /**
817      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
818      * @param {Mixed} value The value to set
819      */
820     setValue : function(v){
821         this.value = v;
822         if(this.rendered){
823             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
824             this.validate();
825         }
826     },
827     
828     /*
829     processValue : function(value){
830         if(this.stripCharsRe){
831             var newValue = value.replace(this.stripCharsRe, '');
832             if(newValue !== value){
833                 this.setRawValue(newValue);
834                 return newValue;
835             }
836         }
837         return value;
838     },
839   */
840     preFocus : function(){
841         
842         if(this.selectOnFocus){
843             this.inputEl().dom.select();
844         }
845     },
846     filterKeys : function(e){
847         var k = e.getKey();
848         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
849             return;
850         }
851         var c = e.getCharCode(), cc = String.fromCharCode(c);
852         if(Roo.isIE && (e.isSpecialKey() || !cc)){
853             return;
854         }
855         if(!this.maskRe.test(cc)){
856             e.stopEvent();
857         }
858     },
859      /**
860      * Clear any invalid styles/messages for this field
861      */
862     clearInvalid : function(){
863         
864         if(!this.el || this.preventMark){ // not rendered
865             return;
866         }
867         
868      
869         this.el.removeClass(this.invalidClass);
870         
871         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
872             
873             var feedback = this.el.select('.form-control-feedback', true).first();
874             
875             if(feedback){
876                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
877             }
878             
879         }
880         
881         this.fireEvent('valid', this);
882     },
883     
884      /**
885      * Mark this field as valid
886      */
887     markValid : function()
888     {
889         if(!this.el  || this.preventMark){ // not rendered...
890             return;
891         }
892         
893         this.el.removeClass([this.invalidClass, this.validClass]);
894         
895         var feedback = this.el.select('.form-control-feedback', true).first();
896             
897         if(feedback){
898             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
899         }
900         
901         if(this.indicator){
902             this.indicator.removeClass('visible');
903             this.indicator.addClass('invisible');
904         }
905         
906         if(this.disabled){
907             return;
908         }
909         
910         if(this.allowBlank && !this.getRawValue().length){
911             return;
912         }
913         
914         this.el.addClass(this.validClass);
915         
916         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
917             
918             var feedback = this.el.select('.form-control-feedback', true).first();
919             
920             if(feedback){
921                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
922                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
923             }
924             
925         }
926         
927         this.fireEvent('valid', this);
928     },
929     
930      /**
931      * Mark this field as invalid
932      * @param {String} msg The validation message
933      */
934     markInvalid : function(msg)
935     {
936         if(!this.el  || this.preventMark){ // not rendered
937             return;
938         }
939         
940         this.el.removeClass([this.invalidClass, this.validClass]);
941         
942         var feedback = this.el.select('.form-control-feedback', true).first();
943             
944         if(feedback){
945             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
946         }
947
948         if(this.disabled){
949             return;
950         }
951         
952         if(this.allowBlank && !this.getRawValue().length){
953             return;
954         }
955         
956         if(this.indicator){
957             this.indicator.removeClass('invisible');
958             this.indicator.addClass('visible');
959         }
960         
961         this.el.addClass(this.invalidClass);
962         
963         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
964             
965             var feedback = this.el.select('.form-control-feedback', true).first();
966             
967             if(feedback){
968                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
969                 
970                 if(this.getValue().length || this.forceFeedback){
971                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
972                 }
973                 
974             }
975             
976         }
977         
978         this.fireEvent('invalid', this, msg);
979     },
980     // private
981     SafariOnKeyDown : function(event)
982     {
983         // this is a workaround for a password hang bug on chrome/ webkit.
984         if (this.inputEl().dom.type != 'password') {
985             return;
986         }
987         
988         var isSelectAll = false;
989         
990         if(this.inputEl().dom.selectionEnd > 0){
991             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
992         }
993         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
994             event.preventDefault();
995             this.setValue('');
996             return;
997         }
998         
999         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
1000             
1001             event.preventDefault();
1002             // this is very hacky as keydown always get's upper case.
1003             //
1004             var cc = String.fromCharCode(event.getCharCode());
1005             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
1006             
1007         }
1008     },
1009     adjustWidth : function(tag, w){
1010         tag = tag.toLowerCase();
1011         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
1012             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
1013                 if(tag == 'input'){
1014                     return w + 2;
1015                 }
1016                 if(tag == 'textarea'){
1017                     return w-2;
1018                 }
1019             }else if(Roo.isOpera){
1020                 if(tag == 'input'){
1021                     return w + 2;
1022                 }
1023                 if(tag == 'textarea'){
1024                     return w-2;
1025                 }
1026             }
1027         }
1028         return w;
1029     },
1030     
1031     setFieldLabel : function(v)
1032     {
1033         if(!this.rendered){
1034             return;
1035         }
1036         
1037         if(this.indicator){
1038             var ar = this.el.select('label > span',true);
1039             
1040             if (ar.elements.length) {
1041                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
1042                 this.fieldLabel = v;
1043                 return;
1044             }
1045             
1046             var br = this.el.select('label',true);
1047             
1048             if(br.elements.length) {
1049                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
1050                 this.fieldLabel = v;
1051                 return;
1052             }
1053             
1054             Roo.log('Cannot Found any of label > span || label in input');
1055             return;
1056         }
1057         
1058         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
1059         this.fieldLabel = v;
1060         
1061         
1062     }
1063 });
1064
1065