text: /*
   * 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.2
   * Release: 2007-11-29
   */ 
  (function($) {
  
  	//Helper Function for Caret positioning
  	$.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};
          }       
  	};
  
  	//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 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;  			//Variable for ignoring control keys
  			var firstNonMaskPos=null; 
  			
  			//Build buffer layout from mask & determine the first non masked character			
  			$.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));
  				
  				//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.								
  								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){//delete
  					clearBuffer(pos.begin,pos.begin+1);
  					writeBuffer();
  					$(this).caret(Math.max(firstNonMaskPos,pos.begin));					
  					return false;
  				}else if (k==27){//escape
  					clearBuffer(0,mask.length);
  					writeBuffer();
  					$(this).caret(firstNonMaskPos);					
  					return false;
  				}									
  			};
  			
  			function keypressEvent(e){					
  				if(ignore){
  					ignore=false;
  					//Fixes Mac FF bug on backspace
  					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){//Ignore
  					return true;
  				}else if ((k>=41 && k<=122) ||k==32 || k>186){//typeable characters
  					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(){	
  				//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);
  					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);
  			//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);
  				
  			checkVal();//Perform initial check for existing values
  		});
  	};
  })(jQuery);