Mercurial > urweb
comparison lib/js/urweb.js @ 1260:25ebd8c4fafb
Thunking recursive JavaScripted function ASTs, to reduce page load time dramatically
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Sat, 22 May 2010 14:09:06 -0400 |
parents | 83b1853d1e58 |
children | 003df929ee08 |
comparison
equal
deleted
inserted
replaced
1259:83b1853d1e58 | 1260:25ebd8c4fafb |
---|---|
1 // Detect browser quirks that we should be aware of. | 1 // Detect browser quirks that we should be aware of. |
2 | 2 |
3 function needsDynPrefix() { | 3 function needsDynPrefix() { |
4 var span = document.createElement("span"); | 4 var span = document.createElement("span"); |
5 span.innerHTML = "<script>alert('test');</script>"; | 5 span.innerHTML = "<script>alert('test');</script>"; |
6 var scripts = span.getElementsByTagName("script"); | 6 var scripts = span.getElementsByTagName("script"); |
7 return scripts.length == 0; | 7 return scripts.length == 0; |
8 } | 8 } |
9 | 9 |
10 var dynPrefix = needsDynPrefix() ? "<span style=\"display:none\">A</span>" : ""; | 10 var dynPrefix = needsDynPrefix() ? "<span style=\"display:none\">A</span>" : ""; |
11 | 11 |
12 // Function versions of operators | 12 // Function versions of operators |
38 | 38 |
39 | 39 |
40 // Lists | 40 // Lists |
41 | 41 |
42 function cons(v, ls) { | 42 function cons(v, ls) { |
43 return { next : ls, data : v }; | 43 return { next : ls, data : v }; |
44 } | 44 } |
45 function rev(ls) { | 45 function rev(ls) { |
46 var acc = null; | 46 var acc = null; |
47 for (; ls; ls = ls.next) | 47 for (; ls; ls = ls.next) |
48 acc = cons(ls.data, acc); | 48 acc = cons(ls.data, acc); |
49 return acc; | 49 return acc; |
50 } | 50 } |
51 function concat(ls1, ls2) { | 51 function concat(ls1, ls2) { |
52 var acc = ls2; | 52 var acc = ls2; |
53 ls1 = rev(ls1); | 53 ls1 = rev(ls1); |
54 for (; ls1; ls1 = ls1.next) | 54 for (; ls1; ls1 = ls1.next) |
55 acc = cons(ls1.data, acc); | 55 acc = cons(ls1.data, acc); |
56 return acc; | 56 return acc; |
57 } | 57 } |
58 function member(x, ls) { | 58 function member(x, ls) { |
59 for (; ls; ls = ls.next) | 59 for (; ls; ls = ls.next) |
60 if (ls.data == x) | 60 if (ls.data == x) |
61 return true; | 61 return true; |
62 return false; | 62 return false; |
63 } | 63 } |
64 function remove(x, ls) { | 64 function remove(x, ls) { |
65 var acc = null; | 65 var acc = null; |
66 | 66 |
67 for (; ls; ls = ls.next) | 67 for (; ls; ls = ls.next) |
68 if (ls.data == x) | 68 if (ls.data == x) |
69 return concat(acc, ls.next); | 69 return concat(acc, ls.next); |
70 else | |
71 acc = cons(ls.data, acc); | |
72 | |
73 return ls; | |
74 } | |
75 function union(ls1, ls2) { | |
76 var acc = ls2; | |
77 | |
78 for (; ls1; ls1 = ls1.next) | |
79 if (!member(ls1.data, ls2)) | |
80 acc = cons(ls1.data, acc); | |
81 | |
82 return acc; | |
83 } | |
84 function length(ls) { | |
85 var acc = 0; | |
86 | |
87 for (; ls; ls = ls.next) | |
88 ++acc; | |
89 | |
90 return acc; | |
91 } | |
92 | |
93 | |
94 // Error handling | |
95 | |
96 function whine(msg) { | |
97 alert(msg); | |
98 throw msg; | |
99 } | |
100 | |
101 function pf(loc) { | |
102 throw ("Pattern match failure (" + loc + ")"); | |
103 } | |
104 | |
105 function runHandlers(kind, ls, arg) { | |
106 if (ls == null) | |
107 alert(kind + ": " + arg); | |
108 for (; ls; ls = ls.next) | |
109 try { | |
110 exec({c:"a", f:{c:"a", f:ls.data, x:{c:"c", v:arg}}, x:{c:"c", v:null}}); | |
111 } catch (v) { } | |
112 } | |
113 | |
114 var errorHandlers = null; | |
115 | |
116 function onError(f) { | |
117 errorHandlers = cons(f, errorHandlers); | |
118 } | |
119 | |
120 function er(s) { | |
121 runHandlers("Error", errorHandlers, s); | |
122 throw {uw_error: s}; | |
123 } | |
124 | |
125 var failHandlers = null; | |
126 | |
127 function onFail(f) { | |
128 failHandlers = cons(f, failHandlers); | |
129 } | |
130 | |
131 function doExn(v) { | |
132 if (v == null || v.uw_error == null) { | |
133 var s = (v == null ? "null" : v.message ? v.message : v.toString()); | |
134 if (v != null && v.fileName && v.lineNumber) | |
135 s += " (" + v.fileName + ":" + v.lineNumber + ")"; | |
136 runHandlers("Fail", failHandlers, s); | |
137 } | |
138 } | |
139 | |
140 var disconnectHandlers = null; | |
141 | |
142 function flift(f) { | |
143 return {env:cons(f,null), body:{c:"v", n:1}}; | |
144 } | |
145 | |
146 function onDisconnect(f) { | |
147 disconnectHandlers = cons(flift(f), disconnectHandlers); | |
148 } | |
149 | |
150 function discon() { | |
151 runHandlers("Disconnect", disconnectHandlers, null); | |
152 } | |
153 | |
154 var connectHandlers = null; | |
155 | |
156 function onConnectFail(f) { | |
157 connectHandlers = cons(flift(f), connectHandlers); | |
158 } | |
159 | |
160 function conn() { | |
161 runHandlers("Connect", connectHandlers, null); | |
162 } | |
163 | |
164 var serverHandlers = null; | |
165 | |
166 function onServerError(f) { | |
167 serverHandlers = cons(f, serverHandlers); | |
168 } | |
169 | |
170 function servErr(s) { | |
171 window.setTimeout(function () { runHandlers("Server", serverHandlers, s); }, 0); | |
172 } | |
173 | |
174 | |
175 // Embedding closures in XML strings | |
176 | |
177 function cs(f) { | |
178 return {closure: f}; | |
179 } | |
180 | |
181 function isWeird(v) { | |
182 return v.closure != null || v.cat1 != null; | |
183 } | |
184 | |
185 function cat(s1, s2) { | |
186 if (isWeird(s1) || isWeird(s2)) | |
187 return {cat1: s1, cat2: s2}; | |
70 else | 188 else |
71 acc = cons(ls.data, acc); | 189 return s1 + s2; |
72 | |
73 return ls; | |
74 } | |
75 function union(ls1, ls2) { | |
76 var acc = ls2; | |
77 | |
78 for (; ls1; ls1 = ls1.next) | |
79 if (!member(ls1.data, ls2)) | |
80 acc = cons(ls1.data, acc); | |
81 | |
82 return acc; | |
83 } | |
84 function length(ls) { | |
85 var acc = 0; | |
86 | |
87 for (; ls; ls = ls.next) | |
88 ++acc; | |
89 | |
90 return acc; | |
91 } | |
92 | |
93 | |
94 // Error handling | |
95 | |
96 function whine(msg) { | |
97 alert(msg); | |
98 throw msg; | |
99 } | |
100 | |
101 function pf(loc) { | |
102 throw ("Pattern match failure (" + loc + ")"); | |
103 } | |
104 | |
105 function runHandlers(kind, ls, arg) { | |
106 if (ls == null) | |
107 alert(kind + ": " + arg); | |
108 for (; ls; ls = ls.next) | |
109 try { | |
110 exec({c:"a", f:{c:"a", f:ls.data, x:{c:"c", v:arg}}, x:{c:"c", v:null}}); | |
111 } catch (v) { } | |
112 } | |
113 | |
114 var errorHandlers = null; | |
115 | |
116 function onError(f) { | |
117 errorHandlers = cons(f, errorHandlers); | |
118 } | |
119 | |
120 function er(s) { | |
121 runHandlers("Error", errorHandlers, s); | |
122 throw {uw_error: s}; | |
123 } | |
124 | |
125 var failHandlers = null; | |
126 | |
127 function onFail(f) { | |
128 failHandlers = cons(f, failHandlers); | |
129 } | |
130 | |
131 function doExn(v) { | |
132 if (v == null || v.uw_error == null) { | |
133 var s = (v == null ? "null" : v.message ? v.message : v.toString()); | |
134 if (v != null && v.fileName && v.lineNumber) | |
135 s += " (" + v.fileName + ":" + v.lineNumber + ")"; | |
136 runHandlers("Fail", failHandlers, s); | |
137 } | |
138 } | |
139 | |
140 var disconnectHandlers = null; | |
141 | |
142 function flift(f) { | |
143 return {env:cons(f,null), body:{c:"v", n:1}}; | |
144 } | |
145 | |
146 function onDisconnect(f) { | |
147 disconnectHandlers = cons(flift(f), disconnectHandlers); | |
148 } | |
149 | |
150 function discon() { | |
151 runHandlers("Disconnect", disconnectHandlers, null); | |
152 } | |
153 | |
154 var connectHandlers = null; | |
155 | |
156 function onConnectFail(f) { | |
157 connectHandlers = cons(flift(f), connectHandlers); | |
158 } | |
159 | |
160 function conn() { | |
161 runHandlers("Connect", connectHandlers, null); | |
162 } | |
163 | |
164 var serverHandlers = null; | |
165 | |
166 function onServerError(f) { | |
167 serverHandlers = cons(f, serverHandlers); | |
168 } | |
169 | |
170 function servErr(s) { | |
171 window.setTimeout(function () { runHandlers("Server", serverHandlers, s); }, 0); | |
172 } | |
173 | |
174 | |
175 // Embedding closures in XML strings | |
176 | |
177 function cs(f) { | |
178 return {closure: f}; | |
179 } | |
180 | |
181 function isWeird(v) { | |
182 return v.closure != null || v.cat1 != null; | |
183 } | |
184 | |
185 function cat(s1, s2) { | |
186 if (isWeird(s1) || isWeird(s2)) | |
187 return {cat1: s1, cat2: s2}; | |
188 else | |
189 return s1 + s2; | |
190 } | 190 } |
191 | 191 |
192 var closures = []; | 192 var closures = []; |
193 var freeClosures = null; | 193 var freeClosures = null; |
194 | 194 |
195 function newClosure(f) { | 195 function newClosure(f) { |
196 var n; | 196 var n; |
197 if (freeClosures == null) { | 197 if (freeClosures == null) { |
198 n = closures.length; | 198 n = closures.length; |
199 } else { | 199 } else { |
200 n = freeClosures.data; | 200 n = freeClosures.data; |
201 freeClosures = freeClosures.next; | 201 freeClosures = freeClosures.next; |
202 } | 202 } |
203 closures[n] = f; | 203 closures[n] = f; |
204 return n; | 204 return n; |
205 } | 205 } |
206 | 206 |
207 function freeClosure(n) { | 207 function freeClosure(n) { |
208 closures[n] = null; | 208 closures[n] = null; |
209 freeClosures = cons(n, freeClosures); | 209 freeClosures = cons(n, freeClosures); |
210 } | 210 } |
211 | 211 |
212 function cr(n) { | 212 function cr(n) { |
213 return closures[n]; | 213 return closures[n]; |
214 } | 214 } |
215 | 215 |
216 function flatten(cls, tr) { | 216 function flatten(cls, tr) { |
217 if (tr.cat1 != null) | 217 if (tr.cat1 != null) |
218 return flatten(cls, tr.cat1) + flatten(cls, tr.cat2); | 218 return flatten(cls, tr.cat1) + flatten(cls, tr.cat2); |
219 else if (tr.closure != null) { | 219 else if (tr.closure != null) { |
220 var cl = newClosure(tr.closure); | 220 var cl = newClosure(tr.closure); |
221 cls.v = cons(cl, cls.v); | 221 cls.v = cons(cl, cls.v); |
222 return "cr(" + cl + ")"; | 222 return "cr(" + cl + ")"; |
223 } else | 223 } else |
224 return tr; | 224 return tr; |
225 } | 225 } |
226 | 226 |
227 function flattenLocal(s) { | 227 function flattenLocal(s) { |
228 var cls = {v : null}; | 228 var cls = {v : null}; |
229 var r = flatten(cls, s); | 229 var r = flatten(cls, s); |
230 for (cl = cls.v; cl != null; cl = cl.next) | 230 for (cl = cls.v; cl != null; cl = cl.next) |
231 freeClosure(cl.data); | 231 freeClosure(cl.data); |
232 return r; | 232 return r; |
233 } | 233 } |
234 | 234 |
235 | 235 |
236 | 236 |
237 // Dynamic tree management | 237 // Dynamic tree management |
238 | 238 |
239 function populate(node) { | 239 function populate(node) { |
240 var s = node.signal; | 240 var s = node.signal; |
241 var oldSources = node.sources; | 241 var oldSources = node.sources; |
242 try { | 242 try { |
243 var sr = execF(s, null); | 243 var sr = execF(s, null); |
244 var newSources = sr._sources; | 244 var newSources = sr._sources; |
245 | 245 |
246 for (var sp = oldSources; sp; sp = sp.next) | 246 for (var sp = oldSources; sp; sp = sp.next) |
247 if (!member(sp.data, newSources)) | 247 if (!member(sp.data, newSources)) |
248 sp.data.dyns = remove(node, sp.data.dyns); | 248 sp.data.dyns = remove(node, sp.data.dyns); |
249 | 249 |
250 for (var sp = newSources; sp; sp = sp.next) | 250 for (var sp = newSources; sp; sp = sp.next) |
251 if (!member(sp.data, oldSources)) | 251 if (!member(sp.data, oldSources)) |
252 sp.data.dyns = cons(node, sp.data.dyns); | 252 sp.data.dyns = cons(node, sp.data.dyns); |
253 | 253 |
254 node.sources = newSources; | 254 node.sources = newSources; |
255 node.recreate(sr._data); | 255 node.recreate(sr._data); |
256 } catch (v) { | 256 } catch (v) { |
257 doExn(v); | 257 doExn(v); |
258 } | 258 } |
259 } | 259 } |
260 | 260 |
261 function sc(v) { | 261 function sc(v) { |
262 return {data : v, dyns : null}; | 262 return {data : v, dyns : null}; |
263 } | 263 } |
264 function sv(s, v) { | 264 function sv(s, v) { |
265 s.data = v; | 265 s.data = v; |
266 | 266 |
267 for (var ls = s.dyns; ls; ls = ls.next) | 267 for (var ls = s.dyns; ls; ls = ls.next) |
268 if (!ls.dead) | 268 if (!ls.dead) |
269 populate(ls.data); | 269 populate(ls.data); |
270 } | 270 } |
271 function sg(s) { | 271 function sg(s) { |
272 return s.data; | 272 return s.data; |
273 } | 273 } |
274 | 274 |
275 function ss(s) { | 275 function ss(s) { |
276 return {env:cons(s, null), body:{c:"r", l: | 276 return {env:cons(s, null), body:{c:"r", l: |
277 cons({n:"sources", v:{c:"c", v:cons(s, null)}}, | 277 cons({n:"sources", v:{c:"c", v:cons(s, null)}}, |
278 cons({n:"data", v:{c:"f", f:sg, a:cons({c:"v", n:1}, null)}}, null))}}; | 278 cons({n:"data", v:{c:"f", f:sg, a:cons({c:"v", n:1}, null)}}, null))}}; |
279 } | 279 } |
280 function sr(v) { | 280 function sr(v) { |
281 return {env:null, body:{c:"c", v:{_sources : null, _data : v}}}; | 281 return {env:null, body:{c:"c", v:{_sources : null, _data : v}}}; |
282 } | 282 } |
283 function sb(x,y) { | 283 function sb(x,y) { |
284 return {env:cons(y,cons(x,null)), | 284 return {env:cons(y,cons(x,null)), |
285 body:{c:"=", | 285 body:{c:"=", |
286 e1:{c:"a", f:{c:"v", n:2}, x:{c:"c", v:null}}, | 286 e1:{c:"a", f:{c:"v", n:2}, x:{c:"c", v:null}}, |
287 e2:{c:"=", | 287 e2:{c:"=", |
288 e1:{c:"a", | 288 e1:{c:"a", |
289 f:{c:"a", f:{c:"v", n:2}, x:{c:".", r:{c:"v", n:0}, f:"data"}}, | 289 f:{c:"a", f:{c:"v", n:2}, x:{c:".", r:{c:"v", n:0}, f:"data"}}, |
290 x:{c:"c", v:null}}, | 290 x:{c:"c", v:null}}, |
291 e2:{c:"r", l:cons( | 291 e2:{c:"r", l:cons( |
292 {n:"sources", v:{c:"f", f:union, a:cons({c:".", r:{c:"v", n:1}, f:"sources"}, | 292 {n:"sources", v:{c:"f", f:union, a:cons({c:".", r:{c:"v", n:1}, f:"sources"}, |
293 cons({c:".", r:{c:"v", n:0}, f:"sources"}, null))}}, | 293 cons({c:".", r:{c:"v", n:0}, f:"sources"}, null))}}, |
294 cons({n:"data", v:{c:".", r:{c:"v", n:0}, f:"data"}}, null))}}}}; | 294 cons({n:"data", v:{c:".", r:{c:"v", n:0}, f:"data"}}, null))}}}}; |
295 } | 295 } |
296 function scur(s) { | 296 function scur(s) { |
297 return execF(s, null)._data; | 297 return execF(s, null)._data; |
298 } | 298 } |
299 | 299 |
300 function lastParent() { | 300 function lastParent() { |
301 var pos = document.body; | 301 var pos = document.body; |
302 | 302 |
303 while (pos.lastChild && pos.lastChild.nodeType == 1) | 303 while (pos.lastChild && pos.lastChild.nodeType == 1) |
304 pos = pos.lastChild; | 304 pos = pos.lastChild; |
305 | 305 |
306 pos = pos.parentNode; | 306 pos = pos.parentNode; |
307 | 307 |
308 return pos; | 308 return pos; |
309 } | 309 } |
310 | 310 |
311 function parent() { | 311 function parent() { |
312 return thisScript ? thisScript.parentNode : lastParent(); | 312 return thisScript ? thisScript.parentNode : lastParent(); |
313 } | 313 } |
314 | 314 |
315 function addNode(node) { | 315 function addNode(node) { |
316 if (thisScript) | 316 if (thisScript) |
317 thisScript.parentNode.replaceChild(node, thisScript); | 317 thisScript.parentNode.replaceChild(node, thisScript); |
318 else | 318 else |
319 lastParent().appendChild(node); | 319 lastParent().appendChild(node); |
320 } | 320 } |
321 | 321 |
322 var thisScript = null; | 322 var thisScript = null; |
323 | 323 |
324 function runScripts(node) { | 324 function runScripts(node) { |
325 if (node.getElementsByTagName) { | 325 if (node.getElementsByTagName) { |
326 var savedScript = thisScript; | 326 var savedScript = thisScript; |
327 | 327 |
328 var scripts = node.getElementsByTagName("script"), scriptsCopy = []; | 328 var scripts = node.getElementsByTagName("script"), scriptsCopy = []; |
329 var len = scripts.length; | 329 var len = scripts.length; |
330 for (var i = 0; i < len; ++i) | 330 for (var i = 0; i < len; ++i) |
331 scriptsCopy[i] = scripts[i]; | 331 scriptsCopy[i] = scripts[i]; |
332 for (var i = 0; i < len; ++i) { | 332 for (var i = 0; i < len; ++i) { |
333 thisScript = scriptsCopy[i]; | 333 thisScript = scriptsCopy[i]; |
334 | 334 |
335 try { | 335 try { |
336 eval(thisScript.text); | 336 eval(thisScript.text); |
337 } catch (v) { | 337 } catch (v) { |
338 doExn(v); | 338 doExn(v); |
339 } | 339 } |
340 if (thisScript.parentNode) | 340 if (thisScript.parentNode) |
341 thisScript.parentNode.removeChild(thisScript); | 341 thisScript.parentNode.removeChild(thisScript); |
342 } | 342 } |
343 | 343 |
344 thisScript = savedScript; | 344 thisScript = savedScript; |
345 } | 345 } |
346 } | 346 } |
347 | 347 |
348 | 348 |
349 // Dynamic tree entry points | 349 // Dynamic tree entry points |
350 | 350 |
351 function killScript(scr) { | 351 function killScript(scr) { |
352 scr.dead = true; | 352 scr.dead = true; |
353 for (var ls = scr.sources; ls; ls = ls.next) | 353 for (var ls = scr.sources; ls; ls = ls.next) |
354 ls.data.dyns = remove(scr, ls.data.dyns); | 354 ls.data.dyns = remove(scr, ls.data.dyns); |
355 for (var ls = scr.closures; ls; ls = ls.next) | 355 for (var ls = scr.closures; ls; ls = ls.next) |
356 freeClosure(ls.data); | 356 freeClosure(ls.data); |
357 } | 357 } |
358 | 358 |
359 function dyn(pnode, s) { | 359 function dyn(pnode, s) { |
360 var x = document.createElement("script"); | 360 var x = document.createElement("script"); |
361 x.dead = false; | 361 x.dead = false; |
362 x.signal = s; | 362 x.signal = s; |
363 x.sources = null; | 363 x.sources = null; |
364 x.closures = null; | 364 x.closures = null; |
365 | 365 |
366 var firstChild = null; | 366 var firstChild = null; |
367 | 367 |
368 x.recreate = function(v) { | 368 x.recreate = function(v) { |
369 for (var ls = x.closures; ls; ls = ls.next) | 369 for (var ls = x.closures; ls; ls = ls.next) |
370 freeClosure(ls.data); | 370 freeClosure(ls.data); |
371 | |
372 var next; | |
373 for (var child = firstChild; child && child != x; child = next) { | |
374 next = child.nextSibling; | |
375 | |
376 killScript(child); | |
377 if (child.getElementsByTagName) { | |
378 var arr = child.getElementsByTagName("script"); | |
379 for (var i = 0; i < arr.length; ++i) | |
380 killScript(arr[i]); | |
381 } | |
382 | |
383 if (child.parentNode) | |
384 child.parentNode.removeChild(child); | |
385 } | |
386 | |
387 var cls = {v : null}; | |
388 var html = flatten(cls, v); | |
389 if (pnode != "table" && pnode != "tr") | |
390 html = dynPrefix + html; | |
391 x.closures = cls.v; | |
392 | |
393 if (pnode == "table") { | |
394 var dummy = document.createElement("body"); | |
395 dummy.innerHTML = "<table>" + html + "</table>"; | |
396 runScripts(dummy); | |
397 var table = x.parentNode; | |
398 | |
399 if (table) { | |
400 firstChild = null; | |
401 var tbody; | |
402 | |
403 var arr = dummy.getElementsByTagName("tbody"); | |
404 | |
405 var tbody; | |
406 if (arr.length > 0 && arr[0].parentNode == dummy.firstChild) { | |
407 tbody = arr[0]; | |
408 var next; | |
409 for (var node = dummy.firstChild.firstChild; node; node = next) { | |
410 next = node.nextSibling; | |
411 | |
412 if (node.tagName != "TBODY") | |
413 tbody.appendChild(node); | |
414 } | |
415 } else | |
416 tbody = dummy.firstChild; | |
417 | 371 |
418 var next; | 372 var next; |
419 firstChild = document.createElement("script"); | 373 for (var child = firstChild; child && child != x; child = next) { |
420 table.insertBefore(firstChild, x); | 374 next = child.nextSibling; |
421 for (var node = tbody.firstChild; node; node = next) { | 375 |
422 next = node.nextSibling; | 376 killScript(child); |
423 table.insertBefore(node, x); | 377 if (child.getElementsByTagName) { |
378 var arr = child.getElementsByTagName("script"); | |
379 for (var i = 0; i < arr.length; ++i) | |
380 killScript(arr[i]); | |
381 } | |
382 | |
383 if (child.parentNode) | |
384 child.parentNode.removeChild(child); | |
424 } | 385 } |
425 } | 386 |
426 } else if (pnode == "tr") { | 387 var cls = {v : null}; |
427 var dummy = document.createElement("body"); | 388 var html = flatten(cls, v); |
428 dummy.innerHTML = "<table><tr>" + html + "</tr></table>"; | 389 if (pnode != "table" && pnode != "tr") |
429 runScripts(dummy); | 390 html = dynPrefix + html; |
430 var table = x.parentNode; | 391 x.closures = cls.v; |
431 | 392 |
432 if (table) { | 393 if (pnode == "table") { |
433 var arr = dummy.getElementsByTagName("tr"); | 394 var dummy = document.createElement("body"); |
434 firstChild = null; | 395 dummy.innerHTML = "<table>" + html + "</table>"; |
435 var tr; | 396 runScripts(dummy); |
436 if (arr.length > 0 && table != null) | 397 var table = x.parentNode; |
437 tr = arr[0]; | 398 |
438 else | 399 if (table) { |
439 tr = dummy.firstChild; | 400 firstChild = null; |
440 | 401 var tbody; |
441 var next; | 402 |
442 firstChild = document.createElement("script"); | 403 var arr = dummy.getElementsByTagName("tbody"); |
443 table.insertBefore(firstChild, x); | 404 |
444 for (var node = tr.firstChild; node; node = next) { | 405 var tbody; |
445 next = node.nextSibling; | 406 if (arr.length > 0 && arr[0].parentNode == dummy.firstChild) { |
446 table.insertBefore(node, x); | 407 tbody = arr[0]; |
408 var next; | |
409 for (var node = dummy.firstChild.firstChild; node; node = next) { | |
410 next = node.nextSibling; | |
411 | |
412 if (node.tagName != "TBODY") | |
413 tbody.appendChild(node); | |
414 } | |
415 } else | |
416 tbody = dummy.firstChild; | |
417 | |
418 var next; | |
419 firstChild = document.createElement("script"); | |
420 table.insertBefore(firstChild, x); | |
421 for (var node = tbody.firstChild; node; node = next) { | |
422 next = node.nextSibling; | |
423 table.insertBefore(node, x); | |
424 } | |
425 } | |
426 } else if (pnode == "tr") { | |
427 var dummy = document.createElement("body"); | |
428 dummy.innerHTML = "<table><tr>" + html + "</tr></table>"; | |
429 runScripts(dummy); | |
430 var table = x.parentNode; | |
431 | |
432 if (table) { | |
433 var arr = dummy.getElementsByTagName("tr"); | |
434 firstChild = null; | |
435 var tr; | |
436 if (arr.length > 0 && table != null) | |
437 tr = arr[0]; | |
438 else | |
439 tr = dummy.firstChild; | |
440 | |
441 var next; | |
442 firstChild = document.createElement("script"); | |
443 table.insertBefore(firstChild, x); | |
444 for (var node = tr.firstChild; node; node = next) { | |
445 next = node.nextSibling; | |
446 table.insertBefore(node, x); | |
447 } | |
448 } | |
449 } else { | |
450 firstChild = document.createElement("span"); | |
451 firstChild.innerHTML = html; | |
452 runScripts(firstChild); | |
453 if (x.parentNode) | |
454 x.parentNode.insertBefore(firstChild, x); | |
447 } | 455 } |
448 } | 456 }; |
449 } else { | 457 |
450 firstChild = document.createElement("span"); | 458 addNode(x); |
451 firstChild.innerHTML = html; | 459 populate(x); |
452 runScripts(firstChild); | |
453 if (x.parentNode) | |
454 x.parentNode.insertBefore(firstChild, x); | |
455 } | |
456 }; | |
457 | |
458 addNode(x); | |
459 populate(x); | |
460 } | 460 } |
461 | 461 |
462 function input(x, s, recreate, type) { | 462 function input(x, s, recreate, type) { |
463 if (type) x.type = type; | 463 if (type) x.type = type; |
464 x.dead = false; | 464 x.dead = false; |
465 x.signal = ss(s); | 465 x.signal = ss(s); |
466 x.sources = null; | 466 x.sources = null; |
467 x.recreate = recreate(x); | 467 x.recreate = recreate(x); |
468 addNode(x); | 468 addNode(x); |
469 populate(x); | 469 populate(x); |
470 | 470 |
471 return x; | 471 return x; |
472 } | 472 } |
473 | 473 |
474 function inp(s) { | 474 function inp(s) { |
475 var x = input(document.createElement("input"), s, | 475 var x = input(document.createElement("input"), s, |
476 function(x) { return function(v) { if (x.value != v) x.value = v; }; }); | 476 function(x) { return function(v) { if (x.value != v) x.value = v; }; }); |
477 x.value = s.data; | 477 x.value = s.data; |
478 x.onkeyup = function() { sv(s, x.value) }; | 478 x.onkeyup = function() { sv(s, x.value) }; |
479 | 479 |
480 return x; | 480 return x; |
481 } | 481 } |
482 | 482 |
483 function sel(s, content) { | 483 function sel(s, content) { |
484 var dummy = document.createElement("span"); | 484 var dummy = document.createElement("span"); |
485 dummy.innerHTML = "<select>" + content + "</select>"; | 485 dummy.innerHTML = "<select>" + content + "</select>"; |
486 var x = input(dummy.firstChild, s, function(x) { return function(v) { if (x.value != v) x.value = v; }; }); | 486 var x = input(dummy.firstChild, s, function(x) { return function(v) { if (x.value != v) x.value = v; }; }); |
487 x.value = s.data; | 487 x.value = s.data; |
488 if (x.value != s.data) | 488 if (x.value != s.data) |
489 sv(s, x.value); | 489 sv(s, x.value); |
490 x.onchange = function() { sv(s, x.value) }; | 490 x.onchange = function() { sv(s, x.value) }; |
491 | 491 |
492 return x; | 492 return x; |
493 } | 493 } |
494 | 494 |
495 function chk(s) { | 495 function chk(s) { |
496 var x = input(document.createElement("input"), s, | 496 var x = input(document.createElement("input"), s, |
497 function(x) { return function(v) { if (x.checked != v) x.checked = v; }; }, "checkbox"); | 497 function(x) { return function(v) { if (x.checked != v) x.checked = v; }; }, "checkbox"); |
498 x.defaultChecked = x.checked = s.data; | 498 x.defaultChecked = x.checked = s.data; |
499 x.onclick = function() { sv(s, x.checked) }; | 499 x.onclick = function() { sv(s, x.checked) }; |
500 | 500 |
501 return x; | 501 return x; |
502 } | 502 } |
503 | 503 |
504 function tbx(s) { | 504 function tbx(s) { |
505 var x = input(document.createElement("textarea"), s, | 505 var x = input(document.createElement("textarea"), s, |
506 function(x) { return function(v) { if (x.innerHTML != v) x.innerHTML = v; }; }); | 506 function(x) { return function(v) { if (x.innerHTML != v) x.innerHTML = v; }; }); |
507 x.innerHTML = s.data; | 507 x.innerHTML = s.data; |
508 x.onkeyup = function() { sv(s, x.value) }; | 508 x.onkeyup = function() { sv(s, x.value) }; |
509 | 509 |
510 return x; | 510 return x; |
511 } | 511 } |
512 | 512 |
513 function addOnChange(x, f) { | 513 function addOnChange(x, f) { |
514 var old = x.onchange; | 514 var old = x.onchange; |
515 x.onchange = function() { old(); f (); }; | 515 x.onchange = function() { old(); f (); }; |
516 } | 516 } |
517 | 517 |
518 | 518 |
519 // Basic string operations | 519 // Basic string operations |
520 | 520 |
521 function eh(x) { | 521 function eh(x) { |
522 if (x == null) | 522 if (x == null) |
523 return "NULL"; | 523 return "NULL"; |
524 else | 524 else |
525 return x.split("&").join("&").split("<").join("<").split(">").join(">"); | 525 return x.split("&").join("&").split("<").join("<").split(">").join(">"); |
526 } | 526 } |
527 | 527 |
528 function ts(x) { return x.toString() } | 528 function ts(x) { return x.toString() } |
529 function bs(b) { return (b ? "True" : "False") } | 529 function bs(b) { return (b ? "True" : "False") } |
530 | 530 |
531 function id(x) { return x; } | 531 function id(x) { return x; } |
532 function sub(s, i) { return s.charAt(i); } | 532 function sub(s, i) { return s.charAt(i); } |
533 function suf(s, i) { return s.substring(i); } | 533 function suf(s, i) { return s.substring(i); } |
534 function slen(s) { return s.length; } | 534 function slen(s) { return s.length; } |
535 function sidx(s, ch) { | 535 function sidx(s, ch) { |
536 var r = s.indexOf(ch); | 536 var r = s.indexOf(ch); |
537 if (r == -1) | 537 if (r == -1) |
538 return null; | |
539 else | |
540 return r; | |
541 } | |
542 function sspn(s, chs) { | |
543 for (var i = 0; i < s.length; ++i) | |
544 if (chs.indexOf(s.charAt(i)) != -1) | |
545 return i; | |
546 | |
538 return null; | 547 return null; |
539 else | |
540 return r; | |
541 } | |
542 function sspn(s, chs) { | |
543 for (var i = 0; i < s.length; ++i) | |
544 if (chs.indexOf(s.charAt(i)) != -1) | |
545 return i; | |
546 | |
547 return null; | |
548 } | 548 } |
549 function schr(s, ch) { | 549 function schr(s, ch) { |
550 var r = s.indexOf(ch); | 550 var r = s.indexOf(ch); |
551 if (r == -1) | 551 if (r == -1) |
552 return null; | 552 return null; |
553 else | 553 else |
554 return s.substring(r); | 554 return s.substring(r); |
555 } | 555 } |
556 function ssub(s, start, len) { | 556 function ssub(s, start, len) { |
557 return s.substring(start, start+len); | 557 return s.substring(start, start+len); |
558 } | 558 } |
559 | 559 |
560 function pi(s) { | 560 function pi(s) { |
561 var r = parseInt(s); | 561 var r = parseInt(s); |
562 if (r.toString() == s) | 562 if (r.toString() == s) |
563 return r; | 563 return r; |
564 else | 564 else |
565 er("Can't parse int: " + s); | 565 er("Can't parse int: " + s); |
566 } | 566 } |
567 | 567 |
568 function pfl(s) { | 568 function pfl(s) { |
569 var r = parseFloat(s); | 569 var r = parseFloat(s); |
570 if (r.toString() == s) | 570 if (r.toString() == s) |
571 return r; | 571 return r; |
572 else | 572 else |
573 er("Can't parse float: " + s); | 573 er("Can't parse float: " + s); |
574 } | 574 } |
575 | 575 |
576 function pio(s) { | 576 function pio(s) { |
577 var r = parseInt(s); | 577 var r = parseInt(s); |
578 if (r.toString() == s) | 578 if (r.toString() == s) |
579 return r; | 579 return r; |
580 else | 580 else |
581 return null; | 581 return null; |
582 } | 582 } |
583 | 583 |
584 function pflo(s) { | 584 function pflo(s) { |
585 var r = parseFloat(s); | 585 var r = parseFloat(s); |
586 if (r.toString() == s) | 586 if (r.toString() == s) |
587 return r; | 587 return r; |
588 else | 588 else |
589 return null; | 589 return null; |
590 } | 590 } |
591 | 591 |
592 function uf(s) { | 592 function uf(s) { |
593 if (s.length == 0) | 593 if (s.length == 0) |
594 return "_"; | 594 return "_"; |
595 s = s.replace(new RegExp ("\\.", "g"), ".2E"); | 595 s = s.replace(new RegExp ("\\.", "g"), ".2E"); |
596 return (s.charAt(0) == '_' ? "_" : "") + encodeURIComponent(s).replace(new RegExp ("%", "g"), "."); | 596 return (s.charAt(0) == '_' ? "_" : "") + encodeURIComponent(s).replace(new RegExp ("%", "g"), "."); |
597 } | 597 } |
598 | 598 |
599 function uu(s) { | 599 function uu(s) { |
600 if (s.length > 0 && s.charAt(0) == '_') { | 600 if (s.length > 0 && s.charAt(0) == '_') { |
601 s = s.substring(1); | 601 s = s.substring(1); |
602 } else if (s.length >= 3 && (s.charAt(0) == '%' || s.charAt(0) == '.') | 602 } else if (s.length >= 3 && (s.charAt(0) == '%' || s.charAt(0) == '.') |
603 && s.charAt(1) == '5' && (s.charAt(2) == 'f' || s.charAt(2) == 'F')) | 603 && s.charAt(1) == '5' && (s.charAt(2) == 'f' || s.charAt(2) == 'F')) |
604 s = s.substring(3); | 604 s = s.substring(3); |
605 s = s.replace(new RegExp ("\\+", "g"), " "); | 605 s = s.replace(new RegExp ("\\+", "g"), " "); |
606 s = s.replace(new RegExp ("\\.", "g"), "%"); | 606 s = s.replace(new RegExp ("\\.", "g"), "%"); |
607 return decodeURIComponent(s); | 607 return decodeURIComponent(s); |
608 } | 608 } |
609 | 609 |
610 function atr(s) { | 610 function atr(s) { |
611 return s.replace(new RegExp ("\"", "g"), """).replace(new RegExp ("&", "g"), "&") | 611 return s.replace(new RegExp ("\"", "g"), """).replace(new RegExp ("&", "g"), "&") |
612 } | 612 } |
613 | 613 |
614 function ub(b) { | 614 function ub(b) { |
615 return b ? "1" : "0"; | 615 return b ? "1" : "0"; |
616 } | 616 } |
617 | 617 |
618 function uul(getToken, getData) { | 618 function uul(getToken, getData) { |
619 var tok = getToken(); | 619 var tok = getToken(); |
620 if (tok == "Nil") { | 620 if (tok == "Nil") { |
621 return null; | 621 return null; |
622 } else if (tok == "Cons") { | 622 } else if (tok == "Cons") { |
623 var d = getData(); | 623 var d = getData(); |
624 var l = uul(getToken, getData); | 624 var l = uul(getToken, getData); |
625 return {_1:d, _2:l}; | 625 return {_1:d, _2:l}; |
626 } else | 626 } else |
627 whine("Can't unmarshal list (" + tok + ")"); | 627 whine("Can't unmarshal list (" + tok + ")"); |
628 } | 628 } |
629 | 629 |
630 function strcmp(str1, str2) { | 630 function strcmp(str1, str2) { |
631 return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1)); | 631 return ((str1 == str2) ? 0 : ((str1 > str2) ? 1 : -1)); |
632 } | 632 } |
633 | 633 |
634 | 634 |
635 // Remote calls | 635 // Remote calls |
636 | 636 |
639 var url_prefix = "/"; | 639 var url_prefix = "/"; |
640 var timeout = 60; | 640 var timeout = 60; |
641 | 641 |
642 function getXHR(uri) | 642 function getXHR(uri) |
643 { | 643 { |
644 try { | |
645 return new XMLHttpRequest(); | |
646 } catch (e) { | |
647 try { | 644 try { |
648 return new ActiveXObject("Msxml2.XMLHTTP"); | 645 return new XMLHttpRequest(); |
649 } catch (e) { | 646 } catch (e) { |
650 try { | 647 try { |
651 return new ActiveXObject("Microsoft.XMLHTTP"); | 648 return new ActiveXObject("Msxml2.XMLHTTP"); |
652 } catch (e) { | 649 } catch (e) { |
653 whine("Your browser doesn't seem to support AJAX."); | 650 try { |
654 } | 651 return new ActiveXObject("Microsoft.XMLHTTP"); |
655 } | 652 } catch (e) { |
656 } | 653 whine("Your browser doesn't seem to support AJAX."); |
654 } | |
655 } | |
656 } | |
657 } | 657 } |
658 | 658 |
659 var sig = null; | 659 var sig = null; |
660 | 660 |
661 var unloading = false, inFlight = null; | 661 var unloading = false, inFlight = null; |
662 | 662 |
663 function unload() { | 663 function unload() { |
664 unloading = true; | 664 unloading = true; |
665 | 665 |
666 for (; inFlight; inFlight = inFlight.next) { | 666 for (; inFlight; inFlight = inFlight.next) { |
667 inFlight.data.abort(); | 667 inFlight.data.abort(); |
668 } | 668 } |
669 } | 669 } |
670 | 670 |
671 function requestUri(xhr, uri, needsSig) { | 671 function requestUri(xhr, uri, needsSig) { |
672 if (unloading) | 672 if (unloading) |
673 return; | 673 return; |
674 | 674 |
675 xhr.open("POST", uri, true); | 675 xhr.open("POST", uri, true); |
676 xhr.setRequestHeader("Content-type", "text/plain"); | 676 xhr.setRequestHeader("Content-type", "text/plain"); |
677 xhr.setRequestHeader("Content-length", "0"); | 677 xhr.setRequestHeader("Content-length", "0"); |
678 xhr.setRequestHeader("Connection", "close"); | 678 xhr.setRequestHeader("Connection", "close"); |
679 | 679 |
680 if (client_id != null) { | 680 if (client_id != null) { |
681 xhr.setRequestHeader("UrWeb-Client", client_id.toString()); | 681 xhr.setRequestHeader("UrWeb-Client", client_id.toString()); |
682 xhr.setRequestHeader("UrWeb-Pass", client_pass.toString()); | 682 xhr.setRequestHeader("UrWeb-Pass", client_pass.toString()); |
683 } | 683 } |
684 | 684 |
685 if (needsSig) { | 685 if (needsSig) { |
686 if (sig == null) | 686 if (sig == null) |
687 whine("Missing cookie signature!"); | 687 whine("Missing cookie signature!"); |
688 | 688 |
689 xhr.setRequestHeader("UrWeb-Sig", sig); | 689 xhr.setRequestHeader("UrWeb-Sig", sig); |
690 } | 690 } |
691 | 691 |
692 inFlight = cons(xhr, inFlight); | 692 inFlight = cons(xhr, inFlight); |
693 xhr.send(null); | 693 xhr.send(null); |
694 } | 694 } |
695 | 695 |
696 function xhrFinished(xhr) { | 696 function xhrFinished(xhr) { |
697 xhr.abort(); | 697 xhr.abort(); |
698 inFlight = remove(xhr, inFlight); | 698 inFlight = remove(xhr, inFlight); |
699 } | 699 } |
700 | 700 |
701 function unurlify(parse, s) { | 701 function unurlify(parse, s) { |
702 return parse(s); | 702 return parse(s); |
703 } | 703 } |
704 | 704 |
705 function rc(prefix, uri, parse, k, needsSig) { | 705 function rc(prefix, uri, parse, k, needsSig) { |
706 uri = cat(prefix, uri); | 706 uri = cat(prefix, uri); |
707 uri = flattenLocal(uri); | 707 uri = flattenLocal(uri); |
708 var xhr = getXHR(); | 708 var xhr = getXHR(); |
709 | 709 |
710 xhr.onreadystatechange = function() { | 710 xhr.onreadystatechange = function() { |
711 if (xhr.readyState == 4) { | 711 if (xhr.readyState == 4) { |
712 var isok = false; | 712 var isok = false; |
713 | 713 |
714 try { | 714 try { |
715 if (xhr.status == 200) | 715 if (xhr.status == 200) |
716 isok = true; | 716 isok = true; |
717 } catch (e) { } | 717 } catch (e) { } |
718 | 718 |
719 if (isok) { | 719 if (isok) { |
720 try { | |
721 k(parse(xhr.responseText)); | |
722 } catch (v) { | |
723 doExn(v); | |
724 } | |
725 } else { | |
726 conn(); | |
727 } | |
728 | |
729 xhrFinished(xhr); | |
730 } | |
731 }; | |
732 | |
733 requestUri(xhr, uri, needsSig); | |
734 } | |
735 | |
736 function path_join(s1, s2) { | |
737 if (s1.length > 0 && s1.charAt(s1.length-1) == '/') | |
738 return s1 + s2; | |
739 else | |
740 return s1 + "/" + s2; | |
741 } | |
742 | |
743 var channels = []; | |
744 | |
745 function newQueue() { | |
746 return { front : null, back : null }; | |
747 } | |
748 function enqueue(q, v) { | |
749 if (q.front == null) { | |
750 q.front = cons(v, null); | |
751 q.back = q.front; | |
752 } else { | |
753 var node = cons(v, null); | |
754 q.back.next = node; | |
755 q.back = node; | |
756 } | |
757 } | |
758 function dequeue(q) { | |
759 if (q.front == null) | |
760 return null; | |
761 else { | |
762 var r = q.front.data; | |
763 q.front = q.front.next; | |
764 if (q.front == null) | |
765 q.back = null; | |
766 return r; | |
767 } | |
768 } | |
769 | |
770 function newChannel() { | |
771 return { msgs : newQueue(), listeners : newQueue() }; | |
772 } | |
773 | |
774 function listener() { | |
775 var uri = path_join(url_prefix, ".msgs"); | |
776 var xhr = getXHR(); | |
777 var tid, orsc, onTimeout; | |
778 | |
779 var connect = function () { | |
780 xhr.onreadystatechange = orsc; | |
781 tid = window.setTimeout(onTimeout, timeout * 500); | |
782 requestUri(xhr, uri, false); | |
783 } | |
784 | |
785 orsc = function() { | |
786 if (xhr.readyState == 4) { | |
787 window.clearTimeout(tid); | |
788 | |
789 var isok = false; | |
790 | |
791 try { | |
792 if (xhr.status == 200) | |
793 isok = true; | |
794 } catch (e) { } | |
795 | |
796 if (isok) { | |
797 var text = xhr.responseText | |
798 if (text == "") | |
799 return; | |
800 var lines = text.split("\n"); | |
801 | |
802 if (lines.length < 2) { | |
803 discon(); | |
804 return; | |
805 } | |
806 | |
807 for (var i = 0; i+1 < lines.length; i += 2) { | |
808 var chn = lines[i]; | |
809 var msg = lines[i+1]; | |
810 | |
811 if (chn < 0) | |
812 whine("Out-of-bounds channel in message from remote server"); | |
813 | |
814 var ch; | |
815 | |
816 if (chn >= channels.length || channels[chn] == null) { | |
817 ch = newChannel(); | |
818 channels[chn] = ch; | |
819 } else | |
820 ch = channels[chn]; | |
821 | |
822 var listener = dequeue(ch.listeners); | |
823 if (listener == null) { | |
824 enqueue(ch.msgs, msg); | |
825 } else { | |
826 try { | |
827 listener(msg); | |
828 } catch (v) { | |
829 doExn(v); | |
830 } | |
831 } | |
832 } | |
833 | |
834 xhrFinished(xhr); | |
835 | |
836 connect(); | |
837 } | |
838 else { | |
839 try { | |
840 if (xhr.status != 0) | |
841 servErr("Error querying remote server for messages: " + xhr.status); | |
842 } catch (e) { } | |
843 } | |
844 } | |
845 }; | |
846 | |
847 onTimeout = function() { | |
848 xhrFinished(xhr); | |
849 connect(); | |
850 }; | |
851 | |
852 connect(); | |
853 } | |
854 | |
855 function rv(chn, parse, k) { | |
856 if (chn == null) | |
857 return; | |
858 | |
859 if (chn < 0) | |
860 whine("Out-of-bounds channel receive"); | |
861 | |
862 var ch; | |
863 | |
864 if (chn >= channels.length || channels[chn] == null) { | |
865 ch = newChannel(); | |
866 channels[chn] = ch; | |
867 } else | |
868 ch = channels[chn]; | |
869 | |
870 var msg = dequeue(ch.msgs); | |
871 if (msg == null) { | |
872 enqueue(ch.listeners, function(msg) { k(parse(msg)); }); | |
873 } else { | |
720 try { | 874 try { |
721 k(parse(xhr.responseText)); | 875 k(parse(msg)); |
722 } catch (v) { | 876 } catch (v) { |
723 doExn(v); | 877 doExn(v); |
724 } | 878 } |
725 } else { | 879 } |
726 conn(); | 880 } |
727 } | 881 |
728 | 882 function sl(ms, k) { |
729 xhrFinished(xhr); | 883 window.setTimeout(function() { k(null); }, ms); |
730 } | 884 } |
731 }; | 885 |
732 | 886 function sp(e) { |
733 requestUri(xhr, uri, needsSig); | 887 execF(e, null); |
734 } | 888 } |
735 | 889 |
736 function path_join(s1, s2) { | 890 |
737 if (s1.length > 0 && s1.charAt(s1.length-1) == '/') | 891 // Key events |
738 return s1 + s2; | 892 |
739 else | 893 var uw_event = null; |
740 return s1 + "/" + s2; | 894 |
741 } | 895 function kc() { |
742 | 896 return window.event ? uw_event.keyCode : uw_event.which; |
743 var channels = []; | 897 } |
744 | 898 |
745 function newQueue() { | 899 |
746 return { front : null, back : null }; | 900 // The Ur interpreter |
747 } | 901 |
748 function enqueue(q, v) { | 902 var urfuncs = []; |
749 if (q.front == null) { | 903 |
750 q.front = cons(v, null); | 904 function lookup(env, n) { |
751 q.back = q.front; | 905 while (env != null) { |
752 } else { | 906 if (n == 0) |
753 var node = cons(v, null); | 907 return env.data; |
754 q.back.next = node; | 908 else { |
755 q.back = node; | 909 --n; |
756 } | 910 env = env.next; |
757 } | |
758 function dequeue(q) { | |
759 if (q.front == null) | |
760 return null; | |
761 else { | |
762 var r = q.front.data; | |
763 q.front = q.front.next; | |
764 if (q.front == null) | |
765 q.back = null; | |
766 return r; | |
767 } | |
768 } | |
769 | |
770 function newChannel() { | |
771 return { msgs : newQueue(), listeners : newQueue() }; | |
772 } | |
773 | |
774 function listener() { | |
775 var uri = path_join(url_prefix, ".msgs"); | |
776 var xhr = getXHR(); | |
777 var tid, orsc, onTimeout; | |
778 | |
779 var connect = function () { | |
780 xhr.onreadystatechange = orsc; | |
781 tid = window.setTimeout(onTimeout, timeout * 500); | |
782 requestUri(xhr, uri, false); | |
783 } | |
784 | |
785 orsc = function() { | |
786 if (xhr.readyState == 4) { | |
787 window.clearTimeout(tid); | |
788 | |
789 var isok = false; | |
790 | |
791 try { | |
792 if (xhr.status == 200) | |
793 isok = true; | |
794 } catch (e) { } | |
795 | |
796 if (isok) { | |
797 var text = xhr.responseText | |
798 if (text == "") | |
799 return; | |
800 var lines = text.split("\n"); | |
801 | |
802 if (lines.length < 2) { | |
803 discon(); | |
804 return; | |
805 } | 911 } |
806 | 912 } |
807 for (var i = 0; i+1 < lines.length; i += 2) { | 913 |
808 var chn = lines[i]; | 914 whine("Out-of-bounds Ur variable reference"); |
809 var msg = lines[i+1]; | 915 } |
810 | 916 |
811 if (chn < 0) | 917 function execP(env, p, v) { |
812 whine("Out-of-bounds channel in message from remote server"); | 918 switch (p.c) { |
813 | 919 case "w": |
814 var ch; | 920 return env; |
815 | 921 case "v": |
816 if (chn >= channels.length || channels[chn] == null) { | 922 return cons(v, env); |
817 ch = newChannel(); | 923 case "c": |
818 channels[chn] = ch; | 924 if (v == p.v) |
819 } else | 925 return env; |
820 ch = channels[chn]; | 926 else |
821 | 927 return false; |
822 var listener = dequeue(ch.listeners); | 928 case "s": |
823 if (listener == null) { | 929 if (v == null) |
824 enqueue(ch.msgs, msg); | 930 return false; |
825 } else { | 931 else |
826 try { | 932 return execP(env, p.p, p.n ? v.v : v); |
827 listener(msg); | 933 case "1": |
828 } catch (v) { | 934 if (v.n != p.n) |
829 doExn(v); | 935 return false; |
936 else | |
937 return execP(env, p.p, v.v); | |
938 case "r": | |
939 for (var fs = p.l; fs != null; fs = fs.next) { | |
940 env = execP(env, fs.data.p, v["_" + fs.data.n]); | |
941 if (env == false) | |
942 return false; | |
943 } | |
944 return env; | |
945 default: | |
946 whine("Unknown Ur pattern kind" + p.c); | |
947 } | |
948 } | |
949 | |
950 function exec0(env, e) { | |
951 return exec1(env, null, e); | |
952 } | |
953 | |
954 function exec1(env, stack, e) { | |
955 var stack, usedK = false; | |
956 | |
957 var saveEnv = function() { | |
958 if (stack.next != null && stack.next.data.c != "<") | |
959 stack = cons({c: "<", env: env}, stack.next); | |
960 else | |
961 stack = stack.next; | |
962 }; | |
963 | |
964 while (true) { | |
965 switch (e.c) { | |
966 case "c": | |
967 var v = e.v; | |
968 if (stack == null) | |
969 return v; | |
970 var fr = stack.data; | |
971 | |
972 switch (fr.c) { | |
973 case "s": | |
974 e = {c: "c", v: {v: v}}; | |
975 stack = stack.next; | |
976 break; | |
977 case "1": | |
978 e = {c: "c", v: {n: fr.n, v: v}}; | |
979 stack = stack.next; | |
980 break; | |
981 case "f": | |
982 fr.args[fr.pos++] = v; | |
983 if (fr.a == null) { | |
984 var res; | |
985 stack = stack.next; | |
986 | |
987 if (fr.f.apply) | |
988 res = fr.f.apply(null, fr.args); | |
989 else if (fr.args.length == 0) | |
990 res = fr.f(); | |
991 else if (fr.args.length == 1) | |
992 res = fr.f(fr.args[0]); | |
993 else if (fr.args.length == 2) | |
994 res = fr.f(fr.args[0], fr.args[1]); | |
995 else if (fr.args.length == 3) | |
996 res = fr.f(fr.args[0], fr.args[1], fr.args[2]); | |
997 else if (fr.args.length == 4) | |
998 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3]); | |
999 else if (fr.args.length == 5) | |
1000 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3], fr.args[4]); | |
1001 else | |
1002 whine("Native function has " + fr.args.length + " args, but there is no special case for that count."); | |
1003 | |
1004 e = {c: "c", v: res}; | |
1005 if (usedK) return null; | |
1006 } else { | |
1007 e = fr.a.data; | |
1008 fr.a = fr.a.next; | |
1009 } | |
1010 break; | |
1011 case "a1": | |
1012 e = fr.x; | |
1013 stack = cons({c: "a2", f: v}, stack.next); | |
1014 break; | |
1015 case "a2": | |
1016 if (fr.f == null) | |
1017 whine("Ur: applying null function"); | |
1018 else if (fr.f.body) { | |
1019 saveEnv(); | |
1020 env = cons(v, fr.f.env); | |
1021 e = fr.f.body; | |
1022 } else { | |
1023 e = {c: "c", v: fr.f(v)}; | |
1024 stack = stack.next; | |
1025 } | |
1026 break; | |
1027 case "<": | |
1028 env = fr.env; | |
1029 stack = stack.next; | |
1030 break; | |
1031 case "r": | |
1032 fr.fs["_" + fr.n] = v; | |
1033 if (fr.l == null) { | |
1034 e = {c: "c", v: fr.fs}; | |
1035 stack = stack.next; | |
1036 } else { | |
1037 fr.n = fr.l.data.n; | |
1038 e = fr.l.data.v; | |
1039 fr.l = fr.l.next; | |
1040 } | |
1041 break; | |
1042 case ".": | |
1043 e = {c: "c", v: v["_" + fr.f]}; | |
1044 stack = stack.next; | |
1045 break; | |
1046 case ";": | |
1047 e = fr.e2; | |
1048 stack = stack.next; | |
1049 break; | |
1050 case "=": | |
1051 saveEnv(); | |
1052 env = cons(v, env); | |
1053 e = fr.e2; | |
1054 break; | |
1055 case "m": | |
1056 var ps; | |
1057 for (ps = fr.p; ps != null; ps = ps.next) { | |
1058 var r = execP(env, ps.data.p, v); | |
1059 if (r != false) { | |
1060 saveEnv(); | |
1061 env = r; | |
1062 e = ps.data.b; | |
1063 break; | |
1064 } | |
1065 } | |
1066 if (ps == null) | |
1067 whine("Match failure in Ur interpretation"); | |
1068 break; | |
1069 default: | |
1070 whine("Unknown Ur continuation kind " + fr.c); | |
830 } | 1071 } |
831 } | 1072 |
1073 break; | |
1074 case "v": | |
1075 e = {c: "c", v: lookup(env, e.n)}; | |
1076 break; | |
1077 case "n": | |
1078 var idx = e.n; | |
1079 e = urfuncs[idx]; | |
1080 if (e.c == "t") | |
1081 e = urfuncs[idx] = e.f(); | |
1082 break; | |
1083 case "s": | |
1084 stack = cons({c: "s"}, stack); | |
1085 e = e.v; | |
1086 break; | |
1087 case "1": | |
1088 stack = cons({c: "1", n: e.n}, stack); | |
1089 e = e.v; | |
1090 break; | |
1091 case "f": | |
1092 if (e.a == null) | |
1093 e = {c: "c", v: (eval(e.f))()}; | |
1094 else { | |
1095 var args = []; | |
1096 stack = cons({c: "f", f: eval(e.f), args: args, pos: 0, a: e.a.next}, stack); | |
1097 if (!e.a.data.c) alert("[2] fr.f = " + e.f + "; 0 = " + e.a.data); | |
1098 e = e.a.data; | |
1099 } | |
1100 break; | |
1101 case "l": | |
1102 e = {c: "c", v: {env: env, body: e.b}}; | |
1103 break; | |
1104 case "a": | |
1105 stack = cons({c: "a1", x: e.x}, stack); | |
1106 e = e.f; | |
1107 break; | |
1108 case "r": | |
1109 if (e.l == null) | |
1110 whine("Empty Ur record in interpretation"); | |
1111 var fs = {}; | |
1112 stack = cons({c: "r", n: e.l.data.n, fs: fs, l: e.l.next}, stack); | |
1113 e = e.l.data.v; | |
1114 break; | |
1115 case ".": | |
1116 stack = cons({c: ".", f: e.f}, stack); | |
1117 e = e.r; | |
1118 break; | |
1119 case ";": | |
1120 stack = cons({c: ";", e2: e.e2}, stack); | |
1121 e = e.e1; | |
1122 break; | |
1123 case "=": | |
1124 stack = cons({c: "=", e2: e.e2}, stack); | |
1125 e = e.e1; | |
1126 break; | |
1127 case "m": | |
1128 stack = cons({c: "m", p: e.p}, stack); | |
1129 e = e.e; | |
1130 break; | |
1131 case "e": | |
1132 e = {c: "c", v: cs({c: "wc", env: env, body: e.e})}; | |
1133 break; | |
1134 case "wc": | |
1135 env = e.env; | |
1136 e = e.body; | |
1137 break; | |
1138 case "K": | |
1139 { var savedStack = stack.next, savedEnv = env; | |
1140 e = {c: "c", v: function(v) { return exec1(savedEnv, savedStack, {c: "c", v: v}); } };} | |
1141 usedK = true; | |
1142 break; | |
1143 default: | |
1144 whine("Unknown Ur expression kind " + e.c); | |
832 } | 1145 } |
833 | 1146 } |
834 xhrFinished(xhr); | 1147 } |
835 | 1148 |
836 connect(); | 1149 function execD(e) { |
837 } | 1150 return exec0(null, e); |
838 else { | 1151 } |
839 try { | 1152 |
840 if (xhr.status != 0) | 1153 function exec(e) { |
841 servErr("Error querying remote server for messages: " + xhr.status); | 1154 var r = exec0(null, e); |
842 } catch (e) { } | 1155 |
843 } | 1156 if (r != null && r.body != null) |
844 } | 1157 return function(v) { return exec0(cons(v, r.env), r.body); }; |
845 }; | |
846 | |
847 onTimeout = function() { | |
848 xhrFinished(xhr); | |
849 connect(); | |
850 }; | |
851 | |
852 connect(); | |
853 } | |
854 | |
855 function rv(chn, parse, k) { | |
856 if (chn == null) | |
857 return; | |
858 | |
859 if (chn < 0) | |
860 whine("Out-of-bounds channel receive"); | |
861 | |
862 var ch; | |
863 | |
864 if (chn >= channels.length || channels[chn] == null) { | |
865 ch = newChannel(); | |
866 channels[chn] = ch; | |
867 } else | |
868 ch = channels[chn]; | |
869 | |
870 var msg = dequeue(ch.msgs); | |
871 if (msg == null) { | |
872 enqueue(ch.listeners, function(msg) { k(parse(msg)); }); | |
873 } else { | |
874 try { | |
875 k(parse(msg)); | |
876 } catch (v) { | |
877 doExn(v); | |
878 } | |
879 } | |
880 } | |
881 | |
882 function sl(ms, k) { | |
883 window.setTimeout(function() { k(null); }, ms); | |
884 } | |
885 | |
886 function sp(e) { | |
887 execF(e, null); | |
888 } | |
889 | |
890 | |
891 // Key events | |
892 | |
893 var uw_event = null; | |
894 | |
895 function kc() { | |
896 return window.event ? uw_event.keyCode : uw_event.which; | |
897 } | |
898 | |
899 | |
900 // The Ur interpreter | |
901 | |
902 var urfuncs = []; | |
903 | |
904 function lookup(env, n) { | |
905 while (env != null) { | |
906 if (n == 0) | |
907 return env.data; | |
908 else { | |
909 --n; | |
910 env = env.next; | |
911 } | |
912 } | |
913 | |
914 whine("Out-of-bounds Ur variable reference"); | |
915 } | |
916 | |
917 function execP(env, p, v) { | |
918 switch (p.c) { | |
919 case "w": | |
920 return env; | |
921 case "v": | |
922 return cons(v, env); | |
923 case "c": | |
924 if (v == p.v) | |
925 return env; | |
926 else | 1158 else |
927 return false; | 1159 return r; |
928 case "s": | |
929 if (v == null) | |
930 return false; | |
931 else | |
932 return execP(env, p.p, p.n ? v.v : v); | |
933 case "1": | |
934 if (v.n != p.n) | |
935 return false; | |
936 else | |
937 return execP(env, p.p, v.v); | |
938 case "r": | |
939 for (var fs = p.l; fs != null; fs = fs.next) { | |
940 env = execP(env, fs.data.p, v["_" + fs.data.n]); | |
941 if (env == false) | |
942 return false; | |
943 } | |
944 return env; | |
945 default: | |
946 whine("Unknown Ur pattern kind" + p.c); | |
947 } | |
948 } | |
949 | |
950 function exec0(env, e) { | |
951 return exec1(env, null, e); | |
952 } | |
953 | |
954 function exec1(env, stack, e) { | |
955 var stack, usedK = false; | |
956 | |
957 var saveEnv = function() { | |
958 if (stack.next != null && stack.next.data.c != "<") | |
959 stack = cons({c: "<", env: env}, stack.next); | |
960 else | |
961 stack = stack.next; | |
962 }; | |
963 | |
964 while (true) { | |
965 switch (e.c) { | |
966 case "c": | |
967 var v = e.v; | |
968 if (stack == null) | |
969 return v; | |
970 var fr = stack.data; | |
971 | |
972 switch (fr.c) { | |
973 case "s": | |
974 e = {c: "c", v: {v: v}}; | |
975 stack = stack.next; | |
976 break; | |
977 case "1": | |
978 e = {c: "c", v: {n: fr.n, v: v}}; | |
979 stack = stack.next; | |
980 break; | |
981 case "f": | |
982 fr.args[fr.pos++] = v; | |
983 if (fr.a == null) { | |
984 var res; | |
985 stack = stack.next; | |
986 | |
987 if (fr.f.apply) | |
988 res = fr.f.apply(null, fr.args); | |
989 else if (fr.args.length == 0) | |
990 res = fr.f(); | |
991 else if (fr.args.length == 1) | |
992 res = fr.f(fr.args[0]); | |
993 else if (fr.args.length == 2) | |
994 res = fr.f(fr.args[0], fr.args[1]); | |
995 else if (fr.args.length == 3) | |
996 res = fr.f(fr.args[0], fr.args[1], fr.args[2]); | |
997 else if (fr.args.length == 4) | |
998 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3]); | |
999 else if (fr.args.length == 5) | |
1000 res = fr.f(fr.args[0], fr.args[1], fr.args[2], fr.args[3], fr.args[4]); | |
1001 else | |
1002 whine("Native function has " + fr.args.length + " args, but there is no special case for that count."); | |
1003 | |
1004 e = {c: "c", v: res}; | |
1005 if (usedK) return null; | |
1006 } else { | |
1007 e = fr.a.data; | |
1008 fr.a = fr.a.next; | |
1009 } | |
1010 break; | |
1011 case "a1": | |
1012 e = fr.x; | |
1013 stack = cons({c: "a2", f: v}, stack.next); | |
1014 break; | |
1015 case "a2": | |
1016 if (fr.f == null) | |
1017 whine("Ur: applying null function"); | |
1018 else if (fr.f.body) { | |
1019 saveEnv(); | |
1020 env = cons(v, fr.f.env); | |
1021 e = fr.f.body; | |
1022 } else { | |
1023 e = {c: "c", v: fr.f(v)}; | |
1024 stack = stack.next; | |
1025 } | |
1026 break; | |
1027 case "<": | |
1028 env = fr.env; | |
1029 stack = stack.next; | |
1030 break; | |
1031 case "r": | |
1032 fr.fs["_" + fr.n] = v; | |
1033 if (fr.l == null) { | |
1034 e = {c: "c", v: fr.fs}; | |
1035 stack = stack.next; | |
1036 } else { | |
1037 fr.n = fr.l.data.n; | |
1038 e = fr.l.data.v; | |
1039 fr.l = fr.l.next; | |
1040 } | |
1041 break; | |
1042 case ".": | |
1043 e = {c: "c", v: v["_" + fr.f]}; | |
1044 stack = stack.next; | |
1045 break; | |
1046 case ";": | |
1047 e = fr.e2; | |
1048 stack = stack.next; | |
1049 break; | |
1050 case "=": | |
1051 saveEnv(); | |
1052 env = cons(v, env); | |
1053 e = fr.e2; | |
1054 break; | |
1055 case "m": | |
1056 var ps; | |
1057 for (ps = fr.p; ps != null; ps = ps.next) { | |
1058 var r = execP(env, ps.data.p, v); | |
1059 if (r != false) { | |
1060 saveEnv(); | |
1061 env = r; | |
1062 e = ps.data.b; | |
1063 break; | |
1064 } | |
1065 } | |
1066 if (ps == null) | |
1067 whine("Match failure in Ur interpretation"); | |
1068 break; | |
1069 default: | |
1070 whine("Unknown Ur continuation kind " + fr.c); | |
1071 } | |
1072 | |
1073 break; | |
1074 case "v": | |
1075 e = {c: "c", v: lookup(env, e.n)}; | |
1076 break; | |
1077 case "n": | |
1078 e = urfuncs[e.n]; | |
1079 break; | |
1080 case "s": | |
1081 stack = cons({c: "s"}, stack); | |
1082 e = e.v; | |
1083 break; | |
1084 case "1": | |
1085 stack = cons({c: "1", n: e.n}, stack); | |
1086 e = e.v; | |
1087 break; | |
1088 case "f": | |
1089 if (e.a == null) | |
1090 e = {c: "c", v: (eval(e.f))()}; | |
1091 else { | |
1092 var args = []; | |
1093 stack = cons({c: "f", f: eval(e.f), args: args, pos: 0, a: e.a.next}, stack); | |
1094 if (!e.a.data.c) alert("[2] fr.f = " + e.f + "; 0 = " + e.a.data); | |
1095 e = e.a.data; | |
1096 } | |
1097 break; | |
1098 case "l": | |
1099 e = {c: "c", v: {env: env, body: e.b}}; | |
1100 break; | |
1101 case "a": | |
1102 stack = cons({c: "a1", x: e.x}, stack); | |
1103 e = e.f; | |
1104 break; | |
1105 case "r": | |
1106 if (e.l == null) | |
1107 whine("Empty Ur record in interpretation"); | |
1108 var fs = {}; | |
1109 stack = cons({c: "r", n: e.l.data.n, fs: fs, l: e.l.next}, stack); | |
1110 e = e.l.data.v; | |
1111 break; | |
1112 case ".": | |
1113 stack = cons({c: ".", f: e.f}, stack); | |
1114 e = e.r; | |
1115 break; | |
1116 case ";": | |
1117 stack = cons({c: ";", e2: e.e2}, stack); | |
1118 e = e.e1; | |
1119 break; | |
1120 case "=": | |
1121 stack = cons({c: "=", e2: e.e2}, stack); | |
1122 e = e.e1; | |
1123 break; | |
1124 case "m": | |
1125 stack = cons({c: "m", p: e.p}, stack); | |
1126 e = e.e; | |
1127 break; | |
1128 case "e": | |
1129 e = {c: "c", v: cs({c: "wc", env: env, body: e.e})}; | |
1130 break; | |
1131 case "wc": | |
1132 env = e.env; | |
1133 e = e.body; | |
1134 break; | |
1135 case "K": | |
1136 { var savedStack = stack.next, savedEnv = env; | |
1137 e = {c: "c", v: function(v) { return exec1(savedEnv, savedStack, {c: "c", v: v}); } };} | |
1138 usedK = true; | |
1139 break; | |
1140 default: | |
1141 whine("Unknown Ur expression kind " + e.c); | |
1142 } | |
1143 } | |
1144 } | |
1145 | |
1146 function execD(e) { | |
1147 return exec0(null, e); | |
1148 } | |
1149 | |
1150 function exec(e) { | |
1151 var r = exec0(null, e); | |
1152 | |
1153 if (r != null && r.body != null) | |
1154 return function(v) { return exec0(cons(v, r.env), r.body); }; | |
1155 else | |
1156 return r; | |
1157 } | 1160 } |
1158 | 1161 |
1159 function execF(f, x) { | 1162 function execF(f, x) { |
1160 return exec0(cons(x, f.env), f.body); | 1163 return exec0(cons(x, f.env), f.body); |
1161 } | 1164 } |
1162 | 1165 |
1163 | 1166 |
1164 // App-specific code | 1167 // App-specific code |
1165 | 1168 |