X-Git-Url: https://git.distorted.org.uk/~mdw/dep-ui/blobdiff_plain/c6150f2f930c56ba8d3264f0cc5b9321b9b29d54..6132735f4d4fe2bd9d4395f5cae34b662c999451:/dep-ui.js diff --git a/dep-ui.js b/dep-ui.js index 22f2e60..6803915 100644 --- a/dep-ui.js +++ b/dep-ui.js @@ -18,21 +18,22 @@ * 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.debug = function(msg) { +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; - DEP_UI.trap = function(what, func) { +function trap(what, func) { try { func(); } catch (e) { @@ -40,30 +41,34 @@ DEP_UI.debug = function(msg) { throw e; } } +DEP_UI.trap = trap; -DEP_UI.elt = function (id) { +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; } @@ -79,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; @@ -95,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. */ @@ -131,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. */ @@ -202,6 +200,7 @@ DEP_UI.output_field = function (id, dep, convert) { dep.add_listener(kicked); kicked(); } +DEP_UI.output_field = output_field; /*----- Periodic maintenance ----------------------------------------------*/ @@ -212,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 -------------------------------------------------*/ -} })(); +})();