annotate lib/js/urweb.js @ 679:44f23712020d

Chat example working nicely, but without dead channel removal
author Adam Chlipala <adamc@hcoop.net>
date Thu, 26 Mar 2009 18:26:50 -0400
parents a8effb6159c2
children 6c9b8875f347
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@673 144 var timeout = 60;
adamc@668 145
adamc@668 146 function getXHR(uri)
adamc@609 147 {
adamc@609 148 try {
adamc@609 149 return new XMLHttpRequest();
adamc@609 150 } catch (e) {
adamc@609 151 try {
adamc@609 152 return new ActiveXObject("Msxml2.XMLHTTP");
adamc@609 153 } catch (e) {
adamc@609 154 try {
adamc@609 155 return new ActiveXObject("Microsoft.XMLHTTP");
adamc@609 156 } catch (e) {
adamc@609 157 throw "Your browser doesn't seem to support AJAX.";
adamc@609 158 }
adamc@609 159 }
adamc@609 160 }
adamc@609 161 }
adamc@609 162
adamc@668 163 function requestUri(xhr, uri) {
adamc@668 164 xhr.open("GET", uri, true);
adamc@668 165
adamc@668 166 if (client_id != 0) {
adamc@668 167 xhr.setRequestHeader("UrWeb-Client", client_id.toString());
adamc@668 168 xhr.setRequestHeader("UrWeb-Pass", client_pass.toString());
adamc@668 169 }
adamc@668 170
adamc@668 171 xhr.send(null);
adamc@668 172 }
adamc@668 173
adamc@613 174 function rc(uri, parse, k) {
adamc@609 175 var xhr = getXHR();
adamc@609 176
adamc@609 177 xhr.onreadystatechange = function() {
adamc@612 178 if (xhr.readyState == 4) {
adamc@612 179 var isok = false;
adamc@612 180
adamc@612 181 try {
adamc@612 182 if (xhr.status == 200)
adamc@612 183 isok = true;
adamc@612 184 } catch (e) { }
adamc@612 185
adamc@612 186 if (isok)
adamc@613 187 k(parse(xhr.responseText));
adamc@649 188 else {
adamc@669 189 whine("Error querying remote server!");
adamc@649 190 }
adamc@612 191 }
adamc@609 192 };
adamc@609 193
adamc@668 194 requestUri(xhr, uri);
adamc@609 195 }
adamc@667 196
adamc@667 197 function path_join(s1, s2) {
adamc@667 198 if (s1.length > 0 && s1[s1.length-1] == '/')
adamc@667 199 return s1 + s2;
adamc@667 200 else
adamc@667 201 return s1 + "/" + s2;
adamc@667 202 }
adamc@667 203
adamc@670 204 var channels = [];
adamc@670 205
adamc@670 206 function newQueue() {
adamc@670 207 return { front : null, back : null };
adamc@670 208 }
adamc@670 209 function enqueue(q, v) {
adamc@670 210 if (q.front == null) {
adamc@670 211 q.front = cons(v, null);
adamc@670 212 q.back = q.front;
adamc@670 213 } else {
adamc@670 214 var node = cons(v, null);
adamc@670 215 q.back.n = node;
adamc@670 216 q.back = node;
adamc@670 217 }
adamc@670 218 }
adamc@670 219 function dequeue(q) {
adamc@670 220 if (q.front == null)
adamc@670 221 return null;
adamc@670 222 else {
adamc@670 223 var r = q.front.v;
adamc@670 224 q.front = q.front.n;
adamc@670 225 if (q.front == null)
adamc@670 226 q.back = null;
adamc@670 227 return r;
adamc@670 228 }
adamc@670 229 }
adamc@670 230
adamc@670 231 function newChannel() {
adamc@670 232 return { msgs : newQueue(), listeners : newQueue() };
adamc@670 233 }
adamc@670 234
adamc@667 235 function listener() {
adamc@668 236 var uri = path_join(url_prefix, ".msgs");
adamc@667 237 var xhr = getXHR();
adamc@673 238 var tid, orsc, onTimeout;
adamc@673 239
adamc@673 240 var connect = function () {
adamc@673 241 xhr.onreadystatechange = orsc;
adamc@673 242 tid = window.setTimeout(onTimeout, timeout * 500);
adamc@673 243 requestUri(xhr, uri);
adamc@673 244 }
adamc@673 245
adamc@673 246 orsc = function() {
adamc@667 247 if (xhr.readyState == 4) {
adamc@673 248 window.clearTimeout(tid);
adamc@673 249
adamc@667 250 var isok = false;
adamc@667 251
adamc@667 252 try {
adamc@667 253 if (xhr.status == 200)
adamc@667 254 isok = true;
adamc@667 255 } catch (e) { }
adamc@667 256
adamc@668 257 if (isok) {
adamc@669 258 var lines = xhr.responseText.split("\n");
adamc@669 259 if (lines.length < 2)
adamc@679 260 return; //throw "Empty message from remote server";
adamc@669 261
adamc@669 262 for (var i = 0; i+1 < lines.length; i += 2) {
adamc@670 263 var chn = lines[i];
adamc@670 264 var msg = lines[i+1];
adamc@670 265
adamc@670 266 if (chn < 0)
adamc@670 267 whine("Out-of-bounds channel in message from remote server");
adamc@670 268
adamc@670 269 var ch;
adamc@670 270
adamc@670 271 if (chn >= channels.length || channels[chn] == null) {
adamc@670 272 ch = newChannel();
adamc@670 273 channels[chn] = ch;
adamc@670 274 } else
adamc@670 275 ch = channels[chn];
adamc@670 276
adamc@670 277 var listener = dequeue(ch.listeners);
adamc@670 278 if (listener == null) {
adamc@670 279 enqueue(ch.msgs, msg);
adamc@670 280 } else {
adamc@670 281 listener(msg);
adamc@670 282 }
adamc@669 283 }
adamc@669 284
adamc@673 285 connect();
adamc@668 286 }
adamc@667 287 else {
adamc@679 288 /*try {
adamc@672 289 whine("Error querying remote server for messages! " + xhr.status);
adamc@679 290 } catch (e) { }*/
adamc@667 291 }
adamc@667 292 }
adamc@667 293 };
adamc@667 294
adamc@673 295 onTimeout = function() {
adamc@673 296 xhr.abort();
adamc@673 297 connect();
adamc@673 298 };
adamc@673 299
adamc@673 300 connect();
adamc@667 301 }
adamc@670 302
adamc@679 303 var listener_started = false;
adamc@679 304
adamc@670 305 function rv(chn, parse, k) {
adamc@670 306 if (chn < 0)
adamc@670 307 whine("Out-of-bounds channel receive");
adamc@670 308
adamc@679 309 if (!listener_started) {
adamc@679 310 listener_started = true;
adamc@679 311 listener();
adamc@679 312 }
adamc@679 313
adamc@670 314 var ch;
adamc@670 315
adamc@670 316 if (chn >= channels.length || channels[chn] == null) {
adamc@670 317 ch = newChannel();
adamc@670 318 channels[chn] = ch;
adamc@670 319 } else
adamc@670 320 ch = channels[chn];
adamc@670 321
adamc@670 322 var msg = dequeue(ch.msgs);
adamc@670 323 if (msg == null) {
adamc@670 324 enqueue(ch.listeners, function(msg) { k(parse(msg))(null); });
adamc@670 325 } else {
adamc@670 326 k(parse(msg))(null);
adamc@670 327 }
adamc@670 328 }
adamc@670 329
adamc@679 330 function uf(s) {
adamc@679 331 return escape(s).replace(new RegExp ("/", "g"), "%2F");
adamc@670 332 }
adamc@679 333
adamc@679 334 function uu(s) {
adamc@679 335 return unescape(s).replace(new RegExp ("\\+", "g"), " ");
adamc@679 336 }