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 The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
211      */
212     regexText : "",
213     /**
214      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
215      */
216     invalidText : "",
217     
218     
219     
220     autocomplete: false,
221     
222     
223     fieldLabel : '',
224     inputType : 'text',
225     
226     name : false,
227     placeholder: false,
228     before : false,
229     after : false,
230     size : false,
231     hasFocus : false,
232     preventMark: false,
233     isFormField : true,
234     value : '',
235     labelWidth : 2,
236     labelAlign : false,
237     readOnly : false,
238     align : false,
239     formatedValue : false,
240     forceFeedback : false,
241     
242     indicatorpos : 'left',
243     
244     labellg : 0,
245     labelmd : 0,
246     labelsm : 0,
247     labelxs : 0,
248     
249     parentLabelAlign : function()
250     {
251         var parent = this;
252         while (parent.parent()) {
253             parent = parent.parent();
254             if (typeof(parent.labelAlign) !='undefined') {
255                 return parent.labelAlign;
256             }
257         }
258         return 'left';
259         
260     },
261     
262     getAutoCreate : function()
263     {
264         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
265         
266         var id = Roo.id();
267         
268         var cfg = {};
269         
270         if(this.inputType != 'hidden'){
271             cfg.cls = 'form-group' //input-group
272         }
273         
274         var input =  {
275             tag: 'input',
276             id : id,
277             type : this.inputType,
278             value : this.value,
279             cls : 'form-control',
280             placeholder : this.placeholder || '',
281             autocomplete : this.autocomplete || 'new-password'
282         };
283         
284         if(this.align){
285             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
286         }
287         
288         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
289             input.maxLength = this.maxLength;
290         }
291         
292         if (this.disabled) {
293             input.disabled=true;
294         }
295         
296         if (this.readOnly) {
297             input.readonly=true;
298         }
299         
300         if (this.name) {
301             input.name = this.name;
302         }
303         
304         if (this.size) {
305             input.cls += ' input-' + this.size;
306         }
307         
308         var settings=this;
309         ['xs','sm','md','lg'].map(function(size){
310             if (settings[size]) {
311                 cfg.cls += ' col-' + size + '-' + settings[size];
312             }
313         });
314         
315         var inputblock = input;
316         
317         var feedback = {
318             tag: 'span',
319             cls: 'glyphicon form-control-feedback'
320         };
321             
322         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
323             
324             inputblock = {
325                 cls : 'has-feedback',
326                 cn :  [
327                     input,
328                     feedback
329                 ] 
330             };  
331         }
332         
333         if (this.before || this.after) {
334             
335             inputblock = {
336                 cls : 'input-group',
337                 cn :  [] 
338             };
339             
340             if (this.before && typeof(this.before) == 'string') {
341                 
342                 inputblock.cn.push({
343                     tag :'span',
344                     cls : 'roo-input-before input-group-addon',
345                     html : this.before
346                 });
347             }
348             if (this.before && typeof(this.before) == 'object') {
349                 this.before = Roo.factory(this.before);
350                 
351                 inputblock.cn.push({
352                     tag :'span',
353                     cls : 'roo-input-before input-group-' +
354                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
355                 });
356             }
357             
358             inputblock.cn.push(input);
359             
360             if (this.after && typeof(this.after) == 'string') {
361                 inputblock.cn.push({
362                     tag :'span',
363                     cls : 'roo-input-after input-group-addon',
364                     html : this.after
365                 });
366             }
367             if (this.after && typeof(this.after) == 'object') {
368                 this.after = Roo.factory(this.after);
369                 
370                 inputblock.cn.push({
371                     tag :'span',
372                     cls : 'roo-input-after input-group-' +
373                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
374                 });
375             }
376             
377             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
378                 inputblock.cls += ' has-feedback';
379                 inputblock.cn.push(feedback);
380             }
381         };
382         
383         if (align ==='left' && this.fieldLabel.length) {
384             
385             cfg.cls += ' roo-form-group-label-left';
386             
387             cfg.cn = [
388                 {
389                     tag : 'i',
390                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
391                     tooltip : 'This field is required'
392                 },
393                 {
394                     tag: 'label',
395                     'for' :  id,
396                     cls : 'control-label',
397                     html : this.fieldLabel
398
399                 },
400                 {
401                     cls : "", 
402                     cn: [
403                         inputblock
404                     ]
405                 }
406             ];
407             
408             var labelCfg = cfg.cn[1];
409             var contentCfg = cfg.cn[2];
410             
411             if(this.indicatorpos == 'right'){
412                 cfg.cn = [
413                     {
414                         tag: 'label',
415                         'for' :  id,
416                         cls : 'control-label',
417                         html : this.fieldLabel
418
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                         cls : "",
427                         cn: [
428                             inputblock
429                         ]
430                     }
431
432                 ];
433                 
434                 labelCfg = cfg.cn[0];
435                 contentCfg = cfg.cn[2];
436             
437             }
438             
439             if(this.labelWidth > 12){
440                 labelCfg.style = "width: " + this.labelWidth + 'px';
441             }
442             
443             if(this.labelWidth < 13 && this.labelmd == 0){
444                 this.labelmd = this.labelWidth;
445             }
446             
447             if(this.labellg > 0){
448                 labelCfg.cls += ' col-lg-' + this.labellg;
449                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
450             }
451             
452             if(this.labelmd > 0){
453                 labelCfg.cls += ' col-md-' + this.labelmd;
454                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
455             }
456             
457             if(this.labelsm > 0){
458                 labelCfg.cls += ' col-sm-' + this.labelsm;
459                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
460             }
461             
462             if(this.labelxs > 0){
463                 labelCfg.cls += ' col-xs-' + this.labelxs;
464                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
465             }
466             
467             
468         } else if ( this.fieldLabel.length) {
469                 
470             cfg.cn = [
471                 {
472                     tag : 'i',
473                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
474                     tooltip : 'This field is required'
475                 },
476                 {
477                     tag: 'label',
478                    //cls : 'input-group-addon',
479                     html : this.fieldLabel
480
481                 },
482
483                inputblock
484
485            ];
486            
487            if(this.indicatorpos == 'right'){
488                 
489                 cfg.cn = [
490                     {
491                         tag: 'label',
492                        //cls : 'input-group-addon',
493                         html : this.fieldLabel
494
495                     },
496                     {
497                         tag : 'i',
498                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
499                         tooltip : 'This field is required'
500                     },
501
502                    inputblock
503
504                ];
505
506             }
507
508         } else {
509             
510             cfg.cn = [
511
512                     inputblock
513
514             ];
515                 
516                 
517         };
518         
519         if (this.parentType === 'Navbar' &&  this.parent().bar) {
520            cfg.cls += ' navbar-form';
521         }
522         
523         if (this.parentType === 'NavGroup') {
524            cfg.cls += ' navbar-form';
525            cfg.tag = 'li';
526         }
527         
528         return cfg;
529         
530     },
531     /**
532      * return the real input element.
533      */
534     inputEl: function ()
535     {
536         return this.el.select('input.form-control',true).first();
537     },
538     
539     tooltipEl : function()
540     {
541         return this.inputEl();
542     },
543     
544     indicatorEl : function()
545     {
546         var indicator = this.el.select('i.roo-required-indicator',true).first();
547         
548         if(!indicator){
549             return false;
550         }
551         
552         return indicator;
553         
554     },
555     
556     setDisabled : function(v)
557     {
558         var i  = this.inputEl().dom;
559         if (!v) {
560             i.removeAttribute('disabled');
561             return;
562             
563         }
564         i.setAttribute('disabled','true');
565     },
566     initEvents : function()
567     {
568           
569         this.inputEl().on("keydown" , this.fireKey,  this);
570         this.inputEl().on("focus", this.onFocus,  this);
571         this.inputEl().on("blur", this.onBlur,  this);
572         
573         this.inputEl().relayEvent('keyup', this);
574         
575         this.indicator = this.indicatorEl();
576         
577         if(this.indicator){
578             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
579             this.indicator.hide();
580         }
581  
582         // reference to original value for reset
583         this.originalValue = this.getValue();
584         //Roo.form.TextField.superclass.initEvents.call(this);
585         if(this.validationEvent == 'keyup'){
586             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
587             this.inputEl().on('keyup', this.filterValidation, this);
588         }
589         else if(this.validationEvent !== false){
590             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
591         }
592         
593         if(this.selectOnFocus){
594             this.on("focus", this.preFocus, this);
595             
596         }
597         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
598             this.inputEl().on("keypress", this.filterKeys, this);
599         } else {
600             this.inputEl().relayEvent('keypress', this);
601         }
602        /* if(this.grow){
603             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
604             this.el.on("click", this.autoSize,  this);
605         }
606         */
607         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
608             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
609         }
610         
611         if (typeof(this.before) == 'object') {
612             this.before.render(this.el.select('.roo-input-before',true).first());
613         }
614         if (typeof(this.after) == 'object') {
615             this.after.render(this.el.select('.roo-input-after',true).first());
616         }
617         
618         
619     },
620     filterValidation : function(e){
621         if(!e.isNavKeyPress()){
622             this.validationTask.delay(this.validationDelay);
623         }
624     },
625      /**
626      * Validates the field value
627      * @return {Boolean} True if the value is valid, else false
628      */
629     validate : function(){
630         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
631         if(this.disabled || this.validateValue(this.getRawValue())){
632             this.markValid();
633             return true;
634         }
635         
636         this.markInvalid();
637         return false;
638     },
639     
640     
641     /**
642      * Validates a value according to the field's validation rules and marks the field as invalid
643      * if the validation fails
644      * @param {Mixed} value The value to validate
645      * @return {Boolean} True if the value is valid, else false
646      */
647     validateValue : function(value){
648         if(value.length < 1)  { // if it's blank
649             if(this.allowBlank){
650                 return true;
651             }            
652             return this.inputEl().hasClass('hide') ? true : false;
653         }
654         
655         if(value.length < this.minLength){
656             return false;
657         }
658         if(value.length > this.maxLength){
659             return false;
660         }
661         if(this.vtype){
662             var vt = Roo.form.VTypes;
663             if(!vt[this.vtype](value, this)){
664                 return false;
665             }
666         }
667         if(typeof this.validator == "function"){
668             var msg = this.validator(value);
669             if(msg !== true){
670                 return false;
671             }
672         }
673         
674         if(this.regex && !this.regex.test(value)){
675             return false;
676         }
677         
678         return true;
679     },
680
681     
682     
683      // private
684     fireKey : function(e){
685         //Roo.log('field ' + e.getKey());
686         if(e.isNavKeyPress()){
687             this.fireEvent("specialkey", this, e);
688         }
689     },
690     focus : function (selectText){
691         if(this.rendered){
692             this.inputEl().focus();
693             if(selectText === true){
694                 this.inputEl().dom.select();
695             }
696         }
697         return this;
698     } ,
699     
700     onFocus : function(){
701         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
702            // this.el.addClass(this.focusClass);
703         }
704         if(!this.hasFocus){
705             this.hasFocus = true;
706             this.startValue = this.getValue();
707             this.fireEvent("focus", this);
708         }
709     },
710     
711     beforeBlur : Roo.emptyFn,
712
713     
714     // private
715     onBlur : function(){
716         this.beforeBlur();
717         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
718             //this.el.removeClass(this.focusClass);
719         }
720         this.hasFocus = false;
721         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
722             this.validate();
723         }
724         var v = this.getValue();
725         if(String(v) !== String(this.startValue)){
726             this.fireEvent('change', this, v, this.startValue);
727         }
728         this.fireEvent("blur", this);
729     },
730     
731     /**
732      * Resets the current field value to the originally loaded value and clears any validation messages
733      */
734     reset : function(){
735         this.setValue(this.originalValue);
736         this.validate();
737     },
738      /**
739      * Returns the name of the field
740      * @return {Mixed} name The name field
741      */
742     getName: function(){
743         return this.name;
744     },
745      /**
746      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
747      * @return {Mixed} value The field value
748      */
749     getValue : function(){
750         
751         var v = this.inputEl().getValue();
752         
753         return v;
754     },
755     /**
756      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
757      * @return {Mixed} value The field value
758      */
759     getRawValue : function(){
760         var v = this.inputEl().getValue();
761         
762         return v;
763     },
764     
765     /**
766      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
767      * @param {Mixed} value The value to set
768      */
769     setRawValue : function(v){
770         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
771     },
772     
773     selectText : function(start, end){
774         var v = this.getRawValue();
775         if(v.length > 0){
776             start = start === undefined ? 0 : start;
777             end = end === undefined ? v.length : end;
778             var d = this.inputEl().dom;
779             if(d.setSelectionRange){
780                 d.setSelectionRange(start, end);
781             }else if(d.createTextRange){
782                 var range = d.createTextRange();
783                 range.moveStart("character", start);
784                 range.moveEnd("character", v.length-end);
785                 range.select();
786             }
787         }
788     },
789     
790     /**
791      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
792      * @param {Mixed} value The value to set
793      */
794     setValue : function(v){
795         this.value = v;
796         if(this.rendered){
797             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
798             this.validate();
799         }
800     },
801     
802     /*
803     processValue : function(value){
804         if(this.stripCharsRe){
805             var newValue = value.replace(this.stripCharsRe, '');
806             if(newValue !== value){
807                 this.setRawValue(newValue);
808                 return newValue;
809             }
810         }
811         return value;
812     },
813   */
814     preFocus : function(){
815         
816         if(this.selectOnFocus){
817             this.inputEl().dom.select();
818         }
819     },
820     filterKeys : function(e){
821         var k = e.getKey();
822         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
823             return;
824         }
825         var c = e.getCharCode(), cc = String.fromCharCode(c);
826         if(Roo.isIE && (e.isSpecialKey() || !cc)){
827             return;
828         }
829         if(!this.maskRe.test(cc)){
830             e.stopEvent();
831         }
832     },
833      /**
834      * Clear any invalid styles/messages for this field
835      */
836     clearInvalid : function(){
837         
838         if(!this.el || this.preventMark){ // not rendered
839             return;
840         }
841         
842      
843         this.el.removeClass(this.invalidClass);
844         
845         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
846             
847             var feedback = this.el.select('.form-control-feedback', true).first();
848             
849             if(feedback){
850                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
851             }
852             
853         }
854         
855         this.fireEvent('valid', this);
856     },
857     
858      /**
859      * Mark this field as valid
860      */
861     markValid : function()
862     {
863         if(!this.el  || this.preventMark){ // not rendered...
864             return;
865         }
866         
867         this.el.removeClass([this.invalidClass, this.validClass]);
868         
869         var feedback = this.el.select('.form-control-feedback', true).first();
870             
871         if(feedback){
872             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
873         }
874
875         if(this.disabled){
876             return;
877         }
878         
879         if(this.allowBlank && !this.getRawValue().length){
880             return;
881         }
882         
883         if(this.indicator){
884             this.indicator.hide();
885         }
886         
887         this.el.addClass(this.validClass);
888         
889         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
890             
891             var feedback = this.el.select('.form-control-feedback', true).first();
892             
893             if(feedback){
894                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
895                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
896             }
897             
898         }
899         
900         this.fireEvent('valid', this);
901     },
902     
903      /**
904      * Mark this field as invalid
905      * @param {String} msg The validation message
906      */
907     markInvalid : function(msg)
908     {
909         if(!this.el  || this.preventMark){ // not rendered
910             return;
911         }
912         
913         this.el.removeClass([this.invalidClass, this.validClass]);
914         
915         var feedback = this.el.select('.form-control-feedback', true).first();
916             
917         if(feedback){
918             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
919         }
920
921         if(this.disabled){
922             return;
923         }
924         
925         if(this.allowBlank && !this.getRawValue().length){
926             return;
927         }
928         
929         if(this.indicator){
930             this.indicator.show();
931         }
932         
933         this.el.addClass(this.invalidClass);
934         
935         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
936             
937             var feedback = this.el.select('.form-control-feedback', true).first();
938             
939             if(feedback){
940                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
941                 
942                 if(this.getValue().length || this.forceFeedback){
943                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
944                 }
945                 
946             }
947             
948         }
949         
950         this.fireEvent('invalid', this, msg);
951     },
952     // private
953     SafariOnKeyDown : function(event)
954     {
955         // this is a workaround for a password hang bug on chrome/ webkit.
956         if (this.inputEl().dom.type != 'password') {
957             return;
958         }
959         
960         var isSelectAll = false;
961         
962         if(this.inputEl().dom.selectionEnd > 0){
963             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
964         }
965         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
966             event.preventDefault();
967             this.setValue('');
968             return;
969         }
970         
971         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
972             
973             event.preventDefault();
974             // this is very hacky as keydown always get's upper case.
975             //
976             var cc = String.fromCharCode(event.getCharCode());
977             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
978             
979         }
980     },
981     adjustWidth : function(tag, w){
982         tag = tag.toLowerCase();
983         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
984             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
985                 if(tag == 'input'){
986                     return w + 2;
987                 }
988                 if(tag == 'textarea'){
989                     return w-2;
990                 }
991             }else if(Roo.isOpera){
992                 if(tag == 'input'){
993                     return w + 2;
994                 }
995                 if(tag == 'textarea'){
996                     return w-2;
997                 }
998             }
999         }
1000         return w;
1001     }
1002     
1003 });
1004
1005