chiark / gitweb /
format.py: Document the formatting directive syntax.
[chopwood] / chpwd.js
1 /* -*-js-*-
2  *
3  * Common JavaScript code for Chopwood
4  *
5  * (c) 2013 Mark Wooding
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Chopwood: a password-changing service.
11  *
12  * Chopwood is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Affero General Public License as
14  * published by the Free Software Foundation; either version 3 of the
15  * License, or (at your option) any later version.
16  *
17  * Chopwood is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Affero General Public License for more details.
21  *
22  * You should have received a copy of the GNU Affero General Public
23  * License along with Chopwood; if not, see
24  * <http://www.gnu.org/licenses/>.
25  */
26
27 /*----- Some utilities ----------------------------------------------------*/
28
29 function elt(id) {
30   /* Return the element with the requested ID. */
31   return document.getElementById(id);
32 }
33
34 function map(func, list) {
35   /* Apply FUNC to each element of LIST, which may actually be any object;
36    * return a new object mapping the same keys to the images of the values in
37    * the function FUNC.
38    */
39   var i;
40   var out = {};
41   for (i in list) out[i] = func(list[i]);
42   return out;
43 }
44
45 /*----- Form validation ---------------------------------------------------*/
46
47 var FORMS = {};
48 /* A map of form names to information about them.  Each form is an object
49  * with the following slots:
50  *
51  *   * elts: A list of element-ids for the form widgets.  These widgets will
52  *     be checked periodically to see whether the input data is acceptable.
53  *
54  *   * check: A function of no arguments, which returns either `null' if
55  *     everything is OK, or an error message as a string.
56  *
57  * Form names aren't just for show.  Some element-ids are constructed using
58  * the form name as a base:
59  *
60  *   * FORM-whinge: An output element in which to display an error message if
61  *     the form's input is unacceptable.
62  *
63  *   * FORM-submit: The Submit button, which needs hooking to inhibit
64  *     submitting a form with invalid data.
65  */
66
67 function check() {
68   /* Check through the various forms to make sure they're filled in OK.  If
69     * not, set the `F-whinge' elements, and disable `F-submit'.
70     */
71   var f, form, whinge;
72
73   for (f in FORMS) {
74     form = FORMS[f];
75     we = elt(f + '-whinge');
76     sb = elt(f + '-submit');
77     whinge = form.check();
78     if (sb !== null) sb.disabled = (whinge !== null);
79     if (we !== null) {
80       we.textContent = whinge || 'OK';
81       we.className = whinge === null ? 'whinge' : 'whinge wrong';
82     }
83   }
84
85   // We can't catch all possible change events: in particular, it seems
86   // really hard to capture changes as a result of selections from a menu --
87   // e.g., delete or paste.  Accept this, and just recheck periodically.
88   check_again(1000);
89 }
90
91 var timer = null;
92 /* The timer for the periodic validation job. */
93
94 function check_again(when) {
95   /* Arrange to check the forms again in WHEN milliseconds. */
96   if (timer !== null) clearTimeout(timer);
97   timer = setTimeout(check, when);
98 }
99
100 var Q = 0;
101 function check_soon(ev) {
102   /* Arrange to check the forms again very soon. */
103   check_again(50);
104 }
105
106 function check_presubmit(ev, f) {
107   /* Check the form F now, popping up an alert and preventing the event EV if
108    * there's something wrong.
109    */
110   var whinge = FORMS[f].check();
111
112   if (whinge !== null) {
113     ev.preventDefault();
114     alert(whinge);
115   }
116 }
117
118 function init() {
119   /* Attach event handlers to the various widgets so that we can keep track
120    * of how well things are being filled in.
121    */
122   var f, form, w, e;
123
124   // Start watching for changes.
125   check_soon();
126
127   for (f in FORMS) (function (f, form) {
128
129     // Ugh.  We have to lambda-bind `f' here so that we can close over it
130     // properly.
131     for (w in form.elts) {
132       if ((e = elt(f + '-' + form.elts[w])) === null) continue;
133       e.addEventListener('click', check_soon);
134       e.addEventListener('change', check_soon);
135       e.addEventListener('keypress', check_soon);
136       e.addEventListener('blur', check_soon);
137     }
138     if ((e = elt(f + '-submit')) !== null) {
139       e.addEventListener('click', function (ev) {
140         return check_presubmit(ev, f)
141       });
142     }
143   })(f, FORMS[f]);
144 }
145
146 window.addEventListener('load', init);
147
148 /*----- That's all, folks -------------------------------------------------*/