/* * Copyright (c) 2007 Josh Bush (digitalbush.com) *  * 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.  */ /* * Version: 1.1 * Release: 2007-09-08 */ (function($) {	//Helper Functions for Caret positioning	function getCaretPosition(ctl){		var res = {begin: 0, end: 0 };		if (ctl.setSelectionRange){			res.begin = ctl.selectionStart;			res.end = ctl.selectionEnd;		}else if (document.selection && document.selection.createRange){			var range = document.selection.createRange();						res.begin = 0 - range.duplicate().moveStart('character', -100000);			res.end = res.begin + range.text.length;		}		return res;	};	function setCaretPosition(ctl, pos){				if(ctl.setSelectionRange){			ctl.focus();			ctl.setSelectionRange(pos,pos);		}else if (ctl.createTextRange){			var range = ctl.createTextRange();			range.collapse(true);			range.moveEnd('character', pos);			range.moveStart('character', pos);			range.select();		}	};		//Predefined character definitions	var charMap={		'9':"[0-9]",		'a':"[A-Za-z]",		'*':"[A-Za-z0-9]"	};		//Helper method to inject character definitions	$.mask={		addPlaceholder : function(c,r){			charMap[c]=r;		}	};		$.fn.unmask=function(){		return this.trigger("unmask");	};		//Main Method	$.fn.mask = function(mask,settings) {			settings = $.extend({			placeholder: "_",			completed: null		}, settings);					//Build Regex for format validation		var reString="^";			for(var i=0;i<mask.length;i++)			reString+=(charMap[mask.charAt(i)] || ("\\"+mask.charAt(i)));							reString+="$";		var re = new RegExp(reString);		return this.each(function(){					var input=$(this);			var buffer=new Array(mask.length);			var locked=new Array(mask.length);					//Build buffer layout from mask			for(var i=0;i<mask.length;i++){				locked[i]=charMap[mask.charAt(i)]==null;				buffer[i]=locked[i]?mask.charAt(i):settings.placeholder;								}						/*Event Bindings*/			function focusEvent(){									checkVal();				writeBuffer();				setTimeout(function(){					setCaretPosition(input[0],0);				},0);			};						input.bind("focus",focusEvent);			input.bind("blur",checkVal);						//Paste events for IE and Mozilla thanks to Kristinn Sigmundsson			if ($.browser.msie) 				this.onpaste= function(){setTimeout(checkVal,0);};                     			else if ($.browser.mozilla)				this.addEventListener('input',checkVal,false);						var ignore=false;  //Variable for ignoring control keys						function keydownEvent(e){				var pos=getCaretPosition(this);																	var k = e.keyCode;				ignore=(k < 16 || (k > 16 && k < 32 ) || (k > 32 && k < 41));								//delete selection before proceeding				if((pos.begin-pos.end)!=0 && (!ignore || k==8 || k==46)){					clearBuffer(pos.begin,pos.end);				}					//backspace and delete get special treatment				if(k==8){//backspace										while(pos.begin-->=0){						if(!locked[pos.begin]){															buffer[pos.begin]=settings.placeholder;							if($.browser.opera){								//Opera won't let you cancel the backspace, so we'll let it backspace over a dummy character.																writeBuffer(pos.begin);								setCaretPosition(this,pos.begin+1);							}else{								writeBuffer();								setCaretPosition(this,pos.begin);							}																return false;														}					}										}else if(k==46){//delete					clearBuffer(pos.begin,pos.begin+1);					writeBuffer();					setCaretPosition(this,pos.begin);					return false;				}else if (k==27){					clearBuffer(0,mask.length);					writeBuffer();					setCaretPosition(this,0);					return false;				}												};			input.bind("keydown",keydownEvent);			function keypressEvent(e){									if(ignore){					ignore=false;					return;				}				e=e||window.event;				var k=e.charCode||e.keyCode||e.which;				var pos=getCaretPosition(this);									var caretPos=pos.begin;									if(e.ctrlKey || e.altKey){//Ignore					return true;				}else if ((k>=41 && k<=122) ||k==32 || k>186){//typeable characters					while(pos.begin<mask.length){							var reString=charMap[mask.charAt(pos.begin)];						var match;						if(reString){							var reChar=new RegExp(reString);							match=String.fromCharCode(k).match(reChar);						}else{//we're on a mask char, go forward and try again							pos.begin+=1;							pos.end=pos.begin;							caretPos+=1;							continue;						}						if(match)							buffer[pos.begin]=String.fromCharCode(k);						else							return false;//reject char						while(++caretPos<mask.length){//seek forward to next typable position							if(!locked[caretPos])															break;													}						break;					}				}else					return false;												writeBuffer();				if(settings.completed && caretPos>=buffer.length)					settings.completed.call(input);				else					setCaretPosition(this,caretPos);								return false;							};			input.bind("keypress",keypressEvent);			/*Helper Methods*/			function clearBuffer(start,end){				for(var i=start;i<end;i++){					if(!locked[i])						buffer[i]=settings.placeholder;				}							};						function writeBuffer(pos){				var s="";				for(var i=0;i<mask.length;i++){					s+=buffer[i];					if(i==pos)						s+=settings.placeholder;				}				input.val(s);				return s;			};						function checkVal(){					//try to place charcters where they belong				var test=input.val();				var pos=0;				for(var i=0;i<mask.length;i++){					if(!locked[i]){						while(pos++<test.length){							//Regex Test each char here.							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);				}								};						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);			});									});	};})(jQuery);
