substrate-lang 0.10.0 → 0.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/build/compiler/{checker.4dd4556dd421@0+0.wasm → checker.1d67e1d4e91a@0+0.wasm} +0 -0
  2. package/build/compiler/{expander.967ce443d379@0+0.wasm → expander.34110a31a949@0+0.wasm} +0 -0
  3. package/build/compiler/{main.4fc52f204d54@0+0.wasm → main.ca9b5089c24d@0+0.wasm} +0 -0
  4. package/build/compiler/program +4 -4
  5. package/build/compiler/{roots.cefd10e93684@0+0.wasm → roots.2f1ad1a79459@0+0.wasm} +0 -0
  6. package/build/sub/{build.459c5b9412bd@0+0.wasm → build.eef7dfa467ec@0+0.wasm} +0 -0
  7. package/build/sub/{common.1edc613607a0@0+0.wasm → common.7617940db2dc@0+0.wasm} +0 -0
  8. package/build/sub/deflate.23f7c4679260@0+0.wasm +0 -0
  9. package/build/sub/{doc.0775df5de6fe@0+0.wasm → doc.92d6d82d99c1@0+0.wasm} +0 -0
  10. package/build/sub/{doc.ef9965d5d402@0+0.wasm → doc.dab689b811ca@0+0.wasm} +0 -0
  11. package/build/sub/{fetch.96b2d10f025e@0+0.wasm → fetch.3367ff4f88c0@0+0.wasm} +0 -0
  12. package/build/sub/{files.ad0469fdf6fa@0+0.wasm → files.5ae9d3cebeca@0+0.wasm} +0 -0
  13. package/build/sub/gzip.16e372eda1cf@0+0.wasm +0 -0
  14. package/build/sub/{hash.660682e4f491@0+0.wasm → hash.c57c141b4862@0+0.wasm} +0 -0
  15. package/build/sub/{html.a23126eb949a@0+0.wasm → html.803f73c35c4a@0+0.wasm} +0 -0
  16. package/build/sub/{manifest.ef317480deb0@0+0.wasm → manifest.da52a88c7af7@0+0.wasm} +0 -0
  17. package/build/sub/{map.f455cca6a42a@0+0.wasm → map.5c8084999550@0+0.wasm} +0 -0
  18. package/build/sub/program +22 -22
  19. package/build/sub/{root.f10a4906f460@0+0.wasm → root.78253db36643@0+0.wasm} +0 -0
  20. package/build/sub/{roots.cefd10e93684@0+0.wasm → roots.2f1ad1a79459@0+0.wasm} +0 -0
  21. package/build/sub/sha256.a6c32b04d799@0+0.wasm +0 -0
  22. package/build/sub/site.47fd989be8fb@0+0.wasm +0 -0
  23. package/build/sub/site.c908b1f6db12@0+0.wasm +0 -0
  24. package/build/sub/{store.a24d7a25daa6@0+0.wasm → store.048c77bb124d@0+0.wasm} +0 -0
  25. package/build/sub/sub.a618f89f2ec8@0+0.wasm +0 -0
  26. package/build/sub/{test.9bd59c73f65e@0+0.wasm → test.b529bf52ba3e@0+0.wasm} +0 -0
  27. package/build/sub/{text.1bc90482bd43@0+0.wasm → text.b214933a4984@0+0.wasm} +0 -0
  28. package/build/sub/{upgrade.39ee449b00d5@0+0.wasm → upgrade.7ec5651ed2e9@0+0.wasm} +0 -0
  29. package/package.json +1 -1
  30. package/std/http/server.sub +7 -7
  31. package/std/http/static.sub +66 -30
  32. package/std/package/manifest.sub +9 -4
  33. package/std/package/site.sub +209 -15
  34. package/std/package.sub +4 -3
  35. package/std/test/browser.sub +20 -14
  36. package/std/test/build.sub +8 -6
  37. package/std/test/page/agent.mjs +8 -2
  38. package/std/web/canvas.sub +4 -4
  39. package/std/web/dom.sub +45 -11
  40. package/std/web/gpu-commands.sub +439 -0
  41. package/std/web/gpu.sub +17 -424
  42. package/std/web/patches.sub +53 -18
  43. package/substrate/VERSION +1 -1
  44. package/substrate/compiler/expander.sub +2 -3
  45. package/substrate/compiler/roots.sub +12 -5
  46. package/substrate/host/grants.mjs +3 -3
  47. package/substrate/host/node/net.mjs +17 -2
  48. package/substrate/host/node/worker.mjs +3 -1
  49. package/substrate/host/subhost.mjs +4 -1
  50. package/substrate/host/web/audio.mjs +18 -10
  51. package/substrate/host/web/audio.sub +9 -3
  52. package/substrate/host/web/dom.mjs +167 -66
  53. package/substrate/host/web/dom.sub +9 -27
  54. package/substrate/host/web/fs.mjs +3 -3
  55. package/substrate/host/web/gpu.mjs +65 -27
  56. package/substrate/host/web/gpu.sub +18 -4
  57. package/substrate/host/web/location.mjs +15 -1
  58. package/substrate/host/web/location.sub +11 -3
  59. package/substrate/host/web/start.mjs +11 -7
  60. package/substrate/host/web/subhost-web.mjs +54 -33
  61. package/substrate/host/web/worker-script.mjs +4 -4
  62. package/substrate/host/web/worker.mjs +4 -4
  63. package/substrate/host/worker.mjs +1 -1
  64. package/build/sub/site.1732b58ec0e8@0+0.wasm +0 -0
  65. package/build/sub/site.ae3913b0b0f4@0+0.wasm +0 -0
  66. package/build/sub/sub.2e3092140706@0+0.wasm +0 -0
@@ -42,7 +42,10 @@ try {
42
42
  if (request.inherit.size > 0) fail('inherit is only for workers');
43
43
  setup.grants = joinedGrants(setup.grants, request.grants);
44
44
  for (const [name, at] of request.fakes) setup.fakes.set(name, at);
45
- if (request.tls) setup.tls = { cert: read(request.tls.cert), key: read(request.tls.key) };
45
+ if (request.tls) {
46
+ const files = [request.tls.cert, request.tls.key];
47
+ setup.tls = { cert: read(files[0]), key: read(files[1]), files };
48
+ }
46
49
  } catch (e) {
47
50
  fail(e.message);
48
51
  }
@@ -14,7 +14,13 @@ export function audio() {
14
14
  let started = 0;
15
15
 
16
16
  const sounds = new Map();
17
- let loading = 0;
17
+ let nextSound = 1;
18
+ const loading = new Map();
19
+ const counted = (tag, n) => {
20
+ const k = (loading.get(tag) || 0) + n;
21
+ if (k === 0) loading.delete(tag);
22
+ else loading.set(tag, k);
23
+ };
18
24
  let decoder = null;
19
25
 
20
26
  const voices = new Map();
@@ -60,35 +66,37 @@ export function audio() {
60
66
 
61
67
  const module = (caller, app) => ({
62
68
  source: SOURCE,
63
- load(id, ptr, length) {
69
+ load(ptr, length, tag) {
70
+ const id = nextSound++;
64
71
  const name = caller.string(ptr, length);
65
72
  const bytes = app.files.get(resolvePath(app.cwd, name));
66
73
  if (!decoder) decoder = new OfflineAudioContext(1, 1, 44100);
67
- loading++;
74
+ counted(tag, 1);
68
75
  const data = bytes
69
76
  ? Promise.resolve(bytes.slice().buffer)
70
- : fetch(name).then((response) => {
77
+ : fetch(app.url(name)).then((response) => {
71
78
  if (!response.ok) throw new Error(`the server answered ${response.status}`);
72
79
  return response.arrayBuffer();
73
80
  });
74
81
  data.then((buffer) => decoder.decodeAudioData(buffer)).then(
75
82
  (decoded) => {
76
83
  sounds.set(id, decoded);
77
- loading--;
84
+ counted(tag, -1);
78
85
  startWaiting();
79
- caller.event(LOADED, id, 0, 0);
86
+ caller.event(LOADED, id, tag, 0);
80
87
  },
81
88
  (e) => {
82
89
  console.error(`audio: cannot load the sound ${name}: ${e.message}`);
83
- loading--;
90
+ counted(tag, -1);
84
91
  startWaiting();
85
- caller.event(FAILED, id, 0, e.name === 'EncodingError' ? errors.invalid : errors.missing,
92
+ caller.event(FAILED, id, tag, e.name === 'EncodingError' ? errors.invalid : errors.missing,
86
93
  utf8.encode(`cannot load the sound ${name}: ${e.message}`));
87
94
  },
88
95
  );
96
+ return id;
89
97
  },
90
- loading() {
91
- return loading;
98
+ loading(tag) {
99
+ return loading.get(tag) || 0;
92
100
  },
93
101
  play(id, volume, rate, loop) {
94
102
  played++;
@@ -1,4 +1,5 @@
1
- ;; Sound: the host module "audio".
1
+ ;; Sound: the host module "audio". audio-load gives the sound's number, and its loaded or failed event goes to tag.
2
+ ;; audio-loading counts the sounds loading for a tag.
2
3
  (provide audio-source audio-loaded audio-failed audio-tone audio-load audio-loading audio-play audio-stop)
3
4
 
4
5
  (const audio-source u32 4)
@@ -6,7 +7,12 @@
6
7
  (const audio-failed u32 2)
7
8
 
8
9
  (import audio-tone "audio" "tone" (i32 f64 f64 f64 f64 ->))
9
- (import audio-load "audio" "load" (i32 i32 i32 ->))
10
- (import audio-loading "audio" "loading" (-> i32))
10
+ (import load "audio" "load" (i32 i32 i32 -> i32))
11
+ (import loading "audio" "loading" (i32 -> i32))
11
12
  (import audio-play "audio" "play" (i32 f64 f64 i32 -> i32))
12
13
  (import audio-stop "audio" "stop" (i32 ->))
14
+
15
+ (define (audio-load [path string] [tag (ptr void)]) -> u32
16
+ (cast u32 (load (cast i32 (-> path bytes)) (cast i32 (-> path length)) (cast i32 tag))))
17
+
18
+ (define (audio-loading [tag (ptr void)]) -> u32 (cast u32 (loading (cast i32 tag))))
@@ -1,4 +1,4 @@
1
- // The host module "dom": applies a program's patches to the page, and sends it the page's events.
1
+ // The host module "dom": applies a program's patches to the document, and sends it the document's events.
2
2
  import { resolvePath } from './fs.mjs';
3
3
  import { errors } from '../errors.mjs';
4
4
 
@@ -10,6 +10,9 @@ const HEARD = 1;
10
10
  const IMAGE_LOADED = 2;
11
11
  const IMAGE_FAILED = 3;
12
12
 
13
+ // The document's node number (std/web/patches.sub).
14
+ const DOCUMENT = 1;
15
+
13
16
  const imageTypes = {
14
17
  png: 'image/png', jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', webp: 'image/webp',
15
18
  avif: 'image/avif', bmp: 'image/bmp', svg: 'image/svg+xml',
@@ -17,9 +20,14 @@ const imageTypes = {
17
20
 
18
21
  export function dom(root) {
19
22
  const templates = new Map();
20
- const nodes = new Map([[0, root]]);
23
+ const nodes = new Map([[0, root], [DOCUMENT, root.ownerDocument]]);
21
24
  const images = new Map();
22
- let loading = 0;
25
+ const loading = new Map();
26
+ const counted = (tag, n) => {
27
+ const k = (loading.get(tag) || 0) + n;
28
+ if (k === 0) loading.delete(tag);
29
+ else loading.set(tag, k);
30
+ };
23
31
  let app = null;
24
32
  let caller = null;
25
33
 
@@ -30,6 +38,73 @@ export function dom(root) {
30
38
  return e && e.source === SOURCE ? e.data : null;
31
39
  };
32
40
 
41
+ const kinds = ['Node', 'Event', 'NodeList', 'HTMLCollection', 'DOMRectReadOnly', 'DOMTokenList',
42
+ 'CSSStyleDeclaration', 'DOMStringMap', 'ValidityState', 'FileList', 'Blob', 'DataTransfer', 'TouchList', 'Touch',
43
+ 'IntersectionObserverEntry'].map((name) => globalThis[name]).filter((kind) => kind !== undefined);
44
+ const reachable = (x) => kinds.some((kind) => x instanceof kind);
45
+
46
+ function guarded(path, f) {
47
+ try {
48
+ return f();
49
+ } catch (e) {
50
+ caller.trap(`dom: ${path}: ${e.message}`);
51
+ }
52
+ }
53
+
54
+ function step(x, name, path) {
55
+ if (!(name in Object(x))) caller.trap(`dom: ${path}: there is no ${name}`);
56
+ return guarded(path, () => x[name]);
57
+ }
58
+
59
+ function owner(root, path) {
60
+ const parts = path.split('.');
61
+ const last = parts.pop();
62
+ let x = root;
63
+ for (let k = 0; k < parts.length; k++) {
64
+ x = step(x, parts[k], path);
65
+ if (x === null || x === undefined) return [null, last];
66
+ if (!reachable(x)) {
67
+ caller.trap(`dom: ${path}: ${parts.slice(0, k + 1).join('.')} is not one of the document's objects`);
68
+ }
69
+ }
70
+ return [x, last];
71
+ }
72
+
73
+ function walk(root, path) {
74
+ const [x, last] = owner(root, path);
75
+ return x === null ? null : step(x, last, path);
76
+ }
77
+
78
+ function read(root, path) {
79
+ const v = walk(root, path);
80
+ const plain = v === null || v === undefined || ['number', 'string', 'boolean'].includes(typeof v);
81
+ if (!plain) caller.trap(`dom: ${path} is not a number, text, or true or false`);
82
+ return v;
83
+ }
84
+
85
+ function write(root, path, value) {
86
+ const [x, last] = owner(root, path);
87
+ if (x === null || !(last in x)) caller.trap(`dom: ${path}: there is no ${last} to set`);
88
+ guarded(path, () => { x[last] = value; });
89
+ }
90
+
91
+ function call(root, path, args) {
92
+ const [x, last] = owner(root, path);
93
+ if (x === null || typeof x[last] !== 'function') caller.trap(`dom: ${path} is not a method`);
94
+ const result = guarded(path, () => x[last](...args));
95
+ if (result && typeof result.catch === 'function') result.catch(() => {});
96
+ }
97
+
98
+ const numberOf = (v) => (v === null || v === undefined ? 0 : Number(v));
99
+ const textOf = (v) => (v === null || v === undefined ? '' : String(v));
100
+
101
+ const bits = new DataView(new ArrayBuffer(8));
102
+ const float64 = (low, high) => {
103
+ bits.setUint32(0, low, true);
104
+ bits.setUint32(4, high, true);
105
+ return bits.getFloat64(0, true);
106
+ };
107
+
33
108
  const listener = (e) => {
34
109
  const messages = e.currentTarget.subListeners;
35
110
  if (messages && e.type in messages) give(e, messages[e.type]);
@@ -66,12 +141,27 @@ export function dom(root) {
66
141
 
67
142
  const windowTarget = (type) => (type === 'pointerlockchange' || type === 'pointerlockerror' ? document : window);
68
143
 
144
+ const watched = new Map();
145
+ let observer = null;
146
+
147
+ function watch(node, message) {
148
+ observer ||= new IntersectionObserver((entries) => {
149
+ for (const entry of entries) if (watched.has(entry.target)) give(entry, watched.get(entry.target));
150
+ });
151
+ watched.set(node, message);
152
+ observer.observe(node);
153
+ }
154
+
69
155
  const unlisten = (id) => {
70
156
  const n = nodes.get(id);
71
157
  if (n instanceof Node && n.subListeners) {
72
158
  for (const message of Object.values(n.subListeners)) caller.forget(message);
73
159
  n.subListeners = null;
74
160
  }
161
+ if (watched.has(n)) {
162
+ observer.unobserve(n);
163
+ watched.delete(n);
164
+ }
75
165
  for (const { type, message, handler } of windowListeners.get(id) || []) {
76
166
  windowTarget(type).removeEventListener(type, handler);
77
167
  caller.forget(message);
@@ -260,68 +350,75 @@ export function dom(root) {
260
350
  const node = nodes.get(w[i + 1]);
261
351
  const type = string(i + 2);
262
352
  (node.subListeners ||= {})[type] = w[i + 4];
263
- node.addEventListener(type, listener);
353
+ if (type === 'visibility') watch(node, w[i + 4]);
354
+ else node.addEventListener(type, listener);
264
355
  i += 5;
265
356
  break;
266
357
  }
267
- case 10: {
268
- const node = nodes.get(w[i + 1]);
269
- after.push(() => node.focus());
270
- i += 2;
271
- break;
272
- }
273
- case 11: {
274
- const node = nodes.get(w[i + 1]);
275
- after.push(() => { node.scrollTop = node.scrollHeight; });
276
- i += 2;
277
- break;
278
- }
279
- case 12:
358
+ case 10:
280
359
  listenWindow(w[i + 1], string(i + 2), w[i + 4]);
281
360
  i += 5;
282
361
  break;
283
- case 15: {
284
- const node = nodes.get(w[i + 1]);
285
- if (w[i + 2] !== 0) {
286
- after.push(() => { const p = node.requestPointerLock(); if (p && p.catch) p.catch(() => {}); });
287
- } else {
288
- after.push(() => document.exitPointerLock());
289
- }
290
- i += 3;
362
+ case 11: {
363
+ const canvas = nodes.get(w[i + 1]);
364
+ const count = w[i + 2];
365
+ const f = new Float32Array(w.buffer, w.byteOffset, w.length);
366
+ draw(canvas.getContext('2d'), w, f, i + 3, i + 3 + count, string);
367
+ i += 3 + count;
291
368
  break;
292
369
  }
293
- case 14: {
370
+ case 12: {
294
371
  const name = string(i + 2);
295
372
  const image = new Image();
296
373
  const bytes = app.files.get(resolvePath(app.cwd, name));
297
374
  const type = imageTypes[name.slice(name.lastIndexOf('.') + 1).toLowerCase()] || '';
298
- const url = bytes ? URL.createObjectURL(new Blob([bytes], { type })) : name;
375
+ const url = bytes ? URL.createObjectURL(new Blob([bytes], { type })) : app.url(name);
299
376
  const id = w[i + 1];
377
+ const tag = w[i + 4];
300
378
  const finished = () => {
301
- loading--;
379
+ counted(tag, -1);
302
380
  if (bytes) URL.revokeObjectURL(url);
303
381
  };
304
- loading++;
382
+ counted(tag, 1);
305
383
  image.onload = () => {
306
384
  finished();
307
- caller.event(IMAGE_LOADED, id, 0, 0);
385
+ caller.event(IMAGE_LOADED, id, tag, 0);
308
386
  };
309
387
  image.onerror = () => {
310
388
  finished();
311
389
  console.error(`dom: cannot load the image ${name}`);
312
- caller.event(IMAGE_FAILED, id, 0, errors.missing, utf8.encode(`cannot load the image ${name}`));
390
+ caller.event(IMAGE_FAILED, id, tag, errors.missing, utf8.encode(`cannot load the image ${name}`));
313
391
  };
314
392
  image.src = url;
315
393
  images.set(w[i + 1], image);
316
- i += 4;
394
+ i += 5;
317
395
  break;
318
396
  }
319
397
  case 13: {
320
- const canvas = nodes.get(w[i + 1]);
321
- const count = w[i + 2];
322
- const f = new Float32Array(w.buffer, w.byteOffset, w.length);
323
- draw(canvas.getContext('2d'), w, f, i + 3, i + 3 + count, string);
324
- i += 3 + count;
398
+ const style = document.createElement('style');
399
+ style.textContent = string(i + 1);
400
+ root.ownerDocument.head.appendChild(style);
401
+ i += 3;
402
+ break;
403
+ }
404
+ case 14: {
405
+ const node = nodeOf(w[i + 1]);
406
+ const path = string(i + 2);
407
+ const value = w[i + 4] === 0 ? float64(w[i + 5], w[i + 6]) : string(i + 5);
408
+ after.push(() => write(node, path, value));
409
+ i += 7;
410
+ break;
411
+ }
412
+ case 15: {
413
+ const node = nodeOf(w[i + 1]);
414
+ const path = string(i + 2);
415
+ const args = [];
416
+ for (let k = 0; k < w[i + 4]; k++) {
417
+ const at = i + 5 + 3 * k;
418
+ args.push(w[at] === 0 ? float64(w[at + 1], w[at + 2]) : string(at + 1));
419
+ }
420
+ after.push(() => call(node, path, args));
421
+ i += 5 + 3 * w[i + 4];
325
422
  break;
326
423
  }
327
424
  default:
@@ -340,45 +437,49 @@ export function dom(root) {
340
437
  caller = theCaller;
341
438
  app = theApp;
342
439
  const given = (s, out) => caller.give(utf8.encode(s), out);
440
+ const current = () => {
441
+ const e = event();
442
+ if (!e) caller.trap('dom: there is no current event');
443
+ return e;
444
+ };
445
+ const known = (id) => {
446
+ if (!nodes.has(id)) caller.trap(`dom: there is no node ${id}`);
447
+ return nodeOf(id);
448
+ };
343
449
  return {
344
450
  source: SOURCE,
345
451
  apply(ptr, length) {
346
452
  apply(caller.memory(), caller.u(ptr), length);
347
453
  },
348
- event_value(out) {
349
- const e = event();
350
- const target = e && e.target;
351
- return given(target && typeof target.value === 'string' ? target.value : '', out);
454
+ event_number(ptr, length) {
455
+ return numberOf(read(current(), caller.string(ptr, length)));
352
456
  },
353
- event_key(out) {
354
- const e = event();
355
- return given(e && typeof e.key === 'string' ? e.key : '', out);
457
+ event_text(ptr, length, out) {
458
+ return given(textOf(read(current(), caller.string(ptr, length))), out);
356
459
  },
357
- event_flags() {
358
- const e = event();
359
- if (!e) return 0;
360
- return (e.shiftKey ? 1 : 0) | (e.ctrlKey ? 2 : 0) | (e.altKey ? 4 : 0) | (e.metaKey ? 8 : 0) |
361
- (e.target && e.target.checked === true ? 16 : 0) | (document.pointerLockElement ? 32 : 0);
460
+ event_call(ptr, length) {
461
+ call(current(), caller.string(ptr, length), []);
362
462
  },
363
- event_number(which) {
364
- const e = event();
365
- if (!e) return 0;
366
- switch (which) {
367
- case 0: return e.timeStamp || 0;
368
- case 1: return e.offsetX || 0;
369
- case 2: return e.offsetY || 0;
370
- case 3: return e.movementX || 0;
371
- case 4: return e.movementY || 0;
372
- case 5: return e.button || 0;
373
- default: return 0;
374
- }
463
+ event_is(node, ptr, length) {
464
+ return walk(current(), caller.string(ptr, length)) === known(node) ? 1 : 0;
465
+ },
466
+ read_number(node, ptr, length) {
467
+ return numberOf(read(known(node), caller.string(ptr, length)));
468
+ },
469
+ read_text(node, ptr, length, out) {
470
+ return given(textOf(read(known(node), caller.string(ptr, length))), out);
471
+ },
472
+ is(node, from, ptr, length) {
473
+ return walk(known(from), caller.string(ptr, length)) === known(node) ? 1 : 0;
375
474
  },
376
- images_loading() {
377
- return loading;
475
+ images_loading(tag) {
476
+ return loading.get(tag) || 0;
378
477
  },
379
- prevent_default() {
380
- const e = event();
381
- if (e && e.preventDefault) e.preventDefault();
478
+ element(ptr, length, node) {
479
+ const found = root.ownerDocument.getElementById(caller.string(ptr, length));
480
+ if (!found) return 0;
481
+ nodes.set(node, found);
482
+ return 1;
382
483
  },
383
484
  };
384
485
  };
@@ -1,34 +1,16 @@
1
- ;; The page: the host module "dom". A heard event carries the id and tag its listener was given. dom-event-flags gives
2
- ;; the bits dom-shift to dom-locked. dom-event-number takes one of dom-time, dom-x, dom-y, dom-movement-x,
3
- ;; dom-movement-y and dom-button, and gives that number for the current event.
4
- (provide dom-source dom-heard dom-image-loaded dom-image-failed
5
- dom-shift dom-control dom-alt dom-meta dom-checked dom-locked
6
- dom-time dom-x dom-y dom-movement-x dom-movement-y dom-button
7
- dom-apply dom-event-value dom-event-key dom-event-flags dom-event-number dom-prevent-default dom-images-loading)
1
+ ;; The document: the host module "dom". A heard event carries the id and tag its listener was given. A visibility
2
+ ;; listener hears when its element comes into the viewport or leaves it. dom-bind-element gives the element with an id a
3
+ ;; node number, for dom-element (std/web/dom.sub). The reads of the document are in std/web/dom.sub too.
4
+ ;; dom-images-loading counts the images loading for a tag.
5
+ (provide dom-source dom-heard dom-image-loaded dom-image-failed dom-apply dom-bind-element dom-images-loading)
8
6
 
9
7
  (const dom-source u32 1)
10
8
  (const dom-heard u32 1)
11
9
  (const dom-image-loaded u32 2)
12
10
  (const dom-image-failed u32 3)
13
11
 
14
- (const dom-shift u32 1)
15
- (const dom-control u32 2)
16
- (const dom-alt u32 4)
17
- (const dom-meta u32 8)
18
- (const dom-checked u32 16)
19
- (const dom-locked u32 32)
20
-
21
- (const dom-time i32 0)
22
- (const dom-x i32 1)
23
- (const dom-y i32 2)
24
- (const dom-movement-x i32 3)
25
- (const dom-movement-y i32 4)
26
- (const dom-button i32 5)
27
-
28
12
  (import dom-apply "dom" "apply" (i32 i32 ->))
29
- (import dom-event-value "dom" "event_value" (i32 -> i32))
30
- (import dom-event-key "dom" "event_key" (i32 -> i32))
31
- (import dom-event-flags "dom" "event_flags" (-> i32))
32
- (import dom-event-number "dom" "event_number" (i32 -> f64))
33
- (import dom-prevent-default "dom" "prevent_default" (->))
34
- (import dom-images-loading "dom" "images_loading" (-> i32))
13
+ (import images-loading "dom" "images_loading" (i32 -> i32))
14
+ (import dom-bind-element "dom" "element" (i32 i32 i32 -> i32))
15
+
16
+ (define (dom-images-loading [tag (ptr void)]) -> u32 (cast u32 (images-loading (cast i32 tag))))
@@ -1,5 +1,5 @@
1
- // A page's files for fs.mjs, kept in memory under the program's working directory and checked against the program's
2
- // grants. A directory exists as long as it has a file in it, and there are no links or modes.
1
+ // A web program's files for fs.mjs, kept in memory under the program's working directory and checked against the
2
+ // program's grants. A directory exists as long as it has a file in it, and there are no links or modes.
3
3
  import { mayPath, mayTree, pathDenied, unlimited } from '../grants.mjs';
4
4
  import { sameFile, tempName } from '../fs.mjs';
5
5
 
@@ -17,7 +17,7 @@ const missing = () => Object.assign(new Error('No such file or directory'), { co
17
17
  const unsupported = () => new Error('Operation not supported');
18
18
  const under = (dir) => (dir === '/' ? '/' : `${dir}/`);
19
19
 
20
- export function pageFiles(files, cwd, grants) {
20
+ export function memoryFiles(files, cwd, grants) {
21
21
  const times = new Map();
22
22
  const at = (k, p, tree = false) => {
23
23
  const key = resolvePath(cwd, p);