Various changes and fixes (#7424)
[openemr.git] / library / textformat.js
blob1705916c68800f6eadff141265f6c2d30d0bbd20
1 // Copyright (C) 2005 Rod Roark <rod@sunsetsystems.com>
2 //
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
8 // Onkeyup handler for dates.  Converts dates that are keyed in to a
9 // consistent format, and helps to reduce typing errors.
11 function datekeyup(e, defcc, withtime) {
12  if (typeof(withtime) == 'undefined') withtime = false;
14  // TODO: rewrite this to avoid the infinite loop
15  /* eslint-disable-next-line no-constant-condition */
16  while(true) {
17   var delim = '';
18   var arr = new Array(0, 0, 0, 0, 0, 0);
19   var ix = 0;
20   var v = e.value;
22   // Build an array to facilitate error checking.
23   for (var i = 0; i < v.length; ++i) {
24    var c = v.charAt(i);
25    if (c >= '0' && c <= '9') {
26     ++arr[ix];
27    } else if (ix < 2 && (c == '-' || c == '/')) {
28     arr[++ix] = 0;
29    } else if (withtime && ix == 2 && c == ' ') {
30     arr[++ix] = 0;
31    } else if (withtime && (ix == 3 || ix == 4) && c == ':') {
32     arr[++ix] = 0;
33    } else {
34     e.value = v.substring(0, i);
35     return;
36    }
37   }
39   // We have finished scanning the string.  If there is a problem,
40   // drop the last character and repeat the loop.
41   if ((ix > 5) ||
42       (!withtime && ix > 2)   ||
43       (ix > 4 && arr[4] == 0) ||
44       (ix > 3 && arr[3] == 0) ||
45       (ix > 2 && arr[2] == 0) ||
46       (ix > 1 && arr[1] == 0) ||
47       (ix > 0 && arr[0] == 0) ||
48       (arr[0] > 8) ||
49       (ix > 0 && arr[0] > 2 && (arr[0] != 4 || arr[1] > 2 || arr[2] > 2)) ||
50       (arr[2] > 2 && (arr[2] > 4 || arr[0] > 2 || arr[1] > 2)))
51   {
52    e.value = v.substring(0, v.length - 1);
53   } else {
54    break;
55   }
56  }
58  // The remainder does reformatting if there is enough data for that.
59  if (arr[2] == 4 && defcc == '1') { // mm/dd/yyyy
60   e.value  = v.substring(arr[0] + arr[1] + 2, arr[0] + arr[1] + 6) + '-'; // year
61   if (arr[0] == 1) e.value += '0';
62   e.value += v.substring(0, arr[0]) + '-'; // month
63   if (arr[1] == 1) e.value += '0';
64   e.value += v.substring(arr[0] + 1, arr[0] + 1 + arr[1]); // day
65  }
66  else if (arr[2] == 4) { // dd-mm-yyyy
67   e.value  = v.substring(arr[0] + arr[1] + 2, arr[0] + arr[1] + 6) + '-'; // year
68   if (arr[1] == 1) e.value += '0';
69   e.value += v.substring(arr[0] + 1, arr[0] + 1 + arr[1]) + '-'; // month
70   if (arr[0] == 1) e.value += '0';
71   e.value += v.substring(0, arr[0]); // day
72  }
73  else if (arr[0] == 4 && arr[2] > 0) { // yyyy-mm-dd
74   e.value  = v.substring(0, arr[0]) + '-'; // year
75   if (arr[1] == 1) e.value += '0';
76   e.value += v.substring(arr[0] + 1, arr[0] + 1 + arr[1]) + '-'; // month
77   e.value += v.substring(arr[0] + arr[1] + 2, arr[0] + arr[1] + 2 + arr[2]); // day (may be 1 digit)
78  }
79  else if (arr[0] == 8 && defcc == '1') { // yyyymmdd
80   e.value  = v.substring(0, 4) + '-'; // year
81   e.value += v.substring(4, 6) + '-'; // month
82   e.value += v.substring(6, 8); // day
83  }
84  else if (arr[0] == 8) { // ddmmyyyy
85   e.value  = v.substring(4, 8) + '-'; // year
86   e.value += v.substring(2, 4) + '-'; // month
87   e.value += v.substring(0, 2); // day
88  }
89  else {
90   return;
91  }
92  if (withtime) {
93   e.value += v.substring(arr[0] + arr[1] + arr[2] + 2);
94  }
97 // Onblur handler to avoid incomplete entry of dates.
99 function dateblur(e, defcc, withtime) {
100  if (typeof(withtime) == 'undefined') withtime = false;
102  var v = e.value;
103  if (v.length == 0) return;
105  var arr = new Array(0, 0, 0, 0, 0);
106  var ix = 0;
107  for (var i = 0; i < v.length; ++i) {
108   var c = v.charAt(i);
109   if (c >= '0' && c <= '9') {
110    ++arr[ix];
111   } else if (c == '-' || c == '/' || c == ' ' || c == ':') {
112    arr[++ix] = 0;
113   } else {
114    alert('Invalid character in date!');
115    return;
116   }
119  // A birth date may be just age in years, in which case we convert it.
120  if (ix == 0 && arr[0] > 0 && arr[0] <= 3 && e.name.indexOf('DOB') >= 0) {
121   var d = new Date();
122   d = new Date(d.getTime() - parseInt(v) * 365.25 * 24 * 60 * 60 * 1000);
123   var s = '' + d.getFullYear() + '-';
124   if (d.getMonth() < 9) s += '0';
125   s += (d.getMonth() + 1) + '-';
126   if (d.getDate() < 10) s += '0';
127   s += d.getDate();
128   e.value = s;
129   return;
132  if ((!withtime && ix != 2) || (withtime && ix < 2) || arr[0] != 4 || arr[1] != 2 || arr[2] < 1) {
133   if (confirm('Date entry is incomplete! Try again?'))
134    e.focus();
135   else
136    e.value = '';
137   return;
140  if (arr[2] == 1) {
141   e.value = v.substring(0, 8) + '0' + v.substring(8);
145 // Private subroutine for US phone number formatting.
146 function usphone(v) {
147  if (v.length > 0 && v.charAt(0) == '-') v = v.substring(1);
148  var oldlen = v.length;
149  for (var i = 0; i < v.length; ++i) {
150   var c = v.charAt(i);
151   if (c < '0' || c > '9') {
152    v = v.substring(0, i) + v.substring(i + 1);
153    --i;
154   }
156  if (oldlen > 3 && v.length >= 3) {
157   v = v.substring(0, 3) + '-' + v.substring(3);
158   if (oldlen > 7 && v.length >= 7) {
159    v = v.substring(0, 7) + '-' + v.substring(7);
160    if (v.length > 12) v = v.substring(0, 12);
161   }
163  return v;
166 // Private subroutine for non-US phone number formatting.
167 function nonusphone(v) {
168  for (var i = 0; i < v.length; ++i) {
169   var c = v.charAt(i);
170   if (c < '0' || c > '9') {
171    v = v.substring(0, i) + v.substring(i + 1);
172    --i;
173   }
175  return v;
178 // Telephone country codes that are exactly 2 digits.
179 var twodigitccs = '/20/30/31/32/33/34/36/39/40/41/43/44/45/46/47/48/49/51/52/53/54/55/56/57/58/60/61/62/63/64/65/66/81/82/84/86/90/91/92/93/94/95/98/';
181 // Onkeyup handler for phone numbers.  Helps to ensure a consistent
182 // format and to reduce typing errors.  defcc is the default telephone
183 // country code as a string.
185 function phonekeyup(e, defcc) {
186  var v = e.value;
187  var oldlen = v.length;
189  // Deal with international formatting.
190  if (v.length > 0 && v.charAt(0) == '+') {
191   var cc = '';
192   for (var i = 1; i < v.length; ++i) {
193    var c = v.charAt(i);
194    if (c < '0' || c > '9') {
195     v = v.substring(0, i) + v.substring(i + i);
196     --i;
197     continue;
198    }
199    cc += c;
200    if (i == 1 && oldlen > 2) {
201     if (cc == '1') { // USA
202      e.value = '+1-' + usphone(v.substring(2));
203      return;
204     }
205     if (cc == '7') { // USSR
206      e.value = '+7-' + nonusphone(v.substring(2));
207      return;
208     }
209    }
210    else if (i == 2 && oldlen > 3) {
211     if (twodigitccs.indexOf(cc) >= 0) {
212      e.value = v.substring(0, 3) + '-' + nonusphone(v.substring(3));
213      return;
214     }
215    }
216    else if (i == 3 && oldlen > 4) {
217     e.value = v.substring(0, 4) + '-' + nonusphone(v.substring(4));
218     return;
219    }
220   }
221   e.value = v;
222   return;
225  if (defcc == '1') {
226   e.value = usphone(v);
227  } else {
228   e.value = nonusphone(v);
231  return;
234 // onKeyUp handler for mask-formatted fields.
235 // This feature is experimental.
236 function maskkeyup(elem, mask) {
237  if (!mask || mask.length == 0) return;
238  var i = 0; // elem and mask index
239  var v = elem.value;
240  for (; i < mask.length && i < v.length; ++i) {
241   var ec = v.charAt(i);
242   var mc = mask.charAt(i);
243   if (mc == '#' && (ec < '0' || ec > '9')) {
244    // digit required but this is not one
245    break;
246   }
247   if (mc == '@' && ec.toLowerCase() == ec.toUpperCase()) {
248    // alpha character required but this is not one
249    break;
250   }
252  v = v.substring(0, i);
253  while (i < mask.length) {
254   var mc = mask.charAt(i++);
255   if (mc == '*' || mc == '#' || mc == '@') break;
256   v += mc;
258  elem.value = v;
261 // onBlur handler for mask-formatted fields.
262 // This feature is experimental.
263 function maskblur(elem, mask) {
264  var v = elem.value;
265  var i = mask.length;
266  if (i > 0 && v.length > 0 && v.length != i) {
267   // there is a mask and a value but the value is not long enough
268   for (; i > 0 && mask.charAt(i-1) == '#'; --i);
269   // i is now index to first # in # string at end of mask
270   if (i > v.length) {
271    // value is too short even if trailing digits in the mask are ignored
272    if (confirm('Field entry is incomplete! Try again?'))
273     elem.focus();
274    else
275     elem.value = '';
276    return;
277   }
278   // if the mask ends with digits then right-justify them in the value
279   while (v.length < mask.length) {
280    v = v.substring(0, i) + '0' + v.substring(i, v.length);
281   }
282   elem.value = v;