9 * @class Roo.bootstrap.Input
10 * @extends Roo.bootstrap.Component
11 * Bootstrap Input class
12 * @cfg {Boolean} disabled is it disabled
13 * @cfg {String} fieldLabel - the label associated
14 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
15 * @cfg {String} name name of the input
16 * @cfg {string} fieldLabel - the label associated
17 * @cfg {string} inputType - input / file submit ...
18 * @cfg {string} placeholder - placeholder to put in text.
19 * @cfg {string} before - input group add on before
20 * @cfg {string} after - input group add on after
21 * @cfg {string} size - (lg|sm) or leave empty..
22 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
23 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
24 * @cfg {Number} md colspan out of 12 for computer-sized screens
25 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
26 * @cfg {string} value default value of the input
27 * @cfg {Number} labelWidth set the width of label (0-12)
28 * @cfg {String} labelAlign (top|left)
29 * @cfg {Boolean} readOnly Specifies that the field should be read-only
30 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
32 * @cfg {String} align (left|center|right) Default left
33 * @cfg {Boolean} forceFeedback (true|false) Default false
39 * @param {Object} config The config object
42 Roo.bootstrap.Input = function(config){
43 Roo.bootstrap.Input.superclass.constructor.call(this, config);
48 * Fires when this field receives input focus.
49 * @param {Roo.form.Field} this
54 * Fires when this field loses input focus.
55 * @param {Roo.form.Field} this
60 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
61 * {@link Roo.EventObject#getKey} to determine which key was pressed.
62 * @param {Roo.form.Field} this
63 * @param {Roo.EventObject} e The event object
68 * Fires just before the field blurs if the field value has changed.
69 * @param {Roo.form.Field} this
70 * @param {Mixed} newValue The new value
71 * @param {Mixed} oldValue The original value
76 * Fires after the field has been marked as invalid.
77 * @param {Roo.form.Field} this
78 * @param {String} msg The validation message
83 * Fires after the field has been validated with no errors.
84 * @param {Roo.form.Field} this
89 * Fires after the key up
90 * @param {Roo.form.Field} this
91 * @param {Roo.EventObject} e The event Object
97 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
99 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
100 automatic validation (defaults to "keyup").
102 validationEvent : "keyup",
104 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
106 validateOnBlur : true,
108 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
110 validationDelay : 250,
112 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
114 focusClass : "x-form-focus", // not needed???
118 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
120 invalidClass : "has-warning",
123 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
125 validClass : "has-success",
128 * @cfg {Boolean} hasFeedback (true|false) default true
133 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
135 invalidFeedbackClass : "glyphicon-warning-sign",
138 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
140 validFeedbackClass : "glyphicon-ok",
143 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
145 selectOnFocus : false,
148 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
152 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
157 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
159 disableKeyFilter : false,
162 * @cfg {Boolean} disabled True to disable the field (defaults to false).
166 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
170 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
172 blankText : "This field is required",
175 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
179 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
181 maxLength : Number.MAX_VALUE,
183 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
185 minLengthText : "The minimum length for this field is {0}",
187 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
189 maxLengthText : "The maximum length for this field is {0}",
193 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
194 * If available, this function will be called only after the basic validators all return true, and will be passed the
195 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
199 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
200 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
201 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
205 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
228 formatedValue : false,
229 forceFeedback : false,
231 parentLabelAlign : function()
234 while (parent.parent()) {
235 parent = parent.parent();
236 if (typeof(parent.labelAlign) !='undefined') {
237 return parent.labelAlign;
244 getAutoCreate : function(){
246 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
252 if(this.inputType != 'hidden'){
253 cfg.cls = 'form-group' //input-group
259 type : this.inputType,
261 cls : 'form-control',
262 placeholder : this.placeholder || '',
263 autocomplete : this.autocomplete || 'new-password'
268 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
271 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
272 input.maxLength = this.maxLength;
284 input.name = this.name;
287 input.cls += ' input-' + this.size;
290 ['xs','sm','md','lg'].map(function(size){
291 if (settings[size]) {
292 cfg.cls += ' col-' + size + '-' + settings[size];
296 var inputblock = input;
300 cls: 'glyphicon form-control-feedback'
303 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
306 cls : 'has-feedback',
314 if (this.before || this.after) {
321 if (this.before && typeof(this.before) == 'string') {
325 cls : 'roo-input-before input-group-addon',
329 if (this.before && typeof(this.before) == 'object') {
330 this.before = Roo.factory(this.before);
331 Roo.log(this.before);
334 cls : 'roo-input-before input-group-' +
335 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
339 inputblock.cn.push(input);
341 if (this.after && typeof(this.after) == 'string') {
344 cls : 'roo-input-after input-group-addon',
348 if (this.after && typeof(this.after) == 'object') {
349 this.after = Roo.factory(this.after);
353 cls : 'roo-input-after input-group-' +
354 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
358 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
359 inputblock.cls += ' has-feedback';
360 inputblock.cn.push(feedback);
364 if (align ==='left' && this.fieldLabel.length) {
365 Roo.log("left and has label");
371 cls : 'control-label col-sm-' + this.labelWidth,
372 html : this.fieldLabel
376 cls : "col-sm-" + (12 - this.labelWidth),
383 } else if ( this.fieldLabel.length) {
389 //cls : 'input-group-addon',
390 html : this.fieldLabel
400 Roo.log(" no label && no align");
409 Roo.log('input-parentType: ' + this.parentType);
411 if (this.parentType === 'Navbar' && this.parent().bar) {
412 cfg.cls += ' navbar-form';
420 * return the real input element.
424 return this.el.select('input.form-control',true).first();
427 tooltipEl : function()
429 return this.inputEl();
432 setDisabled : function(v)
434 var i = this.inputEl().dom;
436 i.removeAttribute('disabled');
440 i.setAttribute('disabled','true');
442 initEvents : function()
445 this.inputEl().on("keydown" , this.fireKey, this);
446 this.inputEl().on("focus", this.onFocus, this);
447 this.inputEl().on("blur", this.onBlur, this);
449 this.inputEl().relayEvent('keyup', this);
451 // reference to original value for reset
452 this.originalValue = this.getValue();
453 //Roo.form.TextField.superclass.initEvents.call(this);
454 if(this.validationEvent == 'keyup'){
455 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
456 this.inputEl().on('keyup', this.filterValidation, this);
458 else if(this.validationEvent !== false){
459 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
462 if(this.selectOnFocus){
463 this.on("focus", this.preFocus, this);
466 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
467 this.inputEl().on("keypress", this.filterKeys, this);
470 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
471 this.el.on("click", this.autoSize, this);
474 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
475 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
478 if (typeof(this.before) == 'object') {
479 this.before.render(this.el.select('.roo-input-before',true).first());
481 if (typeof(this.after) == 'object') {
482 this.after.render(this.el.select('.roo-input-after',true).first());
487 filterValidation : function(e){
488 if(!e.isNavKeyPress()){
489 this.validationTask.delay(this.validationDelay);
493 * Validates the field value
494 * @return {Boolean} True if the value is valid, else false
496 validate : function(){
497 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
498 if(this.disabled || this.validateValue(this.getRawValue())){
509 * Validates a value according to the field's validation rules and marks the field as invalid
510 * if the validation fails
511 * @param {Mixed} value The value to validate
512 * @return {Boolean} True if the value is valid, else false
514 validateValue : function(value){
515 if(value.length < 1) { // if it's blank
522 if(value.length < this.minLength){
525 if(value.length > this.maxLength){
529 var vt = Roo.form.VTypes;
530 if(!vt[this.vtype](value, this)){
534 if(typeof this.validator == "function"){
535 var msg = this.validator(value);
541 if(this.regex && !this.regex.test(value)){
551 fireKey : function(e){
552 //Roo.log('field ' + e.getKey());
553 if(e.isNavKeyPress()){
554 this.fireEvent("specialkey", this, e);
557 focus : function (selectText){
559 this.inputEl().focus();
560 if(selectText === true){
561 this.inputEl().dom.select();
567 onFocus : function(){
568 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
569 // this.el.addClass(this.focusClass);
572 this.hasFocus = true;
573 this.startValue = this.getValue();
574 this.fireEvent("focus", this);
578 beforeBlur : Roo.emptyFn,
584 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
585 //this.el.removeClass(this.focusClass);
587 this.hasFocus = false;
588 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
591 var v = this.getValue();
592 if(String(v) !== String(this.startValue)){
593 this.fireEvent('change', this, v, this.startValue);
595 this.fireEvent("blur", this);
599 * Resets the current field value to the originally loaded value and clears any validation messages
602 this.setValue(this.originalValue);
606 * Returns the name of the field
607 * @return {Mixed} name The name field
613 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
614 * @return {Mixed} value The field value
616 getValue : function(){
618 var v = this.inputEl().getValue();
623 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
624 * @return {Mixed} value The field value
626 getRawValue : function(){
627 var v = this.inputEl().getValue();
633 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
634 * @param {Mixed} value The value to set
636 setRawValue : function(v){
637 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
640 selectText : function(start, end){
641 var v = this.getRawValue();
643 start = start === undefined ? 0 : start;
644 end = end === undefined ? v.length : end;
645 var d = this.inputEl().dom;
646 if(d.setSelectionRange){
647 d.setSelectionRange(start, end);
648 }else if(d.createTextRange){
649 var range = d.createTextRange();
650 range.moveStart("character", start);
651 range.moveEnd("character", v.length-end);
658 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
659 * @param {Mixed} value The value to set
661 setValue : function(v){
664 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
670 processValue : function(value){
671 if(this.stripCharsRe){
672 var newValue = value.replace(this.stripCharsRe, '');
673 if(newValue !== value){
674 this.setRawValue(newValue);
681 preFocus : function(){
683 if(this.selectOnFocus){
684 this.inputEl().dom.select();
687 filterKeys : function(e){
689 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
692 var c = e.getCharCode(), cc = String.fromCharCode(c);
693 if(Roo.isIE && (e.isSpecialKey() || !cc)){
696 if(!this.maskRe.test(cc)){
701 * Clear any invalid styles/messages for this field
703 clearInvalid : function(){
705 if(!this.el || this.preventMark){ // not rendered
708 this.el.removeClass(this.invalidClass);
710 this.fireEvent('valid', this);
714 * Mark this field as valid
716 markValid : function(){
717 if(!this.el || this.preventMark){ // not rendered
721 this.el.removeClass([this.invalidClass, this.validClass]);
723 if(this.disabled || this.allowBlank){
727 this.el.addClass(this.validClass);
729 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && this.getValue().length){
731 var feedback = this.el.select('.form-control-feedback', true).first();
734 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
735 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
740 this.fireEvent('valid', this);
744 * Mark this field as invalid
745 * @param {String} msg The validation message
747 markInvalid : function(msg){
748 if(!this.el || this.preventMark){ // not rendered
752 this.el.removeClass([this.invalidClass, this.validClass]);
754 if(this.disabled || this.allowBlank){
758 this.el.addClass(this.invalidClass);
760 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
762 var feedback = this.el.select('.form-control-feedback', true).first();
765 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
767 if(this.getValue().length || this.forceFeedback){
768 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
775 this.fireEvent('invalid', this, msg);
778 SafariOnKeyDown : function(event)
780 // this is a workaround for a password hang bug on chrome/ webkit.
782 var isSelectAll = false;
784 if(this.inputEl().dom.selectionEnd > 0){
785 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
787 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
788 event.preventDefault();
793 if(isSelectAll && event.getCharCode() > 31){ // not backspace and delete key
795 event.preventDefault();
796 // this is very hacky as keydown always get's upper case.
798 var cc = String.fromCharCode(event.getCharCode());
799 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
803 adjustWidth : function(tag, w){
804 tag = tag.toLowerCase();
805 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
806 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
810 if(tag == 'textarea'){
813 }else if(Roo.isOpera){
817 if(tag == 'textarea'){