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