Ux.InputTextMask for ExtJS4
Ext.define('App.myForm', { | |
extend: 'Ext.form.FormPanel', | |
requires: ['Ux.InputTextMask'], | |
initComponent : function() { | |
Ext.apply(this, { | |
items: this.buildForm() | |
}); | |
App.myForm.superclass.initComponent.call(this); | |
}, | |
buildForm: function() { | |
return [ | |
{ fieldLabel: 'Telefone', name: 'telefone', allowBlank: false, xtype: 'textfield', plugins: [new Ux.InputTextMask('(99) 9999-9999')] }, | |
{ fieldLabel: 'Celular', name: 'celular', allowBlank: false, xtype: 'textfield', plugins: [new Ux.InputTextMask('(99) 9999-9999')] } | |
]; | |
} | |
}); |
/** | |
* InputTextMask script used for mask/regexp operations. | |
* Mask Individual Character Usage: | |
* 9 - designates only numeric values | |
* L - designates only uppercase letter values | |
* l - designates only lowercase letter values | |
* A - designates only alphanumeric values | |
* X - denotes that a custom client script regular expression is specified</li> | |
* All other characters are assumed to be "special" characters used to mask the input component. | |
* Example 1: | |
* (999)999-9999 only numeric values can be entered where the the character | |
* position value is 9. Parenthesis and dash are non-editable/mask characters. | |
* Example 2: | |
* 99L-ll-X[^A-C]X only numeric values for the first two characters, | |
* uppercase values for the third character, lowercase letters for the | |
* fifth/sixth characters, and the last character X[^A-C]X together counts | |
* as the eighth character regular expression that would allow all characters | |
* but "A", "B", and "C". Dashes outside the regular expression are non-editable/mask characters. | |
* @constructor | |
* @param (String) mask The InputTextMask | |
* @param (boolean) clearWhenInvalid True to clear the mask when the field blurs and the text is invalid. Optional, default is true. | |
*/ | |
Ext.define('Ux.InputTextMask', { | |
constructor: function(mask, clearWhenInvalid) { | |
if(clearWhenInvalid === undefined) | |
this.clearWhenInvalid = true; | |
else | |
this.clearWhenInvalid = clearWhenInvalid; | |
this.rawMask = mask; | |
this.viewMask = ''; | |
this.maskArray = new Array(); | |
var mai = 0; | |
var regexp = ''; | |
for(var i=0; i<mask.length; i++){ | |
if(regexp){ | |
if(regexp == 'X'){ | |
regexp = ''; | |
} | |
if(mask.charAt(i) == 'X'){ | |
this.maskArray[mai] = regexp; | |
mai++; | |
regexp = ''; | |
} else { | |
regexp += mask.charAt(i); | |
} | |
} else if(mask.charAt(i) == 'X'){ | |
regexp += 'X'; | |
this.viewMask += '_'; | |
} else if(mask.charAt(i) == '9' || mask.charAt(i) == 'L' || mask.charAt(i) == 'l' || mask.charAt(i) == 'A') { | |
this.viewMask += '_'; | |
this.maskArray[mai] = mask.charAt(i); | |
mai++; | |
} else { | |
this.viewMask += mask.charAt(i); | |
this.maskArray[mai] = RegExp.escape(mask.charAt(i)); | |
mai++; | |
} | |
} | |
this.specialChars = this.viewMask.replace(/(L|l|9|A|_|X)/g,''); | |
return this; | |
}, | |
init: function(field) { | |
this.field = field; | |
if (field.rendered){ | |
this.assignEl(); | |
} else { | |
field.on('render', this.assignEl, this); | |
} | |
field.on('blur',this.removeValueWhenInvalid, this); | |
field.on('focus',this.processMaskFocus, this); | |
}, | |
assignEl: function() { | |
this.inputTextElement = this.field.inputEl.dom; | |
this.field.inputEl.on('keypress', this.processKeyPress, this); | |
this.field.inputEl.on('keydown', this.processKeyDown, this); | |
if(Ext.isSafari || Ext.isIE){ | |
this.field.inputEl.on('paste',this.startTask,this); | |
this.field.inputEl.on('cut',this.startTask,this); | |
} | |
if(Ext.isGecko || Ext.isOpera){ | |
this.field.inputEl.on('mousedown',this.setPreviousValue,this); | |
} | |
if(Ext.isGecko){ | |
this.field.inputEl.on('input',this.onInput,this); | |
} | |
if(Ext.isOpera){ | |
this.field.inputEl.on('input',this.onInputOpera,this); | |
} | |
}, | |
onInput: function(){ | |
this.startTask(false); | |
}, | |
onInputOpera: function(){ | |
if(!this.prevValueOpera){ | |
this.startTask(false); | |
}else{ | |
this.manageBackspaceAndDeleteOpera(); | |
} | |
}, | |
manageBackspaceAndDeleteOpera: function(){ | |
this.inputTextElement.value=this.prevValueOpera.cursorPos.previousValue; | |
this.manageTheText(this.prevValueOpera.keycode,this.prevValueOpera.cursorPos); | |
this.prevValueOpera=null; | |
}, | |
setPreviousValue: function(event){ | |
this.oldCursorPos=this.getCursorPosition(); | |
}, | |
getValidatedKey: function(keycode, cursorPosition) { | |
var maskKey = this.maskArray[cursorPosition.start]; | |
if(maskKey == '9'){ | |
return keycode.pressedKey.match(/[0-9]/); | |
} else if(maskKey == 'L'){ | |
return (keycode.pressedKey.match(/[A-Za-z]/))? keycode.pressedKey.toUpperCase(): null; | |
} else if(maskKey == 'l'){ | |
return (keycode.pressedKey.match(/[A-Za-z]/))? keycode.pressedKey.toLowerCase(): null; | |
} else if(maskKey == 'A'){ | |
return keycode.pressedKey.match(/[A-Za-z0-9]/); | |
} else if(maskKey){ | |
return (keycode.pressedKey.match(new RegExp(maskKey))); | |
} | |
return(null); | |
}, | |
removeValueWhenInvalid: function() { | |
if(this.clearWhenInvalid && this.inputTextElement.value.indexOf('_') > -1){ | |
this.inputTextElement.value = ''; | |
} | |
}, | |
managePaste: function() { | |
if(this.oldCursorPos==null){ | |
return; | |
} | |
var valuePasted=this.inputTextElement.value.substring(this.oldCursorPos.start,this.inputTextElement.value.length-(this.oldCursorPos.previousValue.length-this.oldCursorPos.end)); | |
if(this.oldCursorPos.start<this.oldCursorPos.end){ | |
this.oldCursorPos.previousValue = | |
this.oldCursorPos.previousValue.substring(0,this.oldCursorPos.start)+ | |
this.viewMask.substring(this.oldCursorPos.start,this.oldCursorPos.end)+ | |
this.oldCursorPos.previousValue.substring(this.oldCursorPos.end,this.oldCursorPos.previousValue.length); | |
valuePasted=valuePasted.substr(0,this.oldCursorPos.end-this.oldCursorPos.start); | |
} | |
this.inputTextElement.value=this.oldCursorPos.previousValue; | |
keycode = { | |
unicode: '', | |
isShiftPressed: false, | |
isTab: false, | |
isBackspace: false, | |
isLeftOrRightArrow: false, | |
isDelete: false, | |
pressedKey: '' | |
} | |
var charOk=false; | |
for(var i=0;i<valuePasted.length;i++){ | |
keycode.pressedKey=valuePasted.substr(i,1); | |
keycode.unicode=valuePasted.charCodeAt(i); | |
this.oldCursorPos=this.skipMaskCharacters(keycode,this.oldCursorPos); | |
if(this.oldCursorPos===false){ | |
break; | |
} | |
if(this.injectValue(keycode,this.oldCursorPos)){ | |
charOk=true; | |
this.moveCursorToPosition(keycode, this.oldCursorPos); | |
this.oldCursorPos.previousValue=this.inputTextElement.value; | |
this.oldCursorPos.start=this.oldCursorPos.start+1; | |
} | |
} | |
if(!charOk && this.oldCursorPos!==false){ | |
this.moveCursorToPosition(null, this.oldCursorPos); | |
} | |
this.oldCursorPos=null; | |
}, | |
processKeyDown: function(e){ | |
this.processMaskFormatting(e,'keydown'); | |
}, | |
processKeyPress: function(e){ | |
this.processMaskFormatting(e,'keypress'); | |
}, | |
startTask: function(setOldCursor){ | |
if(this.task==undefined){ | |
this.task=new Ext.util.DelayedTask(this.managePaste,this); | |
} | |
if(setOldCursor!== false){ | |
this.oldCursorPos=this.getCursorPosition(); | |
} | |
this.task.delay(0); | |
}, | |
skipMaskCharacters: function(keycode, cursorPos){ | |
if(cursorPos.start!=cursorPos.end && (keycode.isDelete || keycode.isBackspace)) | |
return(cursorPos); | |
while(this.specialChars.match(RegExp.escape(this.viewMask.charAt(((keycode.isBackspace)? cursorPos.start-1: cursorPos.start))))){ | |
if(keycode.isBackspace) { | |
cursorPos.dec(); | |
} else { | |
cursorPos.inc(); | |
} | |
if(cursorPos.start >= cursorPos.previousValue.length || cursorPos.start < 0){ | |
return false; | |
} | |
} | |
return(cursorPos); | |
}, | |
isManagedByKeyDown: function(keycode){ | |
if(keycode.isDelete || keycode.isBackspace){ | |
return(true); | |
} | |
return(false); | |
}, | |
processMaskFormatting: function(e, type) { | |
this.oldCursorPos=null; | |
var cursorPos = this.getCursorPosition(); | |
var keycode = this.getKeyCode(e, type); | |
if(keycode.unicode==0){//?? sometimes on Safari | |
return; | |
} | |
if((keycode.unicode==67 || keycode.unicode==99) && e.ctrlKey){//Ctrl+c, let's the browser manage it! | |
return; | |
} | |
if((keycode.unicode==88 || keycode.unicode==120) && e.ctrlKey){//Ctrl+x, manage paste | |
this.startTask(); | |
return; | |
} | |
if((keycode.unicode==86 || keycode.unicode==118) && e.ctrlKey){//Ctrl+v, manage paste.... | |
this.startTask(); | |
return; | |
} | |
if((keycode.isBackspace || keycode.isDelete) && Ext.isOpera){ | |
this.prevValueOpera={cursorPos: cursorPos, keycode: keycode}; | |
return; | |
} | |
if(type=='keydown' && !this.isManagedByKeyDown(keycode)){ | |
return true; | |
} | |
if(type=='keypress' && this.isManagedByKeyDown(keycode)){ | |
return true; | |
} | |
if(this.handleEventBubble(e, keycode, type)){ | |
return true; | |
} | |
return(this.manageTheText(keycode, cursorPos)); | |
}, | |
manageTheText: function(keycode, cursorPos){ | |
if(this.inputTextElement.value.length === 0){ | |
this.inputTextElement.value = this.viewMask; | |
} | |
cursorPos=this.skipMaskCharacters(keycode, cursorPos); | |
if(cursorPos===false){ | |
return false; | |
} | |
if(this.injectValue(keycode, cursorPos)){ | |
this.moveCursorToPosition(keycode, cursorPos); | |
} | |
return(false); | |
}, | |
processMaskFocus: function(){ | |
if(this.inputTextElement.value.length == 0){ | |
var cursorPos = this.getCursorPosition(); | |
this.inputTextElement.value = this.viewMask; | |
this.moveCursorToPosition(null, cursorPos); | |
} | |
}, | |
isManagedByBrowser: function(keyEvent, keycode, type){ | |
if(((type=='keypress' && keyEvent.charCode===0) || | |
type=='keydown') && (keycode.unicode==Ext.EventObject.TAB || | |
keycode.unicode==Ext.EventObject.RETURN || | |
keycode.unicode==Ext.EventObject.ENTER || | |
keycode.unicode==Ext.EventObject.SHIFT || | |
keycode.unicode==Ext.EventObject.CONTROL || | |
keycode.unicode==Ext.EventObject.ESC || | |
keycode.unicode==Ext.EventObject.PAGEUP || | |
keycode.unicode==Ext.EventObject.PAGEDOWN || | |
keycode.unicode==Ext.EventObject.END || | |
keycode.unicode==Ext.EventObject.HOME || | |
keycode.unicode==Ext.EventObject.LEFT || | |
keycode.unicode==Ext.EventObject.UP || | |
keycode.unicode==Ext.EventObject.RIGHT || | |
keycode.unicode==Ext.EventObject.DOWN)){ | |
return(true); | |
} | |
return(false); | |
}, | |
handleEventBubble: function(keyEvent, keycode, type) { | |
try { | |
if(keycode && this.isManagedByBrowser(keyEvent, keycode, type)){ | |
return true; | |
} | |
keyEvent.stopEvent(); | |
return false; | |
} catch(e) { | |
alert(e.message); | |
} | |
}, | |
getCursorPosition: function() { | |
var s, e, r; | |
if(this.inputTextElement.createTextRange){ | |
r = document.selection.createRange().duplicate(); | |
r.moveEnd('character', this.inputTextElement.value.length); | |
if(r.text === ''){ | |
s = this.inputTextElement.value.length; | |
} else { | |
s = this.inputTextElement.value.lastIndexOf(r.text); | |
} | |
r = document.selection.createRange().duplicate(); | |
r.moveStart('character', -this.inputTextElement.value.length); | |
e = r.text.length; | |
} else { | |
s = this.inputTextElement.selectionStart; | |
e = this.inputTextElement.selectionEnd; | |
} | |
return this.CursorPosition(s, e, r, this.inputTextElement.value); | |
}, | |
moveCursorToPosition: function(keycode, cursorPosition) { | |
var p = (!keycode || (keycode && keycode.isBackspace ))? cursorPosition.start: cursorPosition.start + 1; | |
if(this.inputTextElement.createTextRange){ | |
cursorPosition.range.move('character', p); | |
cursorPosition.range.select(); | |
} else { | |
this.inputTextElement.selectionStart = p; | |
this.inputTextElement.selectionEnd = p; | |
} | |
}, | |
injectValue: function(keycode, cursorPosition) { | |
if (!keycode.isDelete && keycode.unicode == cursorPosition.previousValue.charCodeAt(cursorPosition.start)) | |
return true; | |
var key; | |
if(!keycode.isDelete && !keycode.isBackspace){ | |
key=this.getValidatedKey(keycode, cursorPosition); | |
} else { | |
if(cursorPosition.start == cursorPosition.end){ | |
key='_'; | |
if(keycode.isBackspace){ | |
cursorPosition.dec(); | |
} | |
} else { | |
key=this.viewMask.substring(cursorPosition.start,cursorPosition.end); | |
} | |
} | |
if(key){ | |
this.inputTextElement.value = cursorPosition.previousValue.substring(0,cursorPosition.start) | |
+ key + | |
cursorPosition.previousValue.substring(cursorPosition.start + key.length,cursorPosition.previousValue.length); | |
return true; | |
} | |
return false; | |
}, | |
getKeyCode: function(onKeyDownEvent, type) { | |
var keycode = {}; | |
keycode.unicode = onKeyDownEvent.getKey(); | |
keycode.isShiftPressed = onKeyDownEvent.shiftKey; | |
keycode.isDelete = ((onKeyDownEvent.getKey() == Ext.EventObject.DELETE && type=='keydown') || ( type=='keypress' && onKeyDownEvent.charCode===0 && onKeyDownEvent.keyCode == Ext.EventObject.DELETE))? true: false; | |
keycode.isTab = (onKeyDownEvent.getKey() == Ext.EventObject.TAB)? true: false; | |
keycode.isBackspace = (onKeyDownEvent.getKey() == Ext.EventObject.BACKSPACE)? true: false; | |
keycode.isLeftOrRightArrow = (onKeyDownEvent.getKey() == Ext.EventObject.LEFT || onKeyDownEvent.getKey() == Ext.EventObject.RIGHT)? true: false; | |
keycode.pressedKey = String.fromCharCode(keycode.unicode); | |
return(keycode); | |
}, | |
CursorPosition: function(start, end, range, previousValue) { | |
var cursorPosition = {}; | |
cursorPosition.start = isNaN(start)? 0: start; | |
cursorPosition.end = isNaN(end)? 0: end; | |
cursorPosition.range = range; | |
cursorPosition.previousValue = previousValue; | |
cursorPosition.inc = function(){cursorPosition.start++;cursorPosition.end++;}; | |
cursorPosition.dec = function(){cursorPosition.start--;cursorPosition.end--;}; | |
return(cursorPosition); | |
} | |
}); | |
Ext.applyIf(RegExp, { | |
escape: function(str) { | |
return new String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment