/** * jquery.meio.mask.js * @author: fabiomcosta * @version: 1.1.3 * * Created by Fabio M. Costa on 2008-09-16. Please report any bug at http://www.meiocodigo.com * * Copyright (c) 2008 Fabio M. Costa http://www.meiocodigo.com * * The MIT License (http://www.opensource.org/licenses/mit-license.php) * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: *  * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. *  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */(function($){        var isIphone = (window.orientation != undefined),                // browsers like firefox2 and before and opera doenst have the onPaste event, but the paste feature can be done with the onInput event.                pasteEvent = (($.browser.opera || ($.browser.mozilla && parseFloat($.browser.version.substr(0,3)) < 1.9 ))? 'input': 'paste');        $.event.special.paste = {                setup: function() {                if(this.addEventListener)                        this.addEventListener(pasteEvent, pasteHandler, false);                        else if (this.attachEvent)                                this.attachEvent(pasteEvent, pasteHandler);                },                teardown: function() {                        if(this.removeEventListener)                        this.removeEventListener(pasteEvent, pasteHandler, false);                        else if (this.detachEvent)                                this.detachEvent(pasteEvent, pasteHandler);                }        };        // the timeout is set because we can't get the value from the input without it        function pasteHandler(e){                var self = this;                e = $.event.fix(e || window.e);                e.type = 'paste';                // Execute the right handlers by setting the event type to paste                setTimeout(function(){ $.event.handle.call(self, e); }, 1);        };        $.extend({                mask : {                        // the mask rules. You may add yours!                        // number rules will be overwritten                        rules : {                                'z': /[a-z]/,                                'Z': /[A-Z]/,                                'a': /[a-zA-Z]/,                                '*': /[0-9a-zA-Z]/,                                '@': /[0-9a-zA-ZçÇáàãâéèêíìóòôõúùü]/                        },                        // these keys will be ignored by the mask.                        // all these numbers where obtained on the keydown event                        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',                                123 : 'f12',                                224	: 'command'                        },                        iphoneKeyRepresentation : {                                10	: 'go',                                127	: 'delete'                        },                        signals : {                                '+' : '',                                '-' : '-'                        },                        // default settings for the plugin                        options : {                                attr: 'alt', // an attr to look for the mask name or the mask itself                                mask: null, // the mask to be used on the input                                type: 'fixed', // the mask of this mask                                maxLength: -1, // the maxLength of the mask                                defaultValue: '', // the default value for this input                                signal: false, // this should not be set, to use signal at masks put the signal you want ('-' or '+') at the default value of this mask.                                                           // See the defined masks for a better understanding.                                textAlign: true, // use false to not use text-align on any mask (at least not by the plugin, you may apply it using css)                                selectCharsOnFocus: true, // select all chars from input on its focus                                autoTab: true, // auto focus the next form element when you type the mask completely                                setSize: false, // sets the input size based on the length of the mask (work with fixed and reverse masks only)                                fixedChars : '[(),.:/ -]', // fixed chars to be used on the masks. You may change it for your needs!                                onInvalid : function(){},                                onValid : function(){},                                onOverflow : function(){}                        },                        // masks. You may add yours!                        // Ex: $.fn.setMask.masks.msk = {mask: '999'}                        // and then if the 'attr' options value is 'alt', your input should look like:                        // <input type="text" name="some_name" id="some_name" alt="msk" />                        masks : {                                'phone'				: { mask : '(99) 9999-9999' },                                'phone-us'			: { mask : '(999) 999-9999' },                                'cpf'				: { mask : '999.999.999-99' }, // cadastro nacional de pessoa fisica                                'cnpj'				: { mask : '99.999.999/9999-99' },                                'date'				: { mask : '39/19/9999' }, //uk date                                'date-us'			: { mask : '19/39/9999' },                                'cep'				: { mask : '99999-999' },                                'time'				: { mask : '29:59' },                                'cc'				: { mask : '9999 9999 9999 9999' }, //credit card mask                                'integer'			: { mask : '999.999.999.999', type : 'reverse' },				                                'decimal'			: { mask : '99,999.999.999.999', type : 'reverse', 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 has not inited...                                if( !this.hasInit ){                                        var self = this, i,                                                keyRep = (isIphone)? this.iphoneKeyRepresentation: this.keyRepresentation;                                        this.ignore = false;                                        // constructs number rules                                        for(i=0; i<=9; i++) this.rules[i] = new RegExp('[0-'+i+']');                                        this.keyRep = keyRep;                                        // ignore keys array creation for iphone or the normal ones                                        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';                                options = options || {};                                this.init();                                return $el.each(function(){                                        if(options.attr) maskObj.options.attr = options.attr;                                        var $this = $(this),                                                o = $.extend({}, maskObj.options),                                                attrValue = $this.attr(o.attr),                                                tmpMask = '';                                        // then we look for the 'attr' option                                        tmpMask = (typeof options == 'string')? options: (attrValue != '')? attrValue: null;                                        if(tmpMask) o.mask = tmpMask;                                        // then we see if it's a defined mask                                        if(maskObj.masks[tmpMask]) o = $.extend(o, maskObj.masks[tmpMask]);                                        // then it looks if the options is an object, if it is we will overwrite the actual options                                        if(typeof options == 'object' && options.constructor != Array) o = $.extend(o, options);                                        //then we look for some metadata on the input                                        if($.metadata) o = $.extend(o, $this.metadata());                                        if(o.mask != null){                                                if($this.data('mask')) maskObj.unset($this);                                                var defaultValue = o.defaultValue,                                                        reverse = (o.type=='reverse'),                                                        fixedCharsRegG = new RegExp(o.fixedChars, 'g');                                                if(o.maxLength == -1) o.maxLength = $this.attr(mlStr);                                                o = $.extend({}, o,{                                                        fixedCharsReg: new RegExp(o.fixedChars),                                                        fixedCharsRegG: fixedCharsRegG,                                                        maskArray: o.mask.split(''),                                                        maskNonFixedCharsArray: o.mask.replace(fixedCharsRegG, '').split('')                                                });                                                //setSize option (this is not removed from the input (while removing the mask) since this would be kind of funky)                                                if((o.type=='fixed' || reverse) && o.setSize && !$this.attr('size')) $this.attr('size', o.mask.length);                                                //sets text-align right for reverse masks                                                if(reverse && o.textAlign) $this.css('text-align', 'right');                                                if(this.value!='' || defaultValue!=''){                                                        // apply mask to the current value of the input or to the default value                                                        var val = maskObj.string((this.value!='')? this.value: defaultValue, o);                                                        //setting defaultValue fixes the reset button from the form                                                        this.defaultValue = val;                                                        $this.val(val);                                                }                                                // compatibility patch for infinite mask, that is now repeat                                                if(o.type=='infinite') o.type = 'repeat';                                                $this.data('mask', o);                                                // removes the maxLength attribute (it will be set again if you use the unset method)                                                $this.removeAttr(mlStr);                                                // setting the input events                                                $this.bind('keydown.mask', {func:maskObj._onKeyDown, thisObj:maskObj}, maskObj._onMask)                                                        .bind('keypress.mask', {func:maskObj._onKeyPress, thisObj:maskObj}, maskObj._onMask)                                                        .bind('keyup.mask', {func:maskObj._onKeyUp, thisObj:maskObj}, maskObj._onMask)                                                        .bind('paste.mask', {func:maskObj._onPaste, thisObj:maskObj}, maskObj._onMask)                                                        .bind('focus.mask', maskObj._onFocus)                                                        .bind('blur.mask', maskObj._onBlur)                                                        .bind('change.mask', maskObj._onChange);                                        }                                });                        },                        //unsets the mask from el                        unset : function(el){                                var $el = $(el);                                return $el.each(function(){                                        var $this = $(this);                                        if($this.data('mask')){                                                var maxLength = $this.data('mask').maxLength;                                                if(maxLength != -1) $this.attr('maxLength', maxLength);                                                $this.unbind('.mask')                                                        .removeData('mask');                                        }                                });                        },                        //masks a string                        string : function(str, options){                                this.init();                                var o={};                                if(typeof str != 'string') str = String(str);                                switch(typeof options){                                        case 'string':                                                // then we see if it's a defined mask	                                                if(this.masks[options]) o = $.extend(o, this.masks[options]);                                                else o.mask = options;                                                break;                                        case 'object':                                                o = options;                                }                                if(!o.fixedChars) o.fixedChars = this.options.fixedChars;                                var fixedCharsReg = new RegExp(o.fixedChars),                                        fixedCharsRegG = new RegExp(o.fixedChars, 'g');                                // insert signal if any                                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(fixedCharsRegG, '').split(''),                                                        o.mask.split(''),                                                        o.type,                                                        o.maxLength,                                                        o.defaultValue,                                                        fixedCharsReg,                                                        o.signal);                        },                        // all the 3 events below are here just to fix the change event on reversed masks.                        // It isn't fired in cases that the keypress event returns false (needed).                        _onFocus: function(e){                                var $this = $(this), dataObj = $this.data('mask');                                dataObj.inputFocusValue = $this.val();                                dataObj.changed = false;                                if(dataObj.selectCharsOnFocus) $this.select();                        },                        _onBlur: function(e){                                var $this = $(this), dataObj = $this.data('mask');                                if(dataObj.inputFocusValue != $this.val() && !dataObj.changed)                                        $this.trigger('change');                        },                        _onChange: function(e){                                $(this).data('mask').changed = true;                        },                        _onMask : function(e){                                var thisObj = e.data.thisObj,                                        o = {};                                o._this = e.target;                                o.$this = $(o._this);                                // if the input is readonly it does nothing                                if(o.$this.attr('readonly')) return true;                                o.data = o.$this.data('mask');                                o[o.data.type] = true;                                o.value = o.$this.val();                                o.nKey = thisObj.__getKeyNumber(e);                                o.range = thisObj.__getRange(o._this);                                o.valueArray = o.value.split('');                                return e.data.func.call(thisObj, e, o);                        },                        _onKeyDown : function(e,o){                                // lets say keypress at desktop == keydown at iphone (theres no keypress at iphone)                                this.ignore = $.inArray(o.nKey, this.ignoreKeys) > -1 || e.ctrlKey || e.metaKey || e.altKey;                                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;                        },                        _onKeyUp : function(e, o){                                //9=TAB_KEY 16=SHIFT_KEY                                //this is a little bug, when you go to an input with tab key                                //it would remove the range selected by default, and that's not a desired behavior                                if(o.nKey==9 || o.nKey==16) return true;                                if(o.data.type=='repeat'){                                        this.__autoTab(o);                                        return true;                                }                                return this._onPaste(e, o);                        },                        _onPaste : function(e,o){                                // changes the signal at the data obj from the input                                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.maxLength,                                        o.data.defaultValue,                                        o.data.fixedCharsReg,                                        o.data.signal                                );                                o.$this.val( $thisVal );                                // this makes the caret stay at first position when                                 // the user removes all values in an input and the plugin adds the default value to it (if it haves one).                                if( !o.reverse && o.data.defaultValue.length && (o.range.start==o.range.end) )                                        this.__setRange(o._this, o.range.start, o.range.end);                                //fix so ie's and safari's caret won't go to the end of the input value.                                if( ($.browser.msie || $.browser.safari) && !o.reverse)                                        this.__setRange(o._this,o.range.start,o.range.end);                                if(this.ignore) return true;                                this.__autoTab(o);                                return true;                        },                        _onKeyPress: function(e, o){                                if(this.ignore) return true;                                // changes the signal at the data obj from the input                                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){                                                // the input value from the range start to the value start                                        var valueStart = rawValue.substr(0, rangeStart),                                                // the input value from the range end to the value end                                                valueEnd = rawValue.substr(o.range.end, rawValue.length);                                        rawValue = valueStart+c+valueEnd;                                        //necessary, if not decremented you will be able to input just the mask.length-1 if signal!=''                                        //ex: mask:99,999.999.999 you will be able to input 99,999.999.99                                        if(o.data.signal && (rangeStart-o.data.signal.length > 0)) rangeStart-=o.data.signal.length;                                }                                var valueArray = rawValue.replace(o.data.fixedCharsRegG, '').split(''),                                        // searches for fixed chars begining from the range start position, till it finds a non fixed                                        extraPos = this.__extraPositionsTill(rangeStart, maskArray, o.data.fixedCharsReg);                                o.rsEp = rangeStart+extraPos;                                if(o.repeat) o.rsEp = 0;                                // if the rule for this character doesnt exist (value.length is bigger than mask.length)                                // added a verification for maxLength in the case of the repeat type mask                                if( !this.rules[maskArray[o.rsEp]] || (o.data.maxLength != -1 && valueArray.length >= o.data.maxLength && o.repeat)){                                        // auto focus on the next input of the current form                                        o.data.onOverflow.call(o._this, c, o.nKey);                                        return false;                                }                                // if the new character is not obeying the law... :P                                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.maxLength,                                        o.data.defaultValue,                                        o.data.fixedCharsReg,                                        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){                                        // the 0 thing is cause theres a particular behavior i wasnt liking when you put a default                                        // value on a fixed mask and you select the value from the input the range would go to the                                        // end of the string when you enter a char. with this it will overwrite the first char wich is a better behavior.                                        // opera fix, cant have range value bigger than value length, i think it loops thought the input value...                                        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){                                //fix for ie                                //this bug was pointed by Pedro Martins                                //it fixes a strange behavior that ie was having after a char was inputted in a text input that                                //had its content selected by any range                                 if($.browser.msie && ((o.range.start==0 && o.range.end==0) || o.range.start != o.range.end ))                                        this.__setRange(o._this, o.value.length);                                return false;                        },                        __autoTab: function(o){                                if(o.data.autoTab                                        && (                                                (                                                        o.$this.val().length >= o.data.maskArray.length                                                         && !o.repeat                                                 ) || (                                                        o.data.maxLength != -1                                                        && o.valueArray.length >= o.data.maxLength                                                        && o.repeat                                                )                                        )                                ){                                        var nextEl = this.__getNextInput(o._this, o.data.autoTab);                                        if(nextEl){                                                o.$this.trigger('blur');                                                nextEl.focus().select();                                        }                                }                        },                        // changes the signal at the data obj from the input			                        __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];                                        }                                }                        },                        __getKeyNumber : function(e){                                return (e.charCode||e.keyCode||e.which);                        },                        // this function is totaly specific to be used with this plugin, youll never need it                        // it gets the array representing an unmasked string and masks it depending on the type of the mask                        __maskArray : function(valueArray, maskNonFixedCharsArray, maskArray, type, maxlength, defaultValue, fixedCharsReg, signal, extraPos){                                if(type == 'reverse') valueArray.reverse();                                valueArray = this.__removeInvalidChars(valueArray, maskNonFixedCharsArray, type=='repeat'||type=='infinite');                                if(defaultValue) valueArray = this.__applyDefaultValue.call(valueArray, defaultValue);                                valueArray = this.__applyMask(valueArray, maskArray, extraPos, fixedCharsReg);                                switch(type){                                        case 'reverse':                                                valueArray.reverse();                                                return (signal || '')+valueArray.join('').substring(valueArray.length-maskArray.length);                                        case 'infinite': case 'repeat':                                                var joinedValue = valueArray.join('');                                                return (maxlength != -1 && valueArray.length >= maxlength)? joinedValue.substring(0, maxlength): joinedValue;                                        default:                                                return valueArray.join('').substring(0, maskArray.length);                                }                                return '';                        },                        // applyes the default value to the result string                        __applyDefaultValue : function(defaultValue){                                var defLen = defaultValue.length,thisLen = this.length,i;                                //removes the leading chars                                for(i=thisLen-1;i>=0;i--){                                        if(this[i]==defaultValue.charAt(0)) this.pop();                                        else break;                                }                                // apply the default value                                for(i=0;i<defLen;i++) if(!this[i])                                        this[i] = defaultValue.charAt(i);                                return this;                        },                        // Removes values that doesnt match the mask from the valueArray                        // Returns the array without the invalid chars.                        __removeInvalidChars : function(valueArray, maskNonFixedCharsArray, repeatType){                                // removes invalid chars                                for(var i=0, y=0; i<valueArray.length; i++ ){                                        if( maskNonFixedCharsArray[y] &&                                                this.rules[maskNonFixedCharsArray[y]] &&                                                !this.rules[maskNonFixedCharsArray[y]].test(valueArray[i]) ){                                                        valueArray.splice(i,1);                                                        if(!repeatType) y--;                                                        i--;                                        }                                        if(!repeatType) y++;                                }                                return valueArray;                        },                        // Apply the current input mask to the valueArray and returns it.                         __applyMask : function(valueArray, maskArray, plus, fixedCharsReg){                                if( typeof plus == 'undefined' ) plus = 0;                                // apply the current mask to the array of chars                                for(var i=0; i<valueArray.length+plus; i++ ){                                        if( maskArray[i] && fixedCharsReg.test(maskArray[i]) )                                                valueArray.splice(i, 0, maskArray[i]);                                }                                return valueArray;                        },                        // searches for fixed chars begining from the range start position, till it finds a non fixed                        __extraPositionsTill : function(rangeStart, maskArray, fixedCharsReg){                                var extraPos = 0;                                while(fixedCharsReg.test(maskArray[rangeStart++])){                                        extraPos++;                                }                                return extraPos;                        },                        __getNextInput: function(input, selector){                                var formEls = input.form.elements,                                        initialInputIndex = $.inArray(input, formEls) + 1,                                        $input = null,                                        i;                                // look for next input on the form of the pased input                                for(i = initialInputIndex; i < formEls.length; i++){                                        $input = $(formEls[i]);                                        if(this.__isNextInput($input, selector))                                                return $input;                                }                                var forms = document.forms,                                        initialFormIndex = $.inArray(input.form, forms) + 1,                                        y, tmpFormEls = null;                                // look for the next forms for the next input                                for(y = initialFormIndex; y < forms.length; y++){                                        tmpFormEls = forms[y].elements;                                        for(i = 0; i < tmpFormEls.length; i++){                                                $input = $(tmpFormEls[i]);                                                if(this.__isNextInput($input, selector))                                                        return $input;                                        }                                }                                return null;                        },                        __isNextInput: function($formEl, selector){                                var formEl = $formEl.get(0);                                return formEl                                        && (formEl.offsetWidth > 0 || formEl.offsetHeight > 0)                                        && formEl.nodeName != 'FIELDSET'                                        && (selector === true || (typeof selector == 'string' && $formEl.is(selector)));                        },                        // http://www.bazon.net/mishoo/articles.epl?art_id=1292                        __setRange : function(input, start, end) {                                if(typeof end == 'undefined') end = start;                                if (input.setSelectionRange){                                        input.setSelectionRange(start, end);                                }                                else{                                        // assumed IE                                        var range = input.createTextRange();                                        range.collapse();                                        range.moveStart('character', start);                                        range.moveEnd('character', end - start);                                        range.select();                                }                        },                        // adaptation from http://digitarald.de/project/autocompleter/                        __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;                        },                        //deprecated                        unmaskedVal : function(el){                                return $(el).val().replace($.mask.fixedCharsRegG, '');                        }                }        });        $.fn.extend({                setMask : function(options){                        return $.mask.set(this, options);                },                unsetMask : function(){                        return $.mask.unset(this);                },                //deprecated                unmaskedVal : function(){                        return $.mask.unmaskedVal(this[0]);                }        });})(jQuery);