X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/dep-ui/blobdiff_plain/ac26861cd2097171f4f61adf7277af93c7094b16..aca28a1d25ea50c837b2699964ef3c53f449f0c1:/dep-ui.js
diff --git a/dep-ui.js b/dep-ui.js
index 29f5d6b..6803915 100644
--- a/dep-ui.js
+++ b/dep-ui.js
@@ -18,36 +18,57 @@
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, see .
+ * along with this program; if not, see .
*/
-var DEP_UI = {}; (function () { with (DEP_UI) {
+var DEP_UI = {}; (function () {
/*----- Utility functions and classes -------------------------------------*/
-DEP_UI.elt = function (id) {
+function debug(msg) {
+ /* Write the string MSG to the `trace' element, if there is one. */
+
+ var e = elt('trace');
+ if (e !== null) e.textContent += msg;
+}
+DEP_UI.debug = debug;
+
+function trap(what, func) {
+ try {
+ func();
+ } catch (e) {
+ debug('caught exception in ' + what + ': ' + e);
+ throw e;
+ }
+}
+DEP_UI.trap = trap;
+
+function elt(id) {
/* Find and return the element with the given ID. */
return document.getElementById(id);
}
+DEP_UI.elt = elt;
-DEP_UI.add_elt_class = function (elt, cls) {
+function add_elt_class(elt, cls) {
/* Add the class name CLS to element ELT's `class' attribute. */
if (!elt.className.match('\\b' + cls + '\\b'))
elt.className += ' ' + cls
}
+DEP_UI.add_elt_class = add_elt_class;
-DEP_UI.rm_elt_class = function (elt, cls) {
+function rm_elt_class(elt, cls) {
/* Remove the class name CLS from element ELT's `class' attribute. */
elt.className = elt.className.replace(
new RegExp ('\\s*\\b' + cls + '\\b\\s*'), ' ');
}
+DEP_UI.rm_elt_class = rm_elt_class;
/* A gadget which can arrange to perform an idempotent action (the FUNC
* argument) again `soon'.
*/
-DEP_UI.Soon = function (func) {
+function Soon(func) {
this.timer = null;
this.func = func;
}
@@ -63,15 +84,16 @@ Soon.prototype = {
this.timer = setTimeout(function () { me.func(); }, 50);
}
};
+DEP.Soon = Soon;
/*----- Conversion machinery ----------------------------------------------*/
/* An exception, thrown if a conversion function doesn't like what it
* sees.
*/
-DEP_UI.BadValue = new DEP.Tag('BadValue');
+BadValue = new DEP.Tag('BadValue'); DEP.BadValue = BadValue;
-DEP_UI.convert_to_numeric = function (string) {
+function convert_to_numeric(string) {
/* Convert the argument STRING to a number. */
if (!string.match('\\S')) throw BadValue;
@@ -79,26 +101,47 @@ DEP_UI.convert_to_numeric = function (string) {
if (n !== n) throw BadValue;
return n;
}
+DEP_UI.convert_to_numeric = convert_to_numeric;
-DEP_UI.convert_from_numeric = function (num) {
+function convert_from_numeric(num) {
/* Convert the argument number NUM to a string, in a useful way. */
return num.toFixed(3);
}
+DEP_UI.convert_from_numeric = convert_from_numeric;
/*----- User interface functions ------------------------------------------*/
/* A list of input fields which might need periodic kicking. */
var KICK_INPUT_FIELDS = [];
-DEP_UI.input_field = function (id, dep, convert) {
- /* Bind an input field (with the given ID) to a DEP, converting the user's
- * input with the CONVERT function.
+function input_widget(id, dep, kickfn) {
+ /* Bind an input widget (with the given ID) to a DEP, calling KICKFN (with
+ * the widget element as its argument) to update DEP from the state of the
+ * widget. This is common machinery for `input_field' and `input_radio'.
*/
var e = elt(id);
- function kick() {
- /* Update the dep from the element content. If the convert function
+ // Name the dep after our id.
+ dep.name = id;
+
+ // Arrange to update the dep `shortly after' updates.
+ function kick() { kickfn(e); }
+ var soon = new Soon(kick);
+ function kick_soon() { soon.kick(); }
+ e.addEventListener('input', kick_soon, false);
+
+ // Set our field to the correct state when the page finishes loading.
+ KICK_INPUT_FIELDS.push(kick);
+}
+
+function input_field(id, dep, convert) {
+ /* Bind an input field (with the given ID) to a DEP, converting the user's
+ * input with the CONVERT function.
+ */
+
+ input_widget(id, dep, function (e) {
+ /* Update the dep from the element content. If the CONVERT function
* doesn't like the input then mark the dep as bad and highlight the
* input element.
*/
@@ -115,53 +158,24 @@ DEP_UI.input_field = function (id, dep, convert) {
dep.make_bad();
add_elt_class(e, 'bad');
}
- }
-
- // Name the dep after our id.
- dep.name = id;
-
- // Arrange to update the dep `shortly after' updates.
- var soon = new Soon(kick);
- function kick_soon () { soon.kick(); }
- e.addEventListener('click', kick_soon);
- e.addEventListener('blur', kick_soon);
- e.addEventListener('keypress', kick_soon);
-
- // Sadly, the collection of events above isn't comprehensive, because we
- // don't actually get told about edits as a result of clipboard operations,
- // or even (sometimes) deletes, so add our `kick' function to a list of
- // such functions to be run periodically just in case.
- KICK_INPUT_FIELDS.push(kick);
+ });
}
+DEP_UI.input_field = input_field;
-DEP_UI.input_radio = function (id, dep) {
+function input_radio(id, dep) {
/* Bind a radio button (with the given ID) to a DEP. When the user frobs
* the button, set the dep to the element's `value' attribute.
*/
- var e = elt(id);
-
- function kick () {
- // Make sure we're actually chosen. We get called periodically
- // regardless of user input.
+ input_widget(id, dep, function (e) {
+ // Make sure we're actually chosen. We might get called regardless of
+ // user input.
if (e.checked) dep.set_value(e.value);
- };
-
- // Name the dep after our id.
- dep.name = id;
-
- // Arrange to update the dep `shortly after' updates.
- var soon = new Soon(kick);
- function kick_soon () { soon.kick(); }
- e.addEventListener('click', kick_soon);
- e.addEventListener('changed', kick_soon);
-
- // The situation for radio buttons doesn't seem as bad as for text widgets,
- // but let's be on the safe side.
- KICK_INPUT_FIELDS.push(kick);
+ });
}
+DEP_UI.input_radio = input_radio;
-DEP_UI.output_field = function (id, dep, convert) {
+function output_field(id, dep, convert) {
/* Bind a DEP to an output element (given by ID), converting the dep's
* value using the CONVERT function.
*/
@@ -186,6 +200,7 @@ DEP_UI.output_field = function (id, dep, convert) {
dep.add_listener(kicked);
kicked();
}
+DEP_UI.output_field = output_field;
/*----- Periodic maintenance ----------------------------------------------*/
@@ -196,11 +211,8 @@ function kick_all() {
DEP.dolist(KICK_INPUT_FIELDS, function (func) { func(); });
}
-// Update the input fields relatively frequently.
-setInterval(kick_all, 500);
-
// And make sure we get everything started when the page is fully loaded.
-window.addEventListener('load', kick_all);
+window.addEventListener('load', kick_all, false);
/*----- That's all, folks -------------------------------------------------*/
-} })();
+})();