// var dateTimeField = new Ext.form.TextField({plugins: [new Ext.ux.InputTextMask('99/99/9999 99:99', clearInvalid:true)]});

/*
Example 1 - 3 numeric values:
new Ext.form.TextField({plugins: [new Ext.ux.InputTextMask('999', true)]});

Example 2 - 4 alphanumeric values:
new Ext.form.TextField({plugins: [new Ext.ux.InputTextMask('AAAA', true)]});

Example 3 - 2 character (all kinds):
new Ext.form.TextField({plugins: [new Ext.ux.InputTextMask('X.XX.X', true)]});
*/

/**
 * 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.
 */

Ext.namespace('Ext.ux');

Ext.ux.InputTextMask = function(mask, clearWhenInvalid) {

    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 = null;
            } 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.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){
          this.field.getEl().on('mousedown',this.setPreviousValue,this);
          this.field.getEl().on('input',this.onInput,this);
        }
    },
    onInput: function(){
      this.startTask(false);
    },

    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.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(cursorPos.previousValue.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.unicode==Ext.EventObject.BACKSPACE ||
      keyCode.unicode==Ext.EventObject.DELETE){
        return(true);
      }
      return(false);
    },

    processMaskFormatting : function(e,type) {
        this.oldCursorPos=null;
        var cursorPos = this.getCursorPosition();
        var keyCode = this.getKeyCode(e,cursorPos);
        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(type=='keydown' && !this.isManagedByKeyDown(keyCode)){
            return true;
        }
        if(type=='keypress' && this.isManagedByKeyDown(keyCode)){
            return true;
        }
        if(this.handleEventBubble(e, keyCode)){
            return true;
        }
        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(keyCode){
      if(!keyCode.isShiftPressed && (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 ||
        keyCode.unicode==Ext.EventObject.F5)){
          return(true);
      }
      return(false);
    },

    handleEventBubble : function(keyEvent, keyCode) {
        try {
            if(keyCode && this.isManagedByBrowser(keyCode)){
                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.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) {
        var keycode = {};
        keycode.unicode =onKeyDownEvent.getKey();
        keycode.isShiftPressed = onKeyDownEvent.shiftKey;
        keycode.isDelete=(onKeyDownEvent.getKey() == Ext.EventObject.DELETE)? true: false;
        keycode.isTab = (onKeyDownEvent.getCharCode() == Ext.EventObject.TAB)? true: false;
        keycode.isBackspace = (onKeyDownEvent.getCharCode() == Ext.EventObject.BACKSPACE)? true: false;
        keycode.isLeftOrRightArrow = (onKeyDownEvent.getCharCode() == Ext.EventObject.LEFT || onKeyDownEvent.getCharCode() == 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);
    }

};

// Add escape prototype feature to RegExp object
// text.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$0');
// text.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g, '\\$1');

if(!RegExp.escape) {
    RegExp.escape = function(text){
    var sp;
    if(!arguments.callee.sRE){
        sp=['/','.','*','+','?','|','(',')','[',']','{','}','\\'];
        arguments.callee.sRE = new RegExp('(\\' + sp.join('|\\') + ')','g');
    }
    return text.replace(arguments.callee.sRE, '\\$1');
    };
};
