annotate lib/js/urweb.js @ 672:df6eb58de040

Fix some AJAX annoyances
author Adam Chlipala <adamc@hcoop.net>
date Tue, 24 Mar 2009 15:05:28 -0400
parents 729e65db2e2f
children a8effb6159c2
rev   line source
adamc@580 1 function cons(v, ls) {
adamc@580 2 return { n : ls, v : v };
adamc@580 3 }
adamc@670 4
adamc@579 5 function callAll(ls) {
adamc@585 6 for (; ls; ls = ls.n)
adamc@579 7 ls.v();
adamc@579 8 }
adamc@574 9
adamc@580 10 function sc(v) {
adamc@580 11 return {v : v, h : null};
adamc@580 12 }
adamc@580 13 function sv(s, v) {
adamc@580 14 s.v = v;
adamc@580 15 callAll(s.h);
adamc@580 16 }
adamc@601 17 function sg(s) {
adamc@601 18 return s.v;
adamc@601 19 }
adamc@579 20
adamc@580 21 function ss(s) {
adamc@580 22 return s;
adamc@580 23 }
adamc@580 24 function sr(v) {
adamc@580 25 return {v : v, h : null};
adamc@580 26 }
adamc@580 27 function sb(x,y) {
adamc@580 28 var z = y(x.v);
adamc@580 29 var s = {v : z.v, h : null};
adamc@580 30
adamc@580 31 function reZ() {
adamc@580 32 z.h = cons(function() { s.v = z.v; callAll(s.h); }, z.h);
adamc@580 33 }
adamc@580 34
adamc@580 35 x.h = cons(function() { z = y(x.v); reZ(); s.v = z.v; callAll(s.h); }, x.h);
adamc@580 36 reZ();
adamc@580 37
adamc@580 38 return s;
adamc@580 39 }
adamc@571 40
adamc@604 41 function lastParent() {
adamc@604 42 var pos = document;
adamc@604 43
adamc@600 44 while (pos.lastChild && pos.lastChild.nodeType == 1)
adamc@600 45 pos = pos.lastChild;
adamc@600 46
adamc@600 47 return pos.parentNode;
adamc@600 48 }
adamc@600 49
adamc@604 50 var thisScript = null;
adamc@603 51
adamc@604 52 function addNode(node) {
adamc@604 53 if (thisScript) {
adamc@604 54 thisScript.parentNode.appendChild(node);
adamc@604 55 thisScript.parentNode.removeChild(thisScript);
adamc@604 56 } else
adamc@604 57 lastParent().appendChild(node);
adamc@603 58 }
adamc@603 59
adamc@604 60 function runScripts(node) {
adamc@604 61 var savedScript = thisScript;
adamc@603 62
adamc@646 63 var scripts = node.getElementsByTagName("script"), scriptsCopy = {};
adamc@604 64 var len = scripts.length;
adamc@646 65 for (var i = 0; i < len; ++i)
adamc@646 66 scriptsCopy[i] = scripts[i];
adamc@604 67 for (var i = 0; i < len; ++i) {
adamc@646 68 thisScript = scriptsCopy[i];
adamc@604 69 eval(thisScript.textContent);
adamc@604 70 }
adamc@604 71
adamc@604 72 thisScript = savedScript;
adamc@603 73 }
adamc@603 74
adamc@603 75 function populate(node, html) {
adamc@603 76 node.innerHTML = html;
adamc@604 77 runScripts(node);
adamc@603 78 }
adamc@603 79
adamc@571 80 function dyn(s) {
adamc@571 81 var x = document.createElement("span");
adamc@604 82 populate(x, s.v);
adamc@604 83 addNode(x);
adamc@603 84 s.h = cons(function() { populate(x, s.v) }, s.h);
adamc@571 85 }
adamc@582 86
adamc@598 87 function inp(t, s) {
adamc@598 88 var x = document.createElement(t);
adamc@598 89 x.value = s.v;
adamc@604 90 addNode(x);
adamc@598 91 s.h = cons(function() { x.value = s.v }, s.h);
adamc@598 92 x.onkeyup = function() { sv(s, x.value) };
adamc@606 93 return x;
adamc@598 94 }
adamc@598 95
adamc@597 96 function eh(x) {
adamc@597 97 return x.split("&").join("&amp;").split("<").join("&lt;").split(">").join("&gt;");
adamc@597 98 }
adamc@597 99
adamc@582 100 function ts(x) { return x.toString() }
adamc@586 101 function bs(b) { return (b ? "True" : "False") }
adamc@586 102
adamc@649 103 function pi(s) {
adamc@649 104 var r = parseInt(s);
adamc@649 105 if (r.toString() == s)
adamc@649 106 return r;
adamc@649 107 else
adamc@649 108 throw "Can't parse int: " + s;
adamc@649 109 }
adamc@649 110
adamc@649 111 function pfl(s) {
adamc@649 112 var r = parseFloat(s);
adamc@649 113 if (r.toString() == s)
adamc@649 114 return r;
adamc@649 115 else
adamc@649 116 throw "Can't parse float: " + s;
adamc@649 117 }
adamc@649 118
adamc@669 119 function whine(msg) {
adamc@669 120 alert(msg);
adamc@669 121 throw msg;
adamc@669 122 }
adamc@669 123
adamc@649 124 function pf() {
adamc@669 125 whine("Pattern match failure");
adamc@649 126 }
adamc@589 127
adamc@603 128 var closures = [];
adamc@603 129
adamc@603 130 function ca(f) {
adamc@603 131 var n = closures.length;
adamc@603 132 closures[n] = f;
adamc@603 133 return n;
adamc@603 134 }
adamc@603 135
adamc@603 136 function cr(n) {
adamc@603 137 return closures[n]();
adamc@603 138 }
adamc@603 139
adamc@609 140
adamc@668 141 var client_id = 0;
adamc@668 142 var client_pass = 0;
adamc@668 143 var url_prefix = "/";
adamc@668 144
adamc@668 145 function getXHR(uri)
adamc@609 146 {
adamc@609 147 try {
adamc@609 148 return new XMLHttpRequest();
adamc@609 149 } catch (e) {
adamc@609 150 try {
adamc@609 151 return new ActiveXObject("Msxml2.XMLHTTP");
adamc@609 152 } catch (e) {
adamc@609 153 try {
adamc@609 154 return new ActiveXObject("Microsoft.XMLHTTP");
adamc@609 155 } catch (e) {
adamc@609 156 throw "Your browser doesn't seem to support AJAX.";
adamc@609 157 }
adamc@609 158 }
adamc@609 159 }
adamc@609 160 }
adamc@609 161
adamc@668 162 function requestUri(xhr, uri) {
adamc@668 163 xhr.open("GET", uri, true);
adamc@668 164
adamc@668 165 if (client_id != 0) {
adamc@668 166 xhr.setRequestHeader("UrWeb-Client", client_id.toString());
adamc@668 167 xhr.setRequestHeader("UrWeb-Pass", client_pass.toString());
adamc@668 168 }
adamc@668 169
adamc@668 170 xhr.send(null);
adamc@668 171 }
adamc@668 172
adamc@613 173 function rc(uri, parse, k) {
adamc@609 174 var xhr = getXHR();
adamc@609 175
adamc@609 176 xhr.onreadystatechange = function() {
adamc@612 177 if (xhr.readyState == 4) {
adamc@612 178 var isok = false;
adamc@612 179
adamc@612 180 try {
adamc@612 181 if (xhr.status == 200)
adamc@612 182 isok = true;
adamc@612 183 } catch (e) { }
adamc@612 184
adamc@612 185 if (isok)
adamc@613 186 k(parse(xhr.responseText));
adamc@649 187 else {
adamc@669 188 whine("Error querying remote server!");
adamc@649 189 }
adamc@612 190 }
adamc@609 191 };
adamc@609 192
adamc@668 193 requestUri(xhr, uri);
adamc@609 194 }
adamc@667 195
adamc@667 196 function path_join(s1, s2) {
adamc@667 197 if (s1.length > 0 && s1[s1.length-1] == '/')
adamc@667 198 return s1 + s2;
adamc@667 199 else
adamc@667 200 return s1 + "/" + s2;
adamc@667 201 }
adamc@667 202
adamc@670 203 var channels = [];
adamc@670 204
adamc@670 205 function newQueue() {
adamc@670 206 return { front : null, back : null };
adamc@670 207 }
adamc@670 208 function enqueue(q, v) {
adamc@670 209 if (q.front == null) {
adamc@670 210 q.front = cons(v, null);
adamc@670 211 q.back = q.front;
adamc@670 212 } else {
adamc@670 213 var node = cons(v, null);
adamc@670 214 q.back.n = node;
adamc@670 215 q.back = node;
adamc@670 216 }
adamc@670 217 }
adamc@670 218 function dequeue(q) {
adamc@670 219 if (q.front == null)
adamc@670 220 return null;
adamc@670 221 else {
adamc@670 222 var r = q.front.v;
adamc@670 223 q.front = q.front.n;
adamc@670 224 if (q.front == null)
adamc@670 225 q.back = null;
adamc@670 226 return r;
adamc@670 227 }
adamc@670 228 }
adamc@670 229
adamc@670 230 function newChannel() {
adamc@670 231 return { msgs : newQueue(), listeners : newQueue() };
adamc@670 232 }
adamc@670 233
adamc@667 234 function listener() {
adamc@668 235 var uri = path_join(url_prefix, ".msgs");
adamc@667 236 var xhr = getXHR();
adamc@669 237 var orsc = function() {
adamc@667 238 if (xhr.readyState == 4) {
adamc@667 239 var isok = false;
adamc@667 240
adamc@667 241 try {
adamc@667 242 if (xhr.status == 200)
adamc@667 243 isok = true;
adamc@667 244 } catch (e) { }
adamc@667 245
adamc@668 246 if (isok) {
adamc@669 247 var lines = xhr.responseText.split("\n");
adamc@669 248 if (lines.length < 2)
adamc@671 249 throw "Empty message from remote server";
adamc@669 250
adamc@669 251 for (var i = 0; i+1 < lines.length; i += 2) {
adamc@670 252 var chn = lines[i];
adamc@670 253 var msg = lines[i+1];
adamc@670 254
adamc@670 255 if (chn < 0)
adamc@670 256 whine("Out-of-bounds channel in message from remote server");
adamc@670 257
adamc@670 258 var ch;
adamc@670 259
adamc@670 260 if (chn >= channels.length || channels[chn] == null) {
adamc@670 261 ch = newChannel();
adamc@670 262 channels[chn] = ch;
adamc@670 263 } else
adamc@670 264 ch = channels[chn];
adamc@670 265
adamc@670 266 var listener = dequeue(ch.listeners);
adamc@670 267 if (listener == null) {
adamc@670 268 enqueue(ch.msgs, msg);
adamc@670 269 } else {
adamc@670 270 listener(msg);
adamc@670 271 }
adamc@669 272 }
adamc@669 273
adamc@669 274 xhr.onreadystatechange = orsc;
adamc@668 275 requestUri(xhr, uri);
adamc@668 276 }
adamc@667 277 else {
adamc@672 278 try {
adamc@672 279 whine("Error querying remote server for messages! " + xhr.status);
adamc@672 280 } catch (e) { }
adamc@667 281 }
adamc@667 282 }
adamc@667 283 };
adamc@667 284
adamc@669 285 xhr.onreadystatechange = orsc;
adamc@668 286 requestUri(xhr, uri);
adamc@667 287 }
adamc@670 288
adamc@670 289 function rv(chn, parse, k) {
adamc@670 290 if (chn < 0)
adamc@670 291 whine("Out-of-bounds channel receive");
adamc@670 292
adamc@670 293 var ch;
adamc@670 294
adamc@670 295 if (chn >= channels.length || channels[chn] == null) {
adamc@670 296 ch = newChannel();
adamc@670 297 channels[chn] = ch;
adamc@670 298 } else
adamc@670 299 ch = channels[chn];
adamc@670 300
adamc@670 301 var msg = dequeue(ch.msgs);
adamc@670 302 if (msg == null) {
adamc@670 303 enqueue(ch.listeners, function(msg) { k(parse(msg))(null); });
adamc@670 304 } else {
adamc@670 305 k(parse(msg))(null);
adamc@670 306 }
adamc@670 307 }
adamc@670 308
adamc@670 309 function unesc(s) {
adamc@670 310 return unescape(s).replace("+", " ");
adamc@670 311 }