Hybrid View

  1. #1
    Ext User bobbicat71's Avatar
    Join Date
    Dec 2007
    Location
    Italy
    Posts
    20
    Vote Rating
    1
    bobbicat71 is on a distinguished road

      1  

    Default InputTextMask plugin for Textfield

    InputTextMask plugin for Textfield


    Hi all,
    here is a plugin for textfield that adds a mask input to the field.

    This is the latest version:
    Code:
    // $Id: InputTextMask.js 293638 2008-02-04 14:33:36Z UE014015 $
    
    Ext.namespace('Ext.ux.netbox');
    
    /**
     * 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.ux.netbox.InputTextMask = 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,'');
    };
    
    Ext.ux.netbox.InputTextMask.prototype = {
    
        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.getEl().dom;
            this.field.getEl().on('keypress', this.processKeyPress, this);
            this.field.getEl().on('keydown', this.processKeyDown, this);
            if(Ext.isSafari || Ext.isIE){
                this.field.getEl().on('paste',this.startTask,this);
                this.field.getEl().on('cut',this.startTask,this);
            }
            if(Ext.isGecko || Ext.isOpera){
                this.field.getEl().on('mousedown',this.setPreviousValue,this);
            }
            if(Ext.isGecko){
              this.field.getEl().on('input',this.onInput,this);
            }
            if(Ext.isOpera){
              this.field.getEl().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){//there is selection...
              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');
      }
    });
    
    Ext.ux.InputTextMask=Ext.ux.netbox.InputTextMask;
    And here is an example of the usage:
    Code:
    var dateTimeField = new Ext.form.TextField({plugins: [new Ext.ux.InputTextMask('99/99/9999 99:99', true)]});
    Enjoy!
    Last edited by bobbicat71; 4 Feb 2008 at 6:43 AM. Reason: latest version, opera support

  2. #2
    Sencha User krycek's Avatar
    Join Date
    Jun 2007
    Posts
    96
    Vote Rating
    0
    krycek is on a distinguished road

      0  

    Default


    I'm going to test it right now

  3. #3
    Sencha User krycek's Avatar
    Join Date
    Jun 2007
    Posts
    96
    Vote Rating
    0
    krycek is on a distinguished road

      0  

    Default


    It didn't work for me. I've tried on IE 6 and firefox 2.
    Here is how i used:
    Code:
     items: [{
                        xtype:'textfield',
                        fieldLabel: 'Name',
                        name: 'name',
    		    plugins: [new Ext.ux.InputTextMask('99/99/9999 99:99', true)],
                        anchor:'95%'
                    }, {...}
    Attached Images

  4. #4
    Ext User dandfra's Avatar
    Join Date
    Jun 2007
    Location
    Trento, Italy
    Posts
    122
    Vote Rating
    0
    dandfra is on a distinguished road

      0  

    Default


    It worked for me (IE6).
    Here's the code:
    PHP Code:
    <html>
    <
    head>
        <
    meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
        <
    title>Demo</title>
        <
    link rel="stylesheet" type="text/css" href="../../ext-2.0/resources/css/ext-all.css" />
        <
    script type="text/javascript" src="../../ext-2.0/adapter/ext/ext-base.js"></script>
        <script type="text/javascript" src="../../ext-2.0/ext-all-debug.js"></script>
        <script type="text/javascript" src="../src/InputTextMask.js"></script>
        <script>
    Ext.onReady(function(){
      var simple = new Ext.FormPanel({
            labelWidth: 75, // label settings here cascade unless overridden
            url:'save-form.php',
            frame:true,
            title: 'Simple Form',
            bodyStyle:'padding:5px 5px 0',
            width: 350,
            defaults: {width: 230},
            items: [{
                    fieldLabel: 'Date',
                    name: 'first',
                    xtype:'textfield',
                    plugins: [new Ext.ux.InputTextMask('99/99/9999 99:99', true)]
                }]
        });

        simple.render(document.body);
    });
    </script>
    </head>
    <body>
    </body>
    </html> 

  5. #5
    Sencha User krycek's Avatar
    Join Date
    Jun 2007
    Posts
    96
    Vote Rating
    0
    krycek is on a distinguished road

      0  

    Default


    It doesn't work when keyCode.unicode are between 97 and 120 (numeric keyboard)

  6. #6
    Sencha User krycek's Avatar
    Join Date
    Jun 2007
    Posts
    96
    Vote Rating
    0
    krycek is on a distinguished road

      0  

    Default


    I've changed processMaskFormatting to:
    Code:
     processMaskFormatting : function(e,type) {
            var keyCode = this.getKeyCode(e);
            if((type=='keydown' || type=='keypress') && ((keyCode.unicode >= 65 && keyCode.unicode <= 90) || (keyCode.unicode >= 97 && keyCode.unicode <= 122))){
                return true;
            }
    
            if(this.handleEventBubble(e, keyCode)){
                return true;
            }
            if(this.inputTextElement.value.length === 0){
                this.inputTextElement.value = this.viewMask;
            }
            var cursorPos = this.getCursorPosition();
            if(cursorPos.end == cursorPos.previousValue.length && !keyCode.isBackspace){
                return false;
            }
    
            while(this.specialChars.match(RegExp.escape(cursorPos.previousValue.charAt(((keyCode.isBackspace)? cursorPos.start-1: cursorPos.start))))){
                if(keyCode.isBackspace) {
                    cursorPos.decStart();
                } else {
                    cursorPos.incStart();
                }
                if(cursorPos.start >= cursorPos.previousValue.length || cursorPos.start < 0){
                    return false;
                }
            }
            if(keyCode.isBackspace){
                cursorPos.decStart();
            }
            if(this.injectValue(keyCode, cursorPos)){
                this.moveCursorToPosition(keyCode, cursorPos);
            }
            return false;
        },
    and It worked

  7. #7
    Ext User bobbicat71's Avatar
    Join Date
    Dec 2007
    Location
    Italy
    Posts
    20
    Vote Rating
    1
    bobbicat71 is on a distinguished road

      0  

    Default


    Quote Originally Posted by krycek View Post
    It doesn't work when keyCode.unicode are between 97 and 120 (numeric keyboard)
    Thanks for the feedback. I have fixed my code to manage the numeric keypad.

  8. #8
    Ext User fother's Avatar
    Join Date
    Sep 2007
    Location
    Brazil
    Posts
    744
    Vote Rating
    -1
    fother is an unknown quantity at this point

      0  

    Default


    in opera 9.25 for windows xp if you use the "backspace" or "delete" for clear your input.. the mask fail..

  9. #9
    Ext User dandfra's Avatar
    Join Date
    Jun 2007
    Location
    Trento, Italy
    Posts
    122
    Vote Rating
    0
    dandfra is on a distinguished road

      0  

    Exclamation Opera is not supported at the moment...

    Opera is not supported at the moment...


    Yes, I know...
    The problem is that we don't support Opera at the moment (we don't need it, we are a bit short with our roadmaps and with Opera some events (such the Backspace and the Delete) are not easyly stoppable...).
    We will support Opera when we will find the time to look at the problem...
    All the patches are welcome...

  10. #10
    Ext User fother's Avatar
    Join Date
    Sep 2007
    Location
    Brazil
    Posts
    744
    Vote Rating
    -1
    fother is an unknown quantity at this point

      0  

    Default


    Yesterday evening I tried to tamper with the script .. Most received no positive result