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