| 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, false); |
| 134 | e.addEventListener('change', check_soon, false); |
| 135 | e.addEventListener('keypress', check_soon, false); |
| 136 | e.addEventListener('blur', check_soon, false); |
| 137 | } |
| 138 | if ((e = elt(f + '-submit')) !== null) { |
| 139 | e.addEventListener('click', function (ev) { |
| 140 | return check_presubmit(ev, f) |
| 141 | }, false); |
| 142 | } |
| 143 | })(f, FORMS[f]); |
| 144 | } |
| 145 | |
| 146 | window.addEventListener('load', init, false); |
| 147 | |
| 148 | /*----- That's all, folks -------------------------------------------------*/ |