2 * Copyright (c) 2007 Josh Bush (digitalbush.com)
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use,
8 * copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
31 //Helper Functions for Caret positioning
32 function getCaretPosition(ctl){
33 var res = {begin: 0, end: 0 };
34 if (ctl.setSelectionRange){
35 res.begin = ctl.selectionStart;
36 res.end = ctl.selectionEnd;
37 }else if (document.selection && document.selection.createRange){
38 var range = document.selection.createRange();
39 res.begin = 0 - range.duplicate().moveStart('character', -100000);
40 res.end = res.begin + range.text.length;
45 function setCaretPosition(ctl, pos){
46 if(ctl.setSelectionRange){
48 ctl.setSelectionRange(pos,pos);
49 }else if (ctl.createTextRange){
50 var range = ctl.createTextRange();
52 range.moveEnd('character', pos);
53 range.moveStart('character', pos);
58 //Predefined character definitions
65 //Helper method to inject character definitions
67 addPlaceholder : function(c,r){
73 $.fn.mask = function(mask,settings) {
79 //Build Regex for format validation
81 for(var i=0;i<mask.length;i++)
82 reString+=(charMap[mask.charAt(i)] || ("\\"+mask.charAt(i)));
84 var re = new RegExp(reString);
86 return this.each(function(){
88 var buffer=new Array(mask.length);
89 var locked=new Array(mask.length);
91 //Build buffer layout from mask
92 for(var i=0;i<mask.length;i++){
93 locked[i]=charMap[mask.charAt(i)]==null;
94 buffer[i]=locked[i]?mask.charAt(i):settings.placeholder;
98 input.focus(function(){
101 setCaretPosition(this,0);
104 input.blur(checkVal);
106 //Paste events for IE and Mozilla thanks to Kristinn Sigmundsson
107 if ($.browser && $.browser.msie)
108 this.onpaste= function(){setTimeout(checkVal,0);};
109 else if ($.browser && $.browser.mozilla)
110 this.addEventListener('input',checkVal,false);
112 var ignore=false; //Variable for ignoring control keys
114 input.keydown(function(e){
115 var pos=getCaretPosition(this);
117 ignore=(k < 16 || (k > 16 && k < 32 ) || (k > 32 && k < 41));
119 //delete selection before proceeding
120 if((pos.begin-pos.end)!=0 && (!ignore || k==8 || k==46)){
121 clearBuffer(pos.begin,pos.end);
123 //backspace and delete get special treatment
125 while(pos.begin-->=0){
126 if(!locked[pos.begin]){
127 buffer[pos.begin]=settings.placeholder;
128 if($.browser && $.browser.opera){
129 //Opera won't let you cancel the backspace, so we'll let it backspace over a dummy character.
130 writeBuffer(pos.begin);
131 setCaretPosition(this,pos.begin+1);
134 setCaretPosition(this,pos.begin);
139 }else if(k==46){//delete
140 clearBuffer(pos.begin,pos.begin+1);
142 setCaretPosition(this,pos.begin);
145 clearBuffer(0,mask.length);
147 setCaretPosition(this,0);
153 input.keypress(function(e){
159 var k=e.charCode||e.keyCode||e.which;
161 var pos=getCaretPosition(this);
162 var caretPos=pos.begin;
164 if(e.ctrlKey || e.altKey){//Ignore
166 }else if ((k>=41 && k<=122) ||k==32 || k>186){//typeable characters
167 while(pos.begin<mask.length){
168 var reString=charMap[mask.charAt(pos.begin)];
171 var reChar=new RegExp(reString);
172 match=String.fromCharCode(k).match(reChar);
173 }else{//we're on a mask char, go forward and try again
181 buffer[pos.begin]=String.fromCharCode(k);
183 return false;//reject char
185 while(++caretPos<mask.length){//seek forward to next typable position
186 if(!locked[caretPos])
195 if(settings.completed && caretPos>=buffer.length)
196 settings.completed.call(input);
198 setCaretPosition(this,caretPos);
204 function clearBuffer(start,end){
205 for(var i=start;i<end;i++){
207 buffer[i]=settings.placeholder;
211 function writeBuffer(pos){
213 for(var i=0;i<mask.length;i++){
216 s+=settings.placeholder;
223 //try to place charcters where they belong
224 var test=input.val();
226 for(var i=0;i<mask.length;i++){
228 while(pos++<test.length){
229 //Regex Test each char here.
230 var reChar=new RegExp(charMap[mask.charAt(i)]);
231 if(test.charAt(pos-1).match(reChar)){
232 buffer[i]=test.charAt(pos-1);
241 clearBuffer(0,mask.length);