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