installers, RT#16584
[freeside.git] / httemplate / elements / masked_input_1.1.js
1 /***********************************************************************
2                        Masked Input version 1.1
3 ************************************************************************
4 Author: Kendall Conrad
5 Home page: http://www.angelwatt.com/coding/masked_input.php
6 Created:  2008-12-16
7 Modified: 2010-04-14
8 Description:
9 License: This work is licensed under a Creative Commons Attribution-Share Alike
10   3.0 United States License http://creativecommons.org/licenses/by-sa/3.0/us/
11
12 Argument pieces:
13 - elm:        [req] text input node to apply the mask on
14 - format:     [req] string format for the mask
15 - allowed:    [opt, '0123456789'] string with chars allowed to be typed
16 - sep:        [opt, '\/:-'] string of char(s) used as separators in mask
17 - typeon:     [opt, '_YMDhms'] string of chars in mask that can be typed on
18 - onbadkey:   [opt, null] function to run when user types a unallowed key
19 - badkeywait: [opt, 0] used with onbadkey. Indicates how long (in ms) to lock
20   text input for onbadkey function to run
21 ***********************************************************************/
22 function MaskedInput(args)
23 {
24   if (args['elm'] === null || args['format'] === null) { return false; }
25   var el     = args['elm'],
26     format   = args['format'],
27     allowed  = args['allowed']    || '0123456789',
28     sep      = args['separator']  || '\/:-',
29     open     = args['typeon']     || '_YMDhms',
30     onbadkey = args['onbadkey']   || function(){},
31     badwait  = args['badkeywait'] || 0;
32   
33   var locked = false, hold = 0;
34   el.value = format;
35   // Assign events
36   el.onkeydown  = KeyHandlerDown;  //
37   el.onkeypress = KeyHandlerPress; // add event handlers to element
38   el.onkeyup    = KeyHandlerUp;    //
39
40   function GetKey(code)
41   {
42     code = code || window.event, ch = '';
43     var keyCode = code.which, evt = code.type;
44     if (keyCode == null) { keyCode = code.keyCode; }
45     if (keyCode === null) { return ''; } // no key, no play
46     // deal with special keys
47     switch (keyCode) {
48     case 8:  ch = 'bksp'; break;
49     case 46: // handle del and . both being 46
50       ch = (evt == 'keydown') ? 'del' : '.'; break;
51     case 16: ch = 'shift'; break;//shift
52     case 0:/*CRAP*/ case 9:/*TAB*/ case 13:/*ENTER*/
53       ch = 'etc'; break;
54     case 37: case 38: case 39: case 40: // arrow keys
55       ch = (!code.shiftKey &&
56            (code.charCode != 39 && code.charCode !== undefined)) ?
57         'etc' : String.fromCharCode(keyCode);
58       break;
59     // default to thinking it's a character or digit
60     default: ch = String.fromCharCode(keyCode);
61     }
62     return ch;
63   }
64   function KeyHandlerDown(e)
65   {
66     e = e || event;
67     if (locked) { return false; }
68     var key = GetKey(e);
69     if (el.value == '') { el.value = format; SetTextCursor(el,0); }
70     // Only do update for bksp del
71     if (key == 'bksp' || key == 'del') { Update(key); return false; }
72     else if (key == 'etc' || key == 'shift') { return true; }
73     else { return true; }    
74   }
75   function KeyHandlerPress(e)
76   {
77     e = e || event;
78     if (locked) { return false; }
79     var key = GetKey(e);
80     // Check if modifier key is being pressed; command
81     if (key=='etc' || e.metaKey || e.ctrlKey || e.altKey) { return true; }
82     if (key != 'bksp' && key != 'del' && key != 'etc' && key != 'shift') {
83       if (!GoodOnes(key)) { return false; }
84       return Update(key);
85     }
86     else { return false; }
87   }
88   function KeyHandlerUp(e) { hold = 0; }
89   function Update(key)
90   {
91     var p = GetTextCursor(el), c = el.value, val = '';
92     // Handle keys now
93     switch (true) {
94     case (allowed.indexOf(key) != -1):
95       if (++p > format.length) { return false; } // if text csor at end
96       // Handle cases where user places csor before separator
97       while (sep.indexOf(c.charAt(p-1)) != -1 && p <= format.length) { p++; }
98       val = c.substr(0, p-1) + key + c.substr(p);
99       // Move csor up a spot if next char is a separator char
100       if (allowed.indexOf(c.charAt(p)) == -1
101           && open.indexOf(c.charAt(p)) == -1) { p++; }
102       break;
103     case (key=='bksp'): // backspace
104       if (--p < 0) return false; // at start of field
105       // If previous char is a separator, move a little more
106       while (allowed.indexOf(c.charAt(p)) == -1
107              && open.indexOf(c.charAt(p)) == -1
108              && p > 1) { p--; }
109       val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1);
110       break;
111     case (key=='del'): // forward delete
112       if (p >= c.length) { return false; } // at end of field
113       // If next char is a separator and not the end of the text field
114       while (sep.indexOf(c.charAt(p)) != -1
115              && c.charAt(p) != '') { p++; }
116       val = c.substr(0, p) + format.substr(p,1) + c.substr(p+1);
117       p++; // Move position forward
118       break;
119     case (key=='etc'): return true; // Catch other allowed chars
120     default: return false;   // Ignore the rest
121     }
122     el.value = '';        // blank it first (Firefox issue)
123     el.value = val;       // put updated value back in
124     SetTextCursor(el, p); // Set the text cursor
125     return false;
126   }
127   function GetTextCursor(node)
128   {
129     try {
130       if (node.selectionStart >= 0) { return node.selectionStart; }
131       else if (document.selection) {// IE
132         var ntxt = node.value; // getting starting text
133         var rng = document.selection.createRange();
134         rng.text = '|%|';
135         var start = node.value.indexOf('|%|');
136         rng.moveStart('character', -3);
137         rng.text = '';
138         // put starting text back in,
139         // fixes issue if all text was highlighted
140         node.value = ntxt;
141         return start;
142       } return -1;
143     } catch(e) { return false; }
144   }
145   function SetTextCursor(node, pos)
146   {
147     try {
148       if (node.selectionStart) {
149         node.focus();
150         node.setSelectionRange(pos,pos);
151       }
152       else if (node.createTextRange) { // IE
153         var rng = node.createTextRange();
154         rng.move('character', pos);
155         rng.select();
156       }
157     } catch(e) { return false; }
158   }
159   function GoodOnes(k)
160   {
161     if (allowed.indexOf(k) == -1 && k!='bksp' && k!='del' && k!='etc') {
162       var p = GetTextCursor(el); // Need to ensure cursor position not lost
163       locked = true; onbadkey();
164       // Hold lock long enough for onbadkey function to run
165       setTimeout(function(){locked=false; SetTextCursor(el,p);}, badwait);
166       return false;
167     } return true;
168   }
169   function resetField() {
170     el.value = format;
171   }
172   function setAllowed(a) {
173     allowed = a;
174     resetField();
175   }
176   function setFormat(f) {
177     format = f;
178     resetField();
179   }
180   function setSeparator(s) {
181     sep = s;
182     resetField();
183   }
184   function setTypeon(t) {
185     open = t;
186     resetField();
187   }
188   return {
189     resetField:resetField,
190     setAllowed:setAllowed,
191     setFormat:setFormat,
192     setSeparator:setSeparator,
193     setTypeon:setTypeon
194   }
195 }