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