Skip to content

Instantly share code, notes, and snippets.

@loiane
Created April 8, 2011 01:10
Show Gist options
  • Save loiane/909119 to your computer and use it in GitHub Desktop.
Save loiane/909119 to your computer and use it in GitHub Desktop.
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');
}
});
@rifkihp
Copy link

rifkihp commented Oct 10, 2015

can i change format become 99/99/9999 ?

@venoxon
Copy link

venoxon commented Nov 6, 2015

@Ricoux Sorry for my english ;-(
Fast (very hardcore way ;-) )
add this line at end of assignEl method:
this.field.inputEl.on('click', this.processClick, this);

and add this methods:

processClick: function (snd) { 
    if (this.isMaskValue()) {
        this.moveCursorToStart();
    }
},

isMaskValue: function () {
    return this.inputTextElement.value === this.viewMask;
},

moveCursorToStart: function () {
    if (this.inputTextElement.createTextRange &&  document.selection) {
        cursorPosition.range.move('character', 0);
        cursorPosition.range.select();
    } else {
        this.inputTextElement.selectionStart = 0;
        this.inputTextElement.selectionEnd = 0;
    }

    this.inputTextElement.focus();
} 

Does someone have better idea?
Thanks

@UnsupportedCallbackException

@DerekB20
Simply change all occurrences of
if(this.inputTextElement.createTextRange)

to
if (this.inputTextElement.createTextRange && document.selection)

Microsoft now plays by the rules http://stackoverflow.com/a/19756936/1204043 and goes with HTML5 standards. So document.selection is legasy

@RobertoPegoraro
Copy link

How to remove mask before send to backend value? And apply mask on value comming backend not masked ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment