(function($){
    var isIphone=(window.orientation!=undefined);
    $.extend({
        mask:{
            rules:{
                'z':/[a-z]/,
                'Z':/[A-Z]/,
                'a':/[a-zA-Z]/,
                '*':/[0-9a-zA-Z]/,
                '@':/[0-9a-zA-ZçÇá� ãéèíìóòõúùü]/
            },
            fixedChars:'[(),.:/ -]',
            keyRepresentation:{
                8:'backspace',
                9:'tab',
                13:'enter',
                16:'shift',
                17:'control',
                18:'alt',
                27:'esc',
                33:'page up',
                34:'page down',
                35:'end',
                36:'home',
                37:'left',
                38:'up',
                39:'right',
                40:'down',
                45:'insert',
                46:'delete',
                116:'f5',
                224:'command'
            },
            iphoneKeyRepresentation:{
                10:'go',
                127:'delete'
            },
            signals:{
                '+':'',
                '-':'-'
            },
            options:{
                attr:'alt',
                mask:null,
                type:'fixed',
                defaultValue:'',
                signal:false,
                onInvalid:function(){},
                onValid:function(){},
                onOverflow:function(){}
            },
        masks:{
            'phone':{
                mask:'(99) 9999-9999'
            },
            'phone-us':{
                mask:'(999) 9999-9999'
            },
            'rg':{
                mask:'9999999999'
            },
            'cpf':{
                mask:'999.999.999-99'
            },
            'cnpj':{
                mask:'99.999.999/9999-99'
            },
            'ie':{
                mask:'999/9999999'
            },
            'pis_pasep':{
                mask:'999.99999.99-9'
            },
            'cgc_te':{
                mask:'999/999999-9'
            },
            'ctps_serie':{
                mask:'999-9'
            },
            'agencia':{
                mask:'9999-9'
            },
            'conta_agencia':{
                mask:'9.999-9'
            },
            'date':{
                mask:'39/19/9999'
            },
            'date-us':{
                mask:'19/39/9999'
            },
            'cep':{
                mask:'99999-999'
            },
            'hora':{
                mask:'29:59'
            },
            'cc':{
                mask:'9999 9999 9999 9999'
            },
            'integer':{
                mask:'999.999.999.999',
                type:'reverse'
            },
            'valor':{
                mask:'99,999.999.99',
                type:'reverse',
                defaultValue:'000'
            },
            'decimal_area':{
                mask:'9.999.999.99',
                type:'reverse',
                defaultValue:'00'
            },
            'decimal_um':{
                mask:'99,999',
                type:'fixed',
                defaultValue:'000'
            },
            'decimal-us':{
                mask:'99.999,999,999,999',
                type:'reverse',
                defaultValue:'000'
            },
            'signed-decimal':{
                mask:'99,999.999.999.999',
                type:'reverse',
                defaultValue:'+000'
            },
            'signed-decimal-us':{
                mask:'99,999.999.999.999',
                type:'reverse',
                defaultValue:'+000'
            }
        },
    init:function(){
        if(!this.hasInit){
            var self=this,i,keyRep=(isIphone)?this.iphoneKeyRepresentation:this.keyRepresentation;
            this.ignore=false;
            this.fixedCharsReg=new RegExp(this.fixedChars);
            this.fixedCharsRegG=new RegExp(this.fixedChars,'g');
            for(i=0;i<=9;i++)this.rules[i]=new RegExp('[0-'+i+']');
            this.keyRep=keyRep;
            this.ignoreKeys=[];
            $.each(keyRep,function(key){
                self.ignoreKeys.push(parseInt(key));
            });
            this.hasInit=true;
        }
    },
    set:function(el,options){
        var maskObj=this,$el=$(el),mlStr='maxLength';
        this.init();
        return $el.each(function(){
            var $this=$(this),o=$.extend({},maskObj.options),attrValue=$this.attr(o.attr),tmpMask='',pasteEvent=maskObj.__getPasteEvent();
            tmpMask=(typeof options=='string')?options:(attrValue!='')?attrValue:null;
            if(tmpMask)o.mask=tmpMask;
            if(maskObj.masks[tmpMask])o=$.extend(o,maskObj.masks[tmpMask]);
            if(typeof options=='object')o=$.extend(o,options);
            if($.metadata)o=$.extend(o,$this.metadata());
            if(o.mask!=null){
                if($this.data('mask'))maskObj.unset($this);
                var defaultValue=o.defaultValue,mlValue=$this.attr(mlStr),reverse=(o.type=='reverse');
                o=$.extend({},o,{
                    maxlength:mlValue,
                    maskArray:o.mask.split(''),
                    maskNonFixedCharsArray:o.mask.replace(maskObj.fixedCharsRegG,'').split('')
                    });
                if(reverse)$this.css('text-align','right');
                if($this.val()!='')$this.val(maskObj.string($this.val(),o));
                else if(defaultValue!='')$this.val(maskObj.string(defaultValue,o));
                $this.data('mask',o);
                $this.removeAttr(mlStr);
                $this.bind('keydown',{
                    func:maskObj._keyDown,
                    thisObj:maskObj
                },maskObj._onMask).bind('keyup',{
                    func:maskObj._keyUp,
                    thisObj:maskObj
                },maskObj._onMask).bind('keypress',{
                    func:maskObj._keyPress,
                    thisObj:maskObj
                },maskObj._onMask).bind(pasteEvent,{
                    func:maskObj._paste,
                    thisObj:maskObj
                },maskObj._delayedOnMask);
            }
        });
},
unset:function(el){
    var $el=$(el),_this=this;
    return $el.each(function(){
        var $this=$(this);
        if($this.data('mask')){
            var maxLength=$this.data('mask').maxlength,pasteEvent=_this.__getPasteEvent();
            if(maxLength!=-1)$this.attr('maxLength',maxLength);
            $this.unbind('keydown',_this._onMask).unbind('keypress',_this._onMask).unbind('keyup',_this._onMask).unbind(pasteEvent,_this._delayedOnMask).removeData('mask');
        }
    });
},
string:function(str,options){
    this.init();
    var o={};

    if(typeof str!='string')str=String(str);
    switch(typeof options){
        case'string':
            if(this.masks[options])o=$.extend(o,this.masks[options]);else o.mask=options;
            break;
        case'object':
            o=options;
    }
    if((o.type=='reverse')&&o.defaultValue){
        if(typeof this.signals[o.defaultValue.charAt(0)]!='undefined'){
            var maybeASignal=str.charAt(0);
            o.signal=(typeof this.signals[maybeASignal]!='undefined')?this.signals[maybeASignal]:this.signals[o.defaultValue.charAt(0)];
            o.defaultValue=o.defaultValue.substring(1);
        }
    }
return this.__maskArray(str.split(''),o.mask.replace(this.fixedCharsRegG,'').split(''),o.mask.split(''),o.type,o.defaultValue,o.signal);
},
unmaskedVal:function(el){
    return $(el).val().replace($.mask.fixedCharsRegG,'');
},
_onMask:function(e){
    var thisObj=e.data.thisObj,o={};

    o._this=e.target;
    o.$this=$(o._this);
    if(o.$this.attr('readonly'))return true;
    o.value=o.$this.val();
    o.nKey=thisObj.__getKeyNumber(e);
    o.range=thisObj.__getRange(o._this);
    o.valueArray=o.value.split('');
    o.data=o.$this.data('mask');
    o[o.data.type]=true;
    return e.data.func.call(thisObj,e,o);
},
_delayedOnMask:function(e){
    e.type='paste';
    setTimeout(function(){
        e.data.thisObj._onMask(e);
    },1);
},
_keyDown:function(e,o){
    this.ignore=($.inArray(o.nKey,this.ignoreKeys)>-1);
    if(this.ignore){
        var rep=this.keyRep[o.nKey];
        o.data.onValid.call(o._this,rep?rep:'',o.nKey);
    }
    return isIphone?this._keyPress(e,o):true;
},
_keyUp:function(e,o){
    if(o.nKey==9&&($.browser.safari||$.browser.msie))return true;
    return(!o.infinite)?this._paste(e,o):true;
},
_paste:function(e,o){
    if(o.reverse)this.__changeSignal(e.type,o);
    var $thisVal=this.__maskArray(o.valueArray,o.data.maskNonFixedCharsArray,o.data.maskArray,o.data.type,o.data.defaultValue,o.data.signal);
    o.$this.val($thisVal);
    if(!o.reverse&&o.data.defaultValue.length&&(o.range.start==o.range.end))
        this.__setRange(o._this,o.range.start,o.range.end);
    if(($.browser.msie||$.browser.safari)&&!o.reverse)this.__setRange(o._this,o.range.start,o.range.end);
    return true;
},
_keyPress:function(e,o){
    if(this.ignore||e.ctrlKey||e.metaKey||e.altKey)return true;
    if(o.reverse)this.__changeSignal(e.type,o);
    var c=String.fromCharCode(o.nKey),rangeStart=o.range.start,rawValue=o.value,maskArray=o.data.maskArray;
    if(o.reverse){
        var valueStart=rawValue.substr(0,rangeStart),valueEnd=rawValue.substr(o.range.end,rawValue.length);
        rawValue=(valueStart+c+valueEnd);
        if(o.data.signal&&(rangeStart-o.data.signal.length>0))rangeStart-=o.data.signal.length;
    }
    var valueArray=rawValue.replace(this.fixedCharsRegG,'').split(''),extraPos=this.__extraPositionsTill(rangeStart,maskArray);
    o.rsEp=rangeStart+extraPos;
    if(o.infinite)o.rsEp=0;
    if(!this.rules[maskArray[o.rsEp]]){
        o.data.onOverflow.call(o._this,c,o.nKey);
        return false;
    }
    else if(!this.rules[maskArray[o.rsEp]].test(c)){
        o.data.onInvalid.call(o._this,c,o.nKey);
        return false;
    }
    else o.data.onValid.call(o._this,c,o.nKey);
    var $thisVal=this.__maskArray(valueArray,o.data.maskNonFixedCharsArray,maskArray,o.data.type,o.data.defaultValue,o.data.signal,extraPos);
    o.$this.val($thisVal);
    return(o.reverse)?this._keyPressReverse(e,o):(o.fixed)?this._keyPressFixed(e,o):true;
},
_keyPressFixed:function(e,o){
    if(o.range.start==o.range.end){
        if((o.rsEp==0&&o.value.length==0)||o.rsEp<o.value.length)
            this.__setRange(o._this,o.rsEp,o.rsEp+1);
    }
    else
        this.__setRange(o._this,o.range.start,o.range.end);
    return true;
},
_keyPressReverse:function(e,o){
    if($.browser.msie&&((o.rangeStart==0&&o.range.end==0)||o.rangeStart!=o.range.end))
        this.__setRange(o._this,o.value.length);
    return false;
},
__changeSignal:function(eventType,o){
    if(o.data.signal!==false){
        var inputChar=(eventType=='paste')?o.value.charAt(0):String.fromCharCode(o.nKey);
        if(this.signals&&(typeof this.signals[inputChar]!='undefined')){
            o.data.signal=this.signals[inputChar];
        }
    }
},
__getPasteEvent:function(){
    return($.browser.opera||($.browser.mozilla&&parseFloat($.browser.version.substr(0,3))<1.9))?'input':'paste';
},
__getKeyNumber:function(e){
    return(e.charCode||e.keyCode||e.which);
},
__maskArray:function(valueArray,maskNonFixedCharsArray,maskArray,type,defaultValue,signal,extraPos){
    if(type=='reverse')valueArray.reverse();
    valueArray=this.__removeInvalidChars(valueArray,maskNonFixedCharsArray);
    if(defaultValue)valueArray=this.__applyDefaultValue.call(valueArray,defaultValue);
    valueArray=this.__applyMask(valueArray,maskArray,extraPos);
    switch(type){
        case'reverse':
            valueArray.reverse();
            return(signal||'')+valueArray.join('').substring(valueArray.length-maskArray.length);
        case'infinite':
            return valueArray.join('');
        default:
            return valueArray.join('').substring(0,maskArray.length);
    }
    return'';
},
__applyDefaultValue:function(defaultValue){
    var defLen=defaultValue.length,thisLen=this.length,i;
    for(i=thisLen-1;i>=0;i--){
        if(this[i]==defaultValue.charAt(0))this.pop();else break;
    }
    for(i=0;i<defLen;i++)if(!this[i])
        this[i]=defaultValue.charAt(i);return this;
},
__removeInvalidChars:function(valueArray,maskNonFixedCharsArray){
    for(var i=0;i<valueArray.length;i++){
        if(maskNonFixedCharsArray[i]&&this.rules[maskNonFixedCharsArray[i]]&&!this.rules[maskNonFixedCharsArray[i]].test(valueArray[i])){
            valueArray.splice(i,1);
            i--;
        }
    }
return valueArray;
},
__applyMask:function(valueArray,maskArray,plus){
    if(typeof plus=='undefined')plus=0;
    for(var i=0;i<valueArray.length+plus;i++){
        if(maskArray[i]&&this.fixedCharsReg.test(maskArray[i]))
            valueArray.splice(i,0,maskArray[i]);
    }
    return valueArray;
},
__extraPositionsTill:function(rangeStart,maskArray){
    var extraPos=0;
    while(this.fixedCharsReg.test(maskArray[rangeStart])){
        rangeStart++;
        extraPos++;
    }
    return extraPos;
},
__setRange:function(input,start,end){
    if(typeof end=='undefined')end=start;
    if(input.setSelectionRange){
        input.setSelectionRange(start,end);
    }else{
        var range=input.createTextRange();
        range.collapse();
        range.moveStart('character',start);
        range.moveEnd('character',end-start);
        range.select();
    }
},
__getRange:function(input){
    if(!$.browser.msie)return{
        start:input.selectionStart,
        end:input.selectionEnd
        };

    var pos={
        start:0,
        end:0
    },range=document.selection.createRange();
    pos.start=0-range.duplicate().moveStart('character',-100000);
    pos.end=pos.start+range.text.length;
    return pos;
}
}
});
$.fn.extend({
    setMask:function(options){
        return $.mask.set(this,options);
    },
    unsetMask:function(){
        return $.mask.unset(this);
    },
    unmaskedVal:function(){
        return $.mask.unmaskedVal(this[0]);
    }
});
})(jQuery);
