Skip to content

Instantly share code, notes, and snippets.

@sigerello
Forked from loiane/Ux.InputTextMask.js
Last active September 20, 2015 12:10
Show Gist options
  • Save sigerello/d4a4b25f0f8f0783bb53 to your computer and use it in GitHub Desktop.
Save sigerello/d4a4b25f0f8f0783bb53 to your computer and use it in GitHub Desktop.

Revisions

  1. @loiane loiane created this gist Apr 8, 2011.
    397 changes: 397 additions & 0 deletions Ux.InputTextMask.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,397 @@
    /**
    * 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');
    }
    });
    19 changes: 19 additions & 0 deletions exemplo.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    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')] }
    ];
    }
    });