adamc@580: function cons(v, ls) { adamc@580: return { n : ls, v : v }; adamc@580: } adamc@670: adamc@579: function callAll(ls) { adamc@585: for (; ls; ls = ls.n) adamc@579: ls.v(); adamc@579: } adamc@574: adamc@580: function sc(v) { adamc@580: return {v : v, h : null}; adamc@580: } adamc@580: function sv(s, v) { adamc@580: s.v = v; adamc@580: callAll(s.h); adamc@580: } adamc@601: function sg(s) { adamc@601: return s.v; adamc@601: } adamc@579: adamc@580: function ss(s) { adamc@580: return s; adamc@580: } adamc@580: function sr(v) { adamc@580: return {v : v, h : null}; adamc@580: } adamc@580: function sb(x,y) { adamc@580: var z = y(x.v); adamc@580: var s = {v : z.v, h : null}; adamc@580: adamc@580: function reZ() { adamc@580: z.h = cons(function() { s.v = z.v; callAll(s.h); }, z.h); adamc@580: } adamc@580: adamc@580: x.h = cons(function() { z = y(x.v); reZ(); s.v = z.v; callAll(s.h); }, x.h); adamc@580: reZ(); adamc@580: adamc@580: return s; adamc@580: } adamc@571: adamc@604: function lastParent() { adamc@604: var pos = document; adamc@604: adamc@600: while (pos.lastChild && pos.lastChild.nodeType == 1) adamc@600: pos = pos.lastChild; adamc@600: adamc@600: return pos.parentNode; adamc@600: } adamc@600: adamc@604: var thisScript = null; adamc@603: adamc@604: function addNode(node) { adamc@604: if (thisScript) { adamc@604: thisScript.parentNode.appendChild(node); adamc@604: thisScript.parentNode.removeChild(thisScript); adamc@604: } else adamc@604: lastParent().appendChild(node); adamc@603: } adamc@603: adamc@604: function runScripts(node) { adamc@604: var savedScript = thisScript; adamc@603: adamc@646: var scripts = node.getElementsByTagName("script"), scriptsCopy = {}; adamc@604: var len = scripts.length; adamc@646: for (var i = 0; i < len; ++i) adamc@646: scriptsCopy[i] = scripts[i]; adamc@604: for (var i = 0; i < len; ++i) { adamc@646: thisScript = scriptsCopy[i]; adamc@604: eval(thisScript.textContent); adamc@604: } adamc@604: adamc@604: thisScript = savedScript; adamc@603: } adamc@603: adamc@603: function populate(node, html) { adamc@603: node.innerHTML = html; adamc@604: runScripts(node); adamc@603: } adamc@603: adamc@571: function dyn(s) { adamc@571: var x = document.createElement("span"); adamc@604: populate(x, s.v); adamc@604: addNode(x); adamc@603: s.h = cons(function() { populate(x, s.v) }, s.h); adamc@571: } adamc@582: adamc@598: function inp(t, s) { adamc@598: var x = document.createElement(t); adamc@598: x.value = s.v; adamc@604: addNode(x); adamc@686: s.h = cons(function() { if (x.value != s.v) x.value = s.v }, s.h); adamc@598: x.onkeyup = function() { sv(s, x.value) }; adamc@606: return x; adamc@598: } adamc@598: adamc@597: function eh(x) { adamc@597: return x.split("&").join("&").split("<").join("<").split(">").join(">"); adamc@597: } adamc@597: adamc@582: function ts(x) { return x.toString() } adamc@586: function bs(b) { return (b ? "True" : "False") } adamc@586: adamc@649: function pi(s) { adamc@649: var r = parseInt(s); adamc@649: if (r.toString() == s) adamc@649: return r; adamc@649: else adamc@649: throw "Can't parse int: " + s; adamc@649: } adamc@649: adamc@649: function pfl(s) { adamc@649: var r = parseFloat(s); adamc@649: if (r.toString() == s) adamc@649: return r; adamc@649: else adamc@649: throw "Can't parse float: " + s; adamc@649: } adamc@649: adamc@669: function whine(msg) { adamc@669: alert(msg); adamc@669: throw msg; adamc@669: } adamc@669: adamc@649: function pf() { adamc@669: whine("Pattern match failure"); adamc@649: } adamc@589: adamc@603: var closures = []; adamc@603: adamc@603: function ca(f) { adamc@603: var n = closures.length; adamc@603: closures[n] = f; adamc@603: return n; adamc@603: } adamc@603: adamc@603: function cr(n) { adamc@603: return closures[n](); adamc@603: } adamc@603: adamc@609: adamc@668: var client_id = 0; adamc@668: var client_pass = 0; adamc@668: var url_prefix = "/"; adamc@673: var timeout = 60; adamc@668: adamc@668: function getXHR(uri) adamc@609: { adamc@609: try { adamc@609: return new XMLHttpRequest(); adamc@609: } catch (e) { adamc@609: try { adamc@609: return new ActiveXObject("Msxml2.XMLHTTP"); adamc@609: } catch (e) { adamc@609: try { adamc@609: return new ActiveXObject("Microsoft.XMLHTTP"); adamc@609: } catch (e) { adamc@609: throw "Your browser doesn't seem to support AJAX."; adamc@609: } adamc@609: } adamc@609: } adamc@609: } adamc@609: adamc@668: function requestUri(xhr, uri) { adamc@668: xhr.open("GET", uri, true); adamc@668: adamc@668: if (client_id != 0) { adamc@668: xhr.setRequestHeader("UrWeb-Client", client_id.toString()); adamc@668: xhr.setRequestHeader("UrWeb-Pass", client_pass.toString()); adamc@668: } adamc@668: adamc@668: xhr.send(null); adamc@668: } adamc@668: adamc@613: function rc(uri, parse, k) { adamc@609: var xhr = getXHR(); adamc@609: adamc@609: xhr.onreadystatechange = function() { adamc@612: if (xhr.readyState == 4) { adamc@612: var isok = false; adamc@612: adamc@612: try { adamc@612: if (xhr.status == 200) adamc@612: isok = true; adamc@612: } catch (e) { } adamc@612: adamc@612: if (isok) adamc@613: k(parse(xhr.responseText)); adamc@649: else { adamc@669: whine("Error querying remote server!"); adamc@649: } adamc@612: } adamc@609: }; adamc@609: adamc@668: requestUri(xhr, uri); adamc@609: } adamc@667: adamc@667: function path_join(s1, s2) { adamc@667: if (s1.length > 0 && s1[s1.length-1] == '/') adamc@667: return s1 + s2; adamc@667: else adamc@667: return s1 + "/" + s2; adamc@667: } adamc@667: adamc@670: var channels = []; adamc@670: adamc@670: function newQueue() { adamc@670: return { front : null, back : null }; adamc@670: } adamc@670: function enqueue(q, v) { adamc@670: if (q.front == null) { adamc@670: q.front = cons(v, null); adamc@670: q.back = q.front; adamc@670: } else { adamc@670: var node = cons(v, null); adamc@670: q.back.n = node; adamc@670: q.back = node; adamc@670: } adamc@670: } adamc@670: function dequeue(q) { adamc@670: if (q.front == null) adamc@670: return null; adamc@670: else { adamc@670: var r = q.front.v; adamc@670: q.front = q.front.n; adamc@670: if (q.front == null) adamc@670: q.back = null; adamc@670: return r; adamc@670: } adamc@670: } adamc@670: adamc@670: function newChannel() { adamc@670: return { msgs : newQueue(), listeners : newQueue() }; adamc@670: } adamc@670: adamc@667: function listener() { adamc@668: var uri = path_join(url_prefix, ".msgs"); adamc@667: var xhr = getXHR(); adamc@673: var tid, orsc, onTimeout; adamc@673: adamc@673: var connect = function () { adamc@673: xhr.onreadystatechange = orsc; adamc@673: tid = window.setTimeout(onTimeout, timeout * 500); adamc@673: requestUri(xhr, uri); adamc@673: } adamc@673: adamc@673: orsc = function() { adamc@667: if (xhr.readyState == 4) { adamc@673: window.clearTimeout(tid); adamc@673: adamc@667: var isok = false; adamc@667: adamc@667: try { adamc@667: if (xhr.status == 200) adamc@667: isok = true; adamc@667: } catch (e) { } adamc@667: adamc@668: if (isok) { adamc@669: var lines = xhr.responseText.split("\n"); adamc@669: if (lines.length < 2) adamc@679: return; //throw "Empty message from remote server"; adamc@669: adamc@669: for (var i = 0; i+1 < lines.length; i += 2) { adamc@670: var chn = lines[i]; adamc@670: var msg = lines[i+1]; adamc@670: adamc@670: if (chn < 0) adamc@670: whine("Out-of-bounds channel in message from remote server"); adamc@670: adamc@670: var ch; adamc@670: adamc@670: if (chn >= channels.length || channels[chn] == null) { adamc@670: ch = newChannel(); adamc@670: channels[chn] = ch; adamc@670: } else adamc@670: ch = channels[chn]; adamc@670: adamc@670: var listener = dequeue(ch.listeners); adamc@670: if (listener == null) { adamc@670: enqueue(ch.msgs, msg); adamc@670: } else { adamc@670: listener(msg); adamc@670: } adamc@669: } adamc@669: adamc@673: connect(); adamc@668: } adamc@667: else { adamc@679: /*try { adamc@672: whine("Error querying remote server for messages! " + xhr.status); adamc@679: } catch (e) { }*/ adamc@667: } adamc@667: } adamc@667: }; adamc@667: adamc@673: onTimeout = function() { adamc@673: xhr.abort(); adamc@673: connect(); adamc@673: }; adamc@673: adamc@673: connect(); adamc@667: } adamc@670: adamc@670: function rv(chn, parse, k) { adamc@682: if (chn == null) adamc@682: return; adamc@682: adamc@670: if (chn < 0) adamc@670: whine("Out-of-bounds channel receive"); adamc@670: adamc@670: var ch; adamc@670: adamc@670: if (chn >= channels.length || channels[chn] == null) { adamc@670: ch = newChannel(); adamc@670: channels[chn] = ch; adamc@670: } else adamc@670: ch = channels[chn]; adamc@670: adamc@670: var msg = dequeue(ch.msgs); adamc@670: if (msg == null) { adamc@670: enqueue(ch.listeners, function(msg) { k(parse(msg))(null); }); adamc@670: } else { adamc@670: k(parse(msg))(null); adamc@670: } adamc@670: } adamc@670: adamc@679: function uf(s) { adamc@679: return escape(s).replace(new RegExp ("/", "g"), "%2F"); adamc@670: } adamc@679: adamc@679: function uu(s) { adamc@679: return unescape(s).replace(new RegExp ("\\+", "g"), " "); adamc@679: }