(function($) {
	$.fn.caret=function(begin,end){	if(this.length==0){ return; }if (typeof begin == 'number') { end = (typeof end == 'number')?end:begin; return this.each(function(){ if(this.setSelectionRange){ this.focus(); this.setSelectionRange(begin,end); }else if (this.createTextRange){ var range = this.createTextRange(); range.collapse(true); range.moveEnd('character', end); range.moveStart('character', begin); range.select(); } }); } else { if (this[0].setSelectionRange){ begin = this[0].selectionStart; end = this[0].selectionEnd; }else if (document.selection && document.selection.createRange){ var range = document.selection.createRange(); begin = 0 - range.duplicate().moveStart('character', -100000); end = begin + range.text.length; } return {begin:begin,end:end}; } };
	var charMap={
		'9':"[0-9]",
		'a':"[A-Za-z]",
		'*':"[A-Za-z0-9]"
	};
	$.mask={ addPlaceholder : function(c,r){ charMap[c]=r; } };
	$.fn.unmask=function(){ return this.trigger("unmask"); };
	$.fn.mask = function(mask,settings) { settings = $.extend({ placeholder: "_",  completed: null }, settings);
		var re = new RegExp("^"+ $.map( mask.split(""), function(c,i){ return charMap[c]||((/[A-Za-z0-9]/.test(c)?"":"\\")+c); }).join('')+ "$");
		return this.each(function(){
			var input=$(this); var buffer=new Array(mask.length); var locked=new Array(mask.length); var valid=false; var ignore=false; var firstNonMaskPos=null;
			$.each( mask.split(""), function(i,c){ locked[i]=(charMap[c]==null); buffer[i]=locked[i]?c:settings.placeholder; if(!locked[i] && firstNonMaskPos==null) { firstNonMaskPos=i; } });
			function focusEvent(){ checkVal(); writeBuffer(); setTimeout(function(){ $(input[0]).caret(valid?mask.length:firstNonMaskPos); },0); };
			function keydownEvent(e){ var pos=$(this).caret(); var k = e.keyCode; ignore=(k < 16 || (k > 16 && k < 32 ) || (k > 32 && k < 41)); if((pos.begin-pos.end)!=0 && (!ignore || k==8 || k==46)){ clearBuffer(pos.begin,pos.end); } if(k==8){ while(pos.begin-->=0){ if(!locked[pos.begin]){ buffer[pos.begin]=settings.placeholder; if($.browser.opera){ s=writeBuffer(); input.val(s.substring(0,pos.begin)+" "+s.substring(pos.begin)); $(this).caret(pos.begin+1); }else{ writeBuffer(); $(this).caret(Math.max(firstNonMaskPos,pos.begin)); } return false; } } }else if(k==46){ clearBuffer(pos.begin,pos.begin+1); writeBuffer(); $(this).caret(Math.max(firstNonMaskPos,pos.begin)); return false; }else if (k==27){ clearBuffer(0,mask.length); writeBuffer(); $(this).caret(firstNonMaskPos); return false; } };
			function keypressEvent(e){ if(ignore){ ignore=false; return (e.keyCode == 8)? false: null; } e=e||window.event; var k=e.charCode||e.keyCode||e.which; var pos=$(this).caret(); if(e.ctrlKey || e.altKey){ return true; }else if ((k>=41 && k<=122) ||k==32 || k>186){ var p=seekNext(pos.begin-1); if(p<mask.length){ if(new RegExp(charMap[mask.charAt(p)]).test(String.fromCharCode(k))){ buffer[p]=String.fromCharCode(k); writeBuffer(); var next=seekNext(p); $(this).caret(next); if(settings.completed && next == mask.length){ settings.completed.call(input); } } } } return false; };
			function clearBuffer(start,end){ for(var i=start;i<end&&i<mask.length;i++){ if(!locked[i]){  buffer[i]=settings.placeholder; } } };
			function writeBuffer(){ return input.val(buffer.join('')).val();  };
			function checkVal(){ var test=input.val(); var pos=0; for(var i=0;i<mask.length;i++){ if(!locked[i]){ while(pos++<test.length){ var reChar=new RegExp(charMap[mask.charAt(i)]); if(test.charAt(pos-1).match(reChar)){ buffer[i]=test.charAt(pos-1); break; } } } } var s=writeBuffer(); if(!s.match(re)){ input.val(""); clearBuffer(0,mask.length); valid=false; }else{ valid=true; } };
			function seekNext(pos){ while(++pos<mask.length){ if(!locked[pos]) {return pos; } } return mask.length; };
			input.one("unmask",function(){ input.unbind("focus",focusEvent); input.unbind("blur",checkVal); input.unbind("keydown",keydownEvent); input.unbind("keypress",keypressEvent); if ($.browser.msie){  this.onpaste= null; } else if ($.browser.mozilla){ this.removeEventListener('input',checkVal,false); } });
			input.bind("focus",focusEvent);
			input.bind("blur",checkVal);
			input.bind("keydown",keydownEvent);
			input.bind("keypress",keypressEvent);
			if ($.browser.msie){ this.onpaste= function(){setTimeout(checkVal,0);}; } else if ($.browser.mozilla) { this.addEventListener('input',checkVal,false); }
			checkVal();
		});
	};
})(jQuery);