sigmx 0.1.1 → 0.2.0
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.
- package/README.md +10 -10
- package/dist/kernel/compile.d.ts +8 -9
- package/dist/kernel/compile.js +102 -67
- package/dist/kernel/compile.js.map +2 -2
- package/dist/kernel/contracts.d.ts +11 -5
- package/dist/kernel/index.d.ts +2 -2
- package/dist/kernel/index.js +5 -6
- package/dist/kernel/index.js.map +2 -2
- package/dist/kernel/precompile.d.ts +8 -10
- package/dist/kernel/precompile.js +25 -75
- package/dist/kernel/precompile.js.map +2 -2
- package/dist/kernel/precompiled.d.ts +7 -4
- package/dist/kernel/precompiled.js +5 -5
- package/dist/kernel/precompiled.js.map +2 -2
- package/dist/kernel/reactive.d.ts +8 -2
- package/dist/kernel/reactive.js +91 -62
- package/dist/kernel/reactive.js.map +3 -3
- package/dist/kernel/runtime.js +78 -68
- package/dist/kernel/runtime.js.map +3 -3
- package/dist/kernel/scan.d.ts +1 -1
- package/dist/kernel/scan.js +3 -3
- package/dist/kernel/scan.js.map +2 -2
- package/dist/kernel/state.d.ts +4 -3
- package/dist/kernel/state.js +91 -80
- package/dist/kernel/state.js.map +2 -2
- package/dist/kernel/strict-csp.d.ts +0 -5
- package/dist/kernel/strict-csp.js +2 -2
- package/dist/kernel/strict-csp.js.map +2 -2
- package/dist/lib/casing.d.ts +4 -1
- package/dist/lib/casing.js +8 -1
- package/dist/lib/casing.js.map +2 -2
- package/dist/lib/objects.d.ts +5 -3
- package/dist/lib/objects.js +10 -5
- package/dist/lib/objects.js.map +2 -2
- package/dist/lib/schedule.d.ts +3 -3
- package/dist/lib/schedule.js +12 -10
- package/dist/lib/schedule.js.map +2 -2
- package/dist/lib/sse.d.ts +10 -0
- package/dist/lib/sse.js +32 -29
- package/dist/lib/sse.js.map +2 -2
- package/dist/plugins/def.d.ts +5 -0
- package/dist/plugins/def.js +22 -0
- package/dist/plugins/def.js.map +7 -0
- package/dist/plugins/directives/attribute-sync.js +8 -12
- package/dist/plugins/directives/attribute-sync.js.map +2 -2
- package/dist/plugins/directives/boost.d.ts +1 -1
- package/dist/plugins/directives/boost.js +34 -31
- package/dist/plugins/directives/boost.js.map +2 -2
- package/dist/plugins/directives/busy-flag.js +13 -16
- package/dist/plugins/directives/busy-flag.js.map +2 -2
- package/dist/plugins/directives/class-toggle.js +17 -16
- package/dist/plugins/directives/class-toggle.js.map +2 -2
- package/dist/plugins/directives/cloak.js +2 -7
- package/dist/plugins/directives/cloak.js.map +2 -2
- package/dist/plugins/directives/collapse.js +36 -37
- package/dist/plugins/directives/collapse.js.map +2 -2
- package/dist/plugins/directives/declare.js +5 -8
- package/dist/plugins/directives/declare.js.map +2 -2
- package/dist/plugins/directives/derive.js +9 -18
- package/dist/plugins/directives/derive.js.map +2 -2
- package/dist/plugins/directives/element-ref.js +8 -8
- package/dist/plugins/directives/element-ref.js.map +2 -2
- package/dist/plugins/directives/event-listener.js +18 -26
- package/dist/plugins/directives/event-listener.js.map +2 -2
- package/dist/plugins/directives/frame-loop.js +9 -14
- package/dist/plugins/directives/frame-loop.js.map +2 -2
- package/dist/plugins/directives/html.js +6 -7
- package/dist/plugins/directives/html.js.map +2 -2
- package/dist/plugins/directives/inline-style.js +17 -20
- package/dist/plugins/directives/inline-style.js.map +2 -2
- package/dist/plugins/directives/mask.js +19 -23
- package/dist/plugins/directives/mask.js.map +2 -2
- package/dist/plugins/directives/media-query.d.ts +4 -1
- package/dist/plugins/directives/media-query.js +13 -13
- package/dist/plugins/directives/media-query.js.map +2 -2
- package/dist/plugins/directives/on-mount.js +4 -12
- package/dist/plugins/directives/on-mount.js.map +2 -2
- package/dist/plugins/directives/patch-listener.js +24 -19
- package/dist/plugins/directives/patch-listener.js.map +2 -2
- package/dist/plugins/directives/remove-me.js +7 -8
- package/dist/plugins/directives/remove-me.js.map +2 -2
- package/dist/plugins/directives/scroll-to.d.ts +4 -1
- package/dist/plugins/directives/scroll-to.js +8 -14
- package/dist/plugins/directives/scroll-to.js.map +2 -2
- package/dist/plugins/directives/size-watch.js +7 -12
- package/dist/plugins/directives/size-watch.js.map +2 -2
- package/dist/plugins/directives/state-dump.js +6 -10
- package/dist/plugins/directives/state-dump.js.map +2 -2
- package/dist/plugins/directives/storage-sync.js +13 -9
- package/dist/plugins/directives/storage-sync.js.map +2 -2
- package/dist/plugins/directives/teleport.js +8 -14
- package/dist/plugins/directives/teleport.js.map +2 -2
- package/dist/plugins/directives/text-content.js +6 -7
- package/dist/plugins/directives/text-content.js.map +2 -2
- package/dist/plugins/directives/timer.js +7 -13
- package/dist/plugins/directives/timer.js.map +2 -2
- package/dist/plugins/directives/transition.d.ts +1 -2
- package/dist/plugins/directives/transition.js +32 -34
- package/dist/plugins/directives/transition.js.map +2 -2
- package/dist/plugins/directives/tween.d.ts +2 -2
- package/dist/plugins/directives/tween.js +34 -50
- package/dist/plugins/directives/tween.js.map +2 -2
- package/dist/plugins/directives/two-way-bind.d.ts +1 -1
- package/dist/plugins/directives/two-way-bind.js +70 -97
- package/dist/plugins/directives/two-way-bind.js.map +2 -2
- package/dist/plugins/directives/url-params.js +35 -59
- package/dist/plugins/directives/url-params.js.map +3 -3
- package/dist/plugins/directives/url-replace.js +6 -7
- package/dist/plugins/directives/url-replace.js.map +2 -2
- package/dist/plugins/directives/validity.js +6 -9
- package/dist/plugins/directives/validity.js.map +2 -2
- package/dist/plugins/directives/viewport.js +18 -26
- package/dist/plugins/directives/viewport.js.map +2 -2
- package/dist/plugins/directives/visibility.js +9 -14
- package/dist/plugins/directives/visibility.js.map +2 -2
- package/dist/plugins/directives/watch.js +2 -8
- package/dist/plugins/directives/watch.js.map +2 -2
- package/dist/plugins/functions/bulk-set.js +7 -6
- package/dist/plugins/functions/bulk-set.js.map +2 -2
- package/dist/plugins/functions/bulk-toggle.js +7 -6
- package/dist/plugins/functions/bulk-toggle.js.map +2 -2
- package/dist/plugins/functions/confirm.js +2 -2
- package/dist/plugins/functions/confirm.js.map +2 -2
- package/dist/plugins/functions/copy-text.js +5 -5
- package/dist/plugins/functions/copy-text.js.map +2 -2
- package/dist/plugins/functions/dispatch.js +5 -5
- package/dist/plugins/functions/dispatch.js.map +2 -2
- package/dist/plugins/functions/locale-format.d.ts +4 -1
- package/dist/plugins/functions/locale-format.js +9 -16
- package/dist/plugins/functions/locale-format.js.map +2 -2
- package/dist/plugins/functions/range-map.js +6 -6
- package/dist/plugins/functions/range-map.js.map +2 -2
- package/dist/plugins/functions/request.d.ts +7 -3
- package/dist/plugins/functions/request.js +77 -86
- package/dist/plugins/functions/request.js.map +2 -2
- package/dist/plugins/functions/untracked-read.js +3 -2
- package/dist/plugins/functions/untracked-read.js.map +2 -2
- package/dist/plugins/functions/websocket.d.ts +3 -3
- package/dist/plugins/functions/websocket.js +13 -24
- package/dist/plugins/functions/websocket.js.map +2 -2
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/index.js +0 -4
- package/dist/plugins/index.js.map +2 -2
- package/dist/plugins/server-events/apply-elements.d.ts +1 -1
- package/dist/plugins/server-events/apply-elements.js +41 -43
- package/dist/plugins/server-events/apply-elements.js.map +2 -2
- package/dist/plugins/server-events/apply-state.js +9 -12
- package/dist/plugins/server-events/apply-state.js.map +2 -2
- package/dist/plugins/server-events/morph.js +46 -66
- package/dist/plugins/server-events/morph.js.map +3 -3
- package/dist/presets/all.js +0 -4
- package/dist/presets/all.js.map +2 -2
- package/dist/server.d.ts +10 -4
- package/dist/server.js +42 -14
- package/dist/server.js.map +2 -2
- package/dist/sigmx.standalone.js +1 -1
- package/dist/vite.js +17 -7
- package/dist/vite.js.map +2 -2
- package/package.json +4 -2
- package/dist/plugins/directives/transition-name.d.ts +0 -1
- package/dist/plugins/directives/transition-name.js +0 -11
- package/dist/plugins/directives/transition-name.js.map +0 -7
- package/dist/plugins/directives/trap.d.ts +0 -5
- package/dist/plugins/directives/trap.js +0 -54
- package/dist/plugins/directives/trap.js.map +0 -7
|
@@ -1,28 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { parseBlock, parseFields } from "../../lib/sse.js";
|
|
2
|
+
import { act } from "../def.js";
|
|
3
|
+
const websocket = act(
|
|
4
|
+
"ws",
|
|
5
|
+
({ el, runtime, cleanup }, url, o = {}) => {
|
|
5
6
|
const socket = new WebSocket(new URL(url, location.href.replace(/^http/, "ws")), o.protocols);
|
|
6
7
|
socket.onmessage = (m) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const v = line.slice(i + 1).trimStart();
|
|
15
|
-
if (field === "event") event = v;
|
|
16
|
-
else if (field === "data") {
|
|
17
|
-
const j = v.indexOf(" ");
|
|
18
|
-
const k = j < 0 ? v : v.slice(0, j);
|
|
19
|
-
const val = j < 0 ? "" : v.slice(j + 1);
|
|
20
|
-
data[k] = k in data ? `${data[k]}
|
|
21
|
-
${val}` : val;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
runtime.emit("server-event", { el, event, data });
|
|
25
|
-
runtime.handle(event, data);
|
|
8
|
+
if (typeof m.data !== "string") return;
|
|
9
|
+
for (const block of m.data.split(/(?:\r?\n){2,}/)) {
|
|
10
|
+
const e = parseBlock(block);
|
|
11
|
+
if (!e) continue;
|
|
12
|
+
const data = parseFields(e.data);
|
|
13
|
+
runtime.emit("server-event", { el, event: e.event, data });
|
|
14
|
+
runtime.handle(e.event, data);
|
|
26
15
|
}
|
|
27
16
|
};
|
|
28
17
|
cleanup(() => socket.close());
|
|
@@ -32,7 +21,7 @@ ${val}` : val;
|
|
|
32
21
|
socket
|
|
33
22
|
};
|
|
34
23
|
}
|
|
35
|
-
|
|
24
|
+
);
|
|
36
25
|
export {
|
|
37
26
|
websocket
|
|
38
27
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/plugins/functions/websocket.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { parseBlock, parseFields } from '../../lib/sse.js';\nimport { act } from '../def.js';\n\nexport type Socket = { send(data: unknown): void; close(): void; readonly socket: WebSocket };\n\n/**\n * `@ws(url, { protocols })` opens a WebSocket whose messages use the server-event wire format\n * (`event <name>` and `data <key> <value>` lines, blank-line separated) and routes them to the\n * same handlers as fetched streams. Returns `{ send, close, socket }`; closed when the element unmounts.\n */\nexport const websocket = act(\n 'ws',\n ({ el, runtime, cleanup }, url: string, o: { protocols?: string | string[] } = {}): Socket => {\n const socket = new WebSocket(new URL(url, location.href.replace(/^http/, 'ws')), o.protocols);\n socket.onmessage = (m) => {\n if (typeof m.data !== 'string') return; // binary frames are not server events\n for (const block of m.data.split(/(?:\\r?\\n){2,}/)) {\n const e = parseBlock(block);\n if (!e) continue;\n const data = parseFields(e.data);\n runtime.emit('server-event', { el, event: e.event, data });\n runtime.handle(e.event, data);\n }\n };\n cleanup(() => socket.close());\n return {\n send: (d) => socket.send(typeof d === 'string' ? d : JSON.stringify(d)),\n close: () => socket.close(),\n socket,\n };\n },\n);\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY,mBAAmB;AACxC,SAAS,WAAW;AASb,MAAM,YAAY;AAAA,EACvB;AAAA,EACA,CAAC,EAAE,IAAI,SAAS,QAAQ,GAAG,KAAa,IAAuC,CAAC,MAAc;AAC5F,UAAM,SAAS,IAAI,UAAU,IAAI,IAAI,KAAK,SAAS,KAAK,QAAQ,SAAS,IAAI,CAAC,GAAG,EAAE,SAAS;AAC5F,WAAO,YAAY,CAAC,MAAM;AACxB,UAAI,OAAO,EAAE,SAAS,SAAU;AAChC,iBAAW,SAAS,EAAE,KAAK,MAAM,eAAe,GAAG;AACjD,cAAM,IAAI,WAAW,KAAK;AAC1B,YAAI,CAAC,EAAG;AACR,cAAM,OAAO,YAAY,EAAE,IAAI;AAC/B,gBAAQ,KAAK,gBAAgB,EAAE,IAAI,OAAO,EAAE,OAAO,KAAK,CAAC;AACzD,gBAAQ,OAAO,EAAE,OAAO,IAAI;AAAA,MAC9B;AAAA,IACF;AACA,YAAQ,MAAM,OAAO,MAAM,CAAC;AAC5B,WAAO;AAAA,MACL,MAAM,CAAC,MAAM,OAAO,KAAK,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC,CAAC;AAAA,MACtE,OAAO,MAAM,OAAO,MAAM;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/plugins/index.d.ts
CHANGED
|
@@ -24,8 +24,6 @@ export { teleport } from './directives/teleport.js';
|
|
|
24
24
|
export { text } from './directives/text-content.js';
|
|
25
25
|
export { onInterval } from './directives/timer.js';
|
|
26
26
|
export { transition } from './directives/transition.js';
|
|
27
|
-
export { viewTransition } from './directives/transition-name.js';
|
|
28
|
-
export { trap } from './directives/trap.js';
|
|
29
27
|
export { animate } from './directives/tween.js';
|
|
30
28
|
export { bind } from './directives/two-way-bind.js';
|
|
31
29
|
export { queryString } from './directives/url-params.js';
|
package/dist/plugins/index.js
CHANGED
|
@@ -24,8 +24,6 @@ import { teleport } from "./directives/teleport.js";
|
|
|
24
24
|
import { text } from "./directives/text-content.js";
|
|
25
25
|
import { onInterval } from "./directives/timer.js";
|
|
26
26
|
import { transition } from "./directives/transition.js";
|
|
27
|
-
import { viewTransition } from "./directives/transition-name.js";
|
|
28
|
-
import { trap } from "./directives/trap.js";
|
|
29
27
|
import { animate } from "./directives/tween.js";
|
|
30
28
|
import { bind } from "./directives/two-way-bind.js";
|
|
31
29
|
import { queryString } from "./directives/url-params.js";
|
|
@@ -101,8 +99,6 @@ export {
|
|
|
101
99
|
text,
|
|
102
100
|
toggleAll,
|
|
103
101
|
transition,
|
|
104
|
-
trap,
|
|
105
|
-
viewTransition,
|
|
106
102
|
websocket
|
|
107
103
|
};
|
|
108
104
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/plugins/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { attr } from './directives/attribute-sync.js';\nexport { boost } from './directives/boost.js';\nexport { indicator } from './directives/busy-flag.js';\nexport { className } from './directives/class-toggle.js';\nexport { cloak } from './directives/cloak.js';\nexport { collapse } from './directives/collapse.js';\nexport { signals } from './directives/declare.js';\nexport { computed } from './directives/derive.js';\nexport { ref } from './directives/element-ref.js';\nexport { on } from './directives/event-listener.js';\nexport { onRaf } from './directives/frame-loop.js';\nexport { html } from './directives/html.js';\nexport { style } from './directives/inline-style.js';\nexport { applyMask, mask } from './directives/mask.js';\nexport { matchMedia } from './directives/media-query.js';\nexport { init } from './directives/on-mount.js';\nexport { onSignalPatch } from './directives/patch-listener.js';\nexport { removeMe } from './directives/remove-me.js';\nexport { scrollIntoView } from './directives/scroll-to.js';\nexport { onResize } from './directives/size-watch.js';\nexport { jsonSignals } from './directives/state-dump.js';\nexport { persist } from './directives/storage-sync.js';\nexport { teleport } from './directives/teleport.js';\nexport { text } from './directives/text-content.js';\nexport { onInterval } from './directives/timer.js';\nexport { transition } from './directives/transition.js';\nexport {
|
|
5
|
-
"mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAC1B,SAAS,aAAa;AACtB,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,WAAW;AACpB,SAAS,UAAU;AACnB,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,WAAW,YAAY;AAChC,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,
|
|
4
|
+
"sourcesContent": ["export { attr } from './directives/attribute-sync.js';\nexport { boost } from './directives/boost.js';\nexport { indicator } from './directives/busy-flag.js';\nexport { className } from './directives/class-toggle.js';\nexport { cloak } from './directives/cloak.js';\nexport { collapse } from './directives/collapse.js';\nexport { signals } from './directives/declare.js';\nexport { computed } from './directives/derive.js';\nexport { ref } from './directives/element-ref.js';\nexport { on } from './directives/event-listener.js';\nexport { onRaf } from './directives/frame-loop.js';\nexport { html } from './directives/html.js';\nexport { style } from './directives/inline-style.js';\nexport { applyMask, mask } from './directives/mask.js';\nexport { matchMedia } from './directives/media-query.js';\nexport { init } from './directives/on-mount.js';\nexport { onSignalPatch } from './directives/patch-listener.js';\nexport { removeMe } from './directives/remove-me.js';\nexport { scrollIntoView } from './directives/scroll-to.js';\nexport { onResize } from './directives/size-watch.js';\nexport { jsonSignals } from './directives/state-dump.js';\nexport { persist } from './directives/storage-sync.js';\nexport { teleport } from './directives/teleport.js';\nexport { text } from './directives/text-content.js';\nexport { onInterval } from './directives/timer.js';\nexport { transition } from './directives/transition.js';\nexport { animate } from './directives/tween.js';\nexport { bind } from './directives/two-way-bind.js';\nexport { queryString } from './directives/url-params.js';\nexport { replaceUrl } from './directives/url-replace.js';\nexport { customValidity } from './directives/validity.js';\nexport { onIntersect } from './directives/viewport.js';\nexport { show } from './directives/visibility.js';\nexport { effect } from './directives/watch.js';\nexport { setAll } from './functions/bulk-set.js';\nexport { toggleAll } from './functions/bulk-toggle.js';\nexport { confirm } from './functions/confirm.js';\nexport { clipboard } from './functions/copy-text.js';\nexport { dispatch } from './functions/dispatch.js';\nexport { intl } from './functions/locale-format.js';\nexport { fit } from './functions/range-map.js';\nexport type { RequestOptions } from './functions/request.js';\nexport { httpDelete, httpGet, httpPatch, httpPost, httpPut } from './functions/request.js';\nexport { peek } from './functions/untracked-read.js';\nexport type { Socket } from './functions/websocket.js';\nexport { websocket } from './functions/websocket.js';\nexport { applyElements } from './server-events/apply-elements.js';\nexport { applyState } from './server-events/apply-state.js';\nexport type { MorphOptions } from './server-events/morph.js';\nexport { activateScripts, morph, morphInner } from './server-events/morph.js';\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAC1B,SAAS,aAAa;AACtB,SAAS,gBAAgB;AACzB,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,WAAW;AACpB,SAAS,UAAU;AACnB,SAAS,aAAa;AACtB,SAAS,YAAY;AACrB,SAAS,aAAa;AACtB,SAAS,WAAW,YAAY;AAChC,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAC9B,SAAS,gBAAgB;AACzB,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;AACzB,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AACxB,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,eAAe;AACxB,SAAS,iBAAiB;AAC1B,SAAS,gBAAgB;AACzB,SAAS,YAAY;AACrB,SAAS,WAAW;AAEpB,SAAS,YAAY,SAAS,WAAW,UAAU,eAAe;AAClE,SAAS,YAAY;AAErB,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAE3B,SAAS,iBAAiB,OAAO,kBAAkB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/** Server event `patch-elements`: `elements <html>`, `selector`, `mode
|
|
1
|
+
/** Server event `patch-elements`: `elements <html>`, `selector`, `mode`. */
|
|
2
2
|
export declare const applyElements: import("../../kernel/contracts.js").HandlerPlugin;
|
|
@@ -1,62 +1,60 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { srv } from "../def.js";
|
|
2
2
|
import { activateScripts, morph, morphInner } from "./morph.js";
|
|
3
|
-
const
|
|
3
|
+
const mergeHead = (head, next) => {
|
|
4
|
+
const wanted = [...next.children];
|
|
5
|
+
for (const old of [...head.children]) {
|
|
6
|
+
const i = wanted.findIndex((n) => n.isEqualNode(old));
|
|
7
|
+
if (i < 0) old.remove();
|
|
8
|
+
else wanted.splice(i, 1);
|
|
9
|
+
}
|
|
10
|
+
for (const n of wanted) {
|
|
11
|
+
const copy = document.importNode(n, true);
|
|
12
|
+
head.append(copy);
|
|
13
|
+
activateScripts(copy);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
4
16
|
const parse = (html) => {
|
|
5
|
-
if (
|
|
17
|
+
if (/^\s*<(!doctype|html)[\s>]|<\/(html|head|body)>/i.test(html))
|
|
18
|
+
return new DOMParser().parseFromString(html, "text/html");
|
|
6
19
|
const t = document.createElement("template");
|
|
7
20
|
t.innerHTML = html;
|
|
8
21
|
return t.content;
|
|
9
22
|
};
|
|
10
|
-
const
|
|
23
|
+
const applyElements = srv("patch-elements", (runtime, { elements = "", selector = "", mode = "outer" }) => {
|
|
11
24
|
const o = { ignoreAttr: runtime.attr("ignore-morph"), preserveAttr: runtime.attr("preserve-attr") };
|
|
12
|
-
const parsed = parse(
|
|
25
|
+
const parsed = parse(elements);
|
|
13
26
|
if (parsed instanceof Document) {
|
|
14
|
-
|
|
27
|
+
for (const { name, value } of parsed.documentElement.attributes) document.documentElement.setAttribute(name, value);
|
|
28
|
+
if (parsed.head.childNodes.length) mergeHead(document.head, parsed.head);
|
|
15
29
|
morph(document.body, parsed.body, o);
|
|
16
30
|
return;
|
|
17
31
|
}
|
|
18
|
-
const
|
|
19
|
-
if (mode === "remove") {
|
|
20
|
-
for (const t of document.querySelectorAll(selector)) t.remove();
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
if (!selector) {
|
|
24
|
-
if (mode !== "outer" && mode !== "replace") throw new Error(`patch-elements mode "${mode}" needs a selector`);
|
|
25
|
-
for (const child of [...frag.children]) {
|
|
26
|
-
const target = child.id && document.getElementById(child.id);
|
|
27
|
-
if (!target) {
|
|
28
|
-
console.warn("patch-elements: no element with id", child.id);
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
if (mode === "outer") morph(target, child, o);
|
|
32
|
-
else {
|
|
33
|
-
target.replaceWith(child);
|
|
34
|
-
activateScripts(child);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
const targets = [...document.querySelectorAll(selector)];
|
|
40
|
-
if (!targets.length) console.warn("patch-elements: no elements match", selector);
|
|
41
|
-
targets.forEach((t, i) => {
|
|
42
|
-
const content = i === targets.length - 1 ? frag : frag.cloneNode(true);
|
|
32
|
+
const patch = (t, content) => {
|
|
43
33
|
const nodes = [...content.childNodes];
|
|
44
|
-
if (mode === "outer" && nodes.length === 1 &&
|
|
34
|
+
if (mode === "outer" && nodes.length === 1 && nodes[0].nodeType === 1) morph(t, nodes[0], o);
|
|
45
35
|
else if (mode === "inner") morphInner(t, content, o);
|
|
46
36
|
else {
|
|
47
|
-
|
|
48
|
-
else t[mode](content);
|
|
37
|
+
mode === "outer" || mode === "replace" ? t.replaceWith(content) : t[mode](content);
|
|
49
38
|
for (const n of nodes) activateScripts(n);
|
|
50
39
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
40
|
+
};
|
|
41
|
+
if (selector) {
|
|
42
|
+
const targets = [...document.querySelectorAll(selector)];
|
|
43
|
+
if (mode === "remove") for (const t of targets) t.remove();
|
|
44
|
+
else
|
|
45
|
+
targets.forEach((t, i) => {
|
|
46
|
+
patch(t, i + 1 < targets.length ? parsed.cloneNode(true) : parsed);
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
49
|
+
if (mode !== "outer" && mode !== "replace") throw new Error(`mode "${mode}" needs a selector`);
|
|
50
|
+
for (const child of [...parsed.children]) {
|
|
51
|
+
const target = child.id && document.getElementById(child.id);
|
|
52
|
+
if (target) {
|
|
53
|
+
const f = document.createDocumentFragment();
|
|
54
|
+
f.append(child);
|
|
55
|
+
patch(target, f);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
60
58
|
}
|
|
61
59
|
});
|
|
62
60
|
export {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/plugins/server-events/apply-elements.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { srv } from '../def.js';\nimport { activateScripts, morph, morphInner } from './morph.js';\n\n/**\n * `<head>` children are matched by equality, not position: a stylesheet or meta that is already\n * present is left alone (no reload flash), missing ones are added, absent ones removed.\n */\nconst mergeHead = (head: HTMLHeadElement, next: HTMLHeadElement) => {\n const wanted = [...next.children];\n for (const old of [...head.children]) {\n const i = wanted.findIndex((n) => n.isEqualNode(old));\n if (i < 0) old.remove();\n else wanted.splice(i, 1);\n }\n for (const n of wanted) {\n const copy = document.importNode(n, true);\n head.append(copy);\n activateScripts(copy);\n }\n};\n\nconst parse = (html: string): DocumentFragment | Document => {\n if (/^\\s*<(!doctype|html)[\\s>]|<\\/(html|head|body)>/i.test(html))\n return new DOMParser().parseFromString(html, 'text/html');\n const t = document.createElement('template');\n t.innerHTML = html;\n return t.content;\n};\n\n/** Server event `patch-elements`: `elements <html>`, `selector`, `mode`. */\nexport const applyElements = srv('patch-elements', (runtime, { elements = '', selector = '', mode = 'outer' }) => {\n const o = { ignoreAttr: runtime.attr('ignore-morph'), preserveAttr: runtime.attr('preserve-attr') };\n const parsed = parse(elements);\n if (parsed instanceof Document) {\n for (const { name, value } of parsed.documentElement.attributes) document.documentElement.setAttribute(name, value);\n if (parsed.head.childNodes.length) mergeHead(document.head, parsed.head);\n morph(document.body, parsed.body, o);\n return;\n }\n /** Put `content` at `t` per `mode`; a lone element in outer mode is morphed rather than swapped. */\n const patch = (t: Element, content: DocumentFragment) => {\n const nodes = [...content.childNodes];\n if (mode === 'outer' && nodes.length === 1 && nodes[0].nodeType === 1) morph(t, nodes[0] as Element, o);\n else if (mode === 'inner') morphInner(t, content, o);\n else {\n mode === 'outer' || mode === 'replace' ? t.replaceWith(content) : (t as any)[mode](content);\n for (const n of nodes) activateScripts(n);\n }\n };\n if (selector) {\n const targets = [...document.querySelectorAll(selector)];\n if (mode === 'remove') for (const t of targets) t.remove();\n else\n targets.forEach((t, i) => {\n patch(t, i + 1 < targets.length ? (parsed.cloneNode(true) as DocumentFragment) : parsed);\n });\n } else {\n if (mode !== 'outer' && mode !== 'replace') throw new Error(`mode \"${mode}\" needs a selector`);\n for (const child of [...parsed.children]) {\n const target = child.id && document.getElementById(child.id);\n if (target) {\n const f = document.createDocumentFragment();\n f.append(child);\n patch(target, f);\n }\n }\n }\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW;AACpB,SAAS,iBAAiB,OAAO,kBAAkB;AAMnD,MAAM,YAAY,CAAC,MAAuB,SAA0B;AAClE,QAAM,SAAS,CAAC,GAAG,KAAK,QAAQ;AAChC,aAAW,OAAO,CAAC,GAAG,KAAK,QAAQ,GAAG;AACpC,UAAM,IAAI,OAAO,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,CAAC;AACpD,QAAI,IAAI,EAAG,KAAI,OAAO;AAAA,QACjB,QAAO,OAAO,GAAG,CAAC;AAAA,EACzB;AACA,aAAW,KAAK,QAAQ;AACtB,UAAM,OAAO,SAAS,WAAW,GAAG,IAAI;AACxC,SAAK,OAAO,IAAI;AAChB,oBAAgB,IAAI;AAAA,EACtB;AACF;AAEA,MAAM,QAAQ,CAAC,SAA8C;AAC3D,MAAI,kDAAkD,KAAK,IAAI;AAC7D,WAAO,IAAI,UAAU,EAAE,gBAAgB,MAAM,WAAW;AAC1D,QAAM,IAAI,SAAS,cAAc,UAAU;AAC3C,IAAE,YAAY;AACd,SAAO,EAAE;AACX;AAGO,MAAM,gBAAgB,IAAI,kBAAkB,CAAC,SAAS,EAAE,WAAW,IAAI,WAAW,IAAI,OAAO,QAAQ,MAAM;AAChH,QAAM,IAAI,EAAE,YAAY,QAAQ,KAAK,cAAc,GAAG,cAAc,QAAQ,KAAK,eAAe,EAAE;AAClG,QAAM,SAAS,MAAM,QAAQ;AAC7B,MAAI,kBAAkB,UAAU;AAC9B,eAAW,EAAE,MAAM,MAAM,KAAK,OAAO,gBAAgB,WAAY,UAAS,gBAAgB,aAAa,MAAM,KAAK;AAClH,QAAI,OAAO,KAAK,WAAW,OAAQ,WAAU,SAAS,MAAM,OAAO,IAAI;AACvE,UAAM,SAAS,MAAM,OAAO,MAAM,CAAC;AACnC;AAAA,EACF;AAEA,QAAM,QAAQ,CAAC,GAAY,YAA8B;AACvD,UAAM,QAAQ,CAAC,GAAG,QAAQ,UAAU;AACpC,QAAI,SAAS,WAAW,MAAM,WAAW,KAAK,MAAM,CAAC,EAAE,aAAa,EAAG,OAAM,GAAG,MAAM,CAAC,GAAc,CAAC;AAAA,aAC7F,SAAS,QAAS,YAAW,GAAG,SAAS,CAAC;AAAA,SAC9C;AACH,eAAS,WAAW,SAAS,YAAY,EAAE,YAAY,OAAO,IAAK,EAAU,IAAI,EAAE,OAAO;AAC1F,iBAAW,KAAK,MAAO,iBAAgB,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,UAAU;AACZ,UAAM,UAAU,CAAC,GAAG,SAAS,iBAAiB,QAAQ,CAAC;AACvD,QAAI,SAAS,SAAU,YAAW,KAAK,QAAS,GAAE,OAAO;AAAA;AAEvD,cAAQ,QAAQ,CAAC,GAAG,MAAM;AACxB,cAAM,GAAG,IAAI,IAAI,QAAQ,SAAU,OAAO,UAAU,IAAI,IAAyB,MAAM;AAAA,MACzF,CAAC;AAAA,EACL,OAAO;AACL,QAAI,SAAS,WAAW,SAAS,UAAW,OAAM,IAAI,MAAM,SAAS,IAAI,oBAAoB;AAC7F,eAAW,SAAS,CAAC,GAAG,OAAO,QAAQ,GAAG;AACxC,YAAM,SAAS,MAAM,MAAM,SAAS,eAAe,MAAM,EAAE;AAC3D,UAAI,QAAQ;AACV,cAAM,IAAI,SAAS,uBAAuB;AAC1C,UAAE,OAAO,KAAK;AACd,cAAM,QAAQ,CAAC;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AACF,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
const applyState =
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} catch {
|
|
10
|
-
patch = runtime.compiler([], `return (${signals})`)();
|
|
11
|
-
}
|
|
12
|
-
runtime.store.merge(patch, { ifMissing: onlyIfMissing === "true" });
|
|
1
|
+
import { srv } from "../def.js";
|
|
2
|
+
const applyState = srv("patch-signals", (runtime, { signals, onlyIfMissing }) => {
|
|
3
|
+
if (!signals) return;
|
|
4
|
+
let patch;
|
|
5
|
+
try {
|
|
6
|
+
patch = JSON.parse(signals);
|
|
7
|
+
} catch {
|
|
8
|
+
patch = runtime.compiler([], `return (${signals})`)();
|
|
13
9
|
}
|
|
10
|
+
runtime.store.merge(patch, { ifMissing: onlyIfMissing === "true" });
|
|
14
11
|
});
|
|
15
12
|
export {
|
|
16
13
|
applyState
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/plugins/server-events/apply-state.ts"],
|
|
4
|
-
"sourcesContent": ["import {
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
4
|
+
"sourcesContent": ["import { srv } from '../def.js';\n\n/** Server event `patch-signals`: `signals {json}` merged into the store; `onlyIfMissing true` keeps existing values. */\nexport const applyState = srv('patch-signals', (runtime, { signals, onlyIfMissing }) => {\n if (!signals) return;\n let patch: Record<string, unknown>;\n try {\n patch = JSON.parse(signals);\n } catch {\n patch = runtime.compiler([], `return (${signals})`)();\n }\n runtime.store.merge(patch, { ifMissing: onlyIfMissing === 'true' });\n});\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW;AAGb,MAAM,aAAa,IAAI,iBAAiB,CAAC,SAAS,EAAE,SAAS,cAAc,MAAM;AACtF,MAAI,CAAC,QAAS;AACd,MAAI;AACJ,MAAI;AACF,YAAQ,KAAK,MAAM,OAAO;AAAA,EAC5B,QAAQ;AACN,YAAQ,QAAQ,SAAS,CAAC,GAAG,WAAW,OAAO,GAAG,EAAE;AAAA,EACtD;AACA,UAAQ,MAAM,MAAM,OAAO,EAAE,WAAW,kBAAkB,OAAO,CAAC;AACpE,CAAC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const ran = /* @__PURE__ */ new WeakSet();
|
|
2
|
-
const isEl = (n) =>
|
|
2
|
+
const isEl = (n) => n?.nodeType === 1;
|
|
3
3
|
const activateScripts = (root) => {
|
|
4
4
|
if (!isEl(root)) return;
|
|
5
|
-
for (const old of
|
|
6
|
-
if (ran.has(old)) continue;
|
|
5
|
+
for (const old of [root, ...root.querySelectorAll("script")]) {
|
|
6
|
+
if (!old.matches("script") || ran.has(old)) continue;
|
|
7
7
|
const s = document.createElement("script");
|
|
8
8
|
for (const { name, value } of old.attributes) s.setAttribute(name, value);
|
|
9
9
|
s.text = old.text;
|
|
@@ -11,50 +11,36 @@ const activateScripts = (root) => {
|
|
|
11
11
|
old.replaceWith(s);
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const fresh = (b, insert) => {
|
|
15
|
+
const n = document.importNode(b, true);
|
|
16
|
+
insert(n);
|
|
17
|
+
activateScripts(n);
|
|
18
18
|
};
|
|
19
|
+
const idsIn = (n) => [n, ...n.querySelectorAll?.("[id]") ?? []].filter((e) => isEl(e) && e.id);
|
|
20
|
+
const containsKept = (n, ctx) => idsIn(n).some((e) => ctx.keep.has(e.id));
|
|
19
21
|
const place = (parent, node, before) => {
|
|
20
|
-
const p = parent;
|
|
21
22
|
try {
|
|
22
|
-
|
|
23
|
+
parent.moveBefore(node, before);
|
|
23
24
|
} catch {
|
|
24
25
|
parent.insertBefore(node, before);
|
|
25
26
|
}
|
|
26
27
|
};
|
|
27
|
-
const
|
|
28
|
-
for (let n = from; n; n = n.nextSibling) if (isEl(n) && n.id === id) return n;
|
|
29
|
-
return ctx.pantry.querySelector(`#${CSS.escape(id)}`) ?? document.getElementById(id);
|
|
30
|
-
};
|
|
31
|
-
const compatible = (old, next, ctx) => old.nodeType === next.nodeType && old.nodeName === next.nodeName && (!isEl(old) || !old.id || old.id === next.id) && !(isEl(old) && ctx.keep.has(old.id) && old.id !== next.id);
|
|
28
|
+
const compatible = (old, next) => old.nodeName === next.nodeName && (!isEl(old) || !old.id || old.id === next.id);
|
|
32
29
|
const syncAttributes = (a, b, ctx) => {
|
|
33
|
-
const keep = new Set(
|
|
34
|
-
|
|
35
|
-
);
|
|
30
|
+
const keep = new Set((b.getAttribute(ctx.preserve) ?? a.getAttribute(ctx.preserve) ?? "").split(/\s+/));
|
|
31
|
+
const differs = (name) => !keep.has(name) && a.getAttribute(name) !== b.getAttribute(name);
|
|
36
32
|
let changed = false;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
a
|
|
41
|
-
changed = true;
|
|
42
|
-
}
|
|
43
|
-
if (attrDiffers("checked")) {
|
|
44
|
-
a.checked = b.hasAttribute("checked");
|
|
45
|
-
changed = true;
|
|
46
|
-
}
|
|
47
|
-
} else if (a instanceof HTMLTextAreaElement && b instanceof HTMLTextAreaElement) {
|
|
48
|
-
if (!keep.has("value") && a.defaultValue !== b.defaultValue) {
|
|
49
|
-
a.value = b.defaultValue;
|
|
50
|
-
changed = true;
|
|
51
|
-
}
|
|
52
|
-
} else if (a instanceof HTMLOptionElement && b instanceof HTMLOptionElement) {
|
|
53
|
-
if (attrDiffers("selected")) {
|
|
54
|
-
a.selected = b.hasAttribute("selected");
|
|
33
|
+
const live = a instanceof HTMLInputElement && a.type !== "file" ? ["value", "checked"] : a instanceof HTMLOptionElement ? ["selected"] : [];
|
|
34
|
+
for (const name of live) {
|
|
35
|
+
if (differs(name)) {
|
|
36
|
+
a[name] = name === "value" ? b.getAttribute(name) ?? "" : b.hasAttribute(name);
|
|
55
37
|
changed = true;
|
|
56
38
|
}
|
|
57
39
|
}
|
|
40
|
+
if (a instanceof HTMLTextAreaElement && !keep.has("value") && a.defaultValue !== b.defaultValue) {
|
|
41
|
+
a.value = b.defaultValue;
|
|
42
|
+
changed = true;
|
|
43
|
+
}
|
|
58
44
|
for (const { name, value } of b.attributes)
|
|
59
45
|
if (!keep.has(name) && a.getAttribute(name) !== value) a.setAttribute(name, value);
|
|
60
46
|
for (const { name } of [...a.attributes]) if (!keep.has(name) && !b.hasAttribute(name)) a.removeAttribute(name);
|
|
@@ -64,23 +50,22 @@ const syncAttributes = (a, b, ctx) => {
|
|
|
64
50
|
);
|
|
65
51
|
};
|
|
66
52
|
const morphNode = (a, b, ctx) => {
|
|
67
|
-
if (a.
|
|
68
|
-
|
|
69
|
-
a.replaceWith(fresh);
|
|
70
|
-
activateScripts(fresh);
|
|
53
|
+
if (a.nodeName !== b.nodeName) {
|
|
54
|
+
fresh(b, (n) => a.replaceWith(n));
|
|
71
55
|
return;
|
|
72
56
|
}
|
|
73
57
|
if (!isEl(a) || !isEl(b)) {
|
|
74
58
|
if (a.nodeValue !== b.nodeValue) a.nodeValue = b.nodeValue;
|
|
75
59
|
return;
|
|
76
60
|
}
|
|
61
|
+
if (b.localName === "script" && !a.isEqualNode(b)) {
|
|
62
|
+
fresh(b, (n) => a.replaceWith(n));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
77
65
|
if (a.hasAttribute(ctx.ignore) && b.hasAttribute(ctx.ignore)) return;
|
|
78
66
|
syncAttributes(a, b, ctx);
|
|
79
|
-
if (a instanceof HTMLTemplateElement
|
|
80
|
-
|
|
81
|
-
} else if (!a.isEqualNode(b)) {
|
|
82
|
-
morphChildren(a, b, ctx);
|
|
83
|
-
}
|
|
67
|
+
if (a instanceof HTMLTemplateElement) a.innerHTML = b.innerHTML;
|
|
68
|
+
else if (!a.isEqualNode(b)) morphChildren(a, b, ctx);
|
|
84
69
|
};
|
|
85
70
|
const morphChildren = (parent, next, ctx) => {
|
|
86
71
|
let cur = parent.firstChild;
|
|
@@ -88,56 +73,51 @@ const morphChildren = (parent, next, ctx) => {
|
|
|
88
73
|
while (cur && cur !== stop) {
|
|
89
74
|
const n = cur;
|
|
90
75
|
cur = cur.nextSibling;
|
|
91
|
-
|
|
76
|
+
if (!(isEl(n) && ctx.keep.get(n.id) === n)) n.remove();
|
|
92
77
|
}
|
|
93
78
|
return cur === stop;
|
|
94
79
|
};
|
|
95
80
|
for (const nb of [...next.childNodes]) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
81
|
+
const kept = isEl(nb) && ctx.keep.get(nb.id);
|
|
82
|
+
if (kept) {
|
|
83
|
+
let m2 = cur;
|
|
84
|
+
while (m2 && m2 !== kept) m2 = m2.nextSibling;
|
|
85
|
+
if (m2) {
|
|
86
|
+
dropUntil(kept);
|
|
87
|
+
cur = kept.nextSibling;
|
|
88
|
+
} else place(parent, kept, cur);
|
|
89
|
+
morphNode(kept, nb, ctx);
|
|
90
|
+
continue;
|
|
104
91
|
}
|
|
105
92
|
let m = cur;
|
|
106
|
-
while (m && !compatible(m, nb
|
|
93
|
+
while (m && !compatible(m, nb)) m = m.nextSibling;
|
|
107
94
|
if (m) {
|
|
108
95
|
dropUntil(m);
|
|
109
96
|
cur = m.nextSibling;
|
|
110
97
|
morphNode(m, nb, ctx);
|
|
111
98
|
} else if (isEl(nb) && containsKept(nb, ctx)) {
|
|
112
|
-
const shell = document.
|
|
99
|
+
const shell = document.importNode(nb, false);
|
|
113
100
|
parent.insertBefore(shell, cur);
|
|
114
101
|
morphNode(shell, nb, ctx);
|
|
115
102
|
} else {
|
|
116
|
-
|
|
117
|
-
parent.insertBefore(fresh, cur);
|
|
118
|
-
activateScripts(fresh);
|
|
103
|
+
fresh(nb, (n) => parent.insertBefore(n, cur));
|
|
119
104
|
}
|
|
120
105
|
}
|
|
121
106
|
dropUntil(null);
|
|
122
107
|
};
|
|
123
108
|
const context = (target, next, o) => {
|
|
124
|
-
const keep = /* @__PURE__ */ new Set();
|
|
125
109
|
const wanted = new Map(idsIn(next).map((e) => [e.id, e.tagName]));
|
|
126
|
-
for (const e of idsIn(target)) if (wanted.get(e.id) === e.tagName) keep.add(e.id);
|
|
127
110
|
return {
|
|
128
|
-
keep,
|
|
129
|
-
pantry: document.createDocumentFragment(),
|
|
111
|
+
keep: new Map(idsIn(target).flatMap((e) => wanted.get(e.id) === e.tagName ? [[e.id, e]] : [])),
|
|
130
112
|
ignore: o.ignoreAttr ?? "data-ignore-morph",
|
|
131
113
|
preserve: o.preserveAttr ?? "data-preserve-attr"
|
|
132
114
|
};
|
|
133
115
|
};
|
|
134
116
|
const morph = (target, next, o = {}) => {
|
|
135
|
-
|
|
136
|
-
morphNode(target, next, ctx);
|
|
117
|
+
morphNode(target, next, context(target, next, o));
|
|
137
118
|
};
|
|
138
119
|
const morphInner = (target, next, o = {}) => {
|
|
139
|
-
|
|
140
|
-
morphChildren(target, next, ctx);
|
|
120
|
+
morphChildren(target, next, context(target, next, o));
|
|
141
121
|
};
|
|
142
122
|
export {
|
|
143
123
|
activateScripts,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/plugins/server-events/morph.ts"],
|
|
4
|
-
"sourcesContent": ["// Id-aware DOM morphing: update `target` in place to match `next`, keeping elements whose ids\n// appear in both trees (and their state: focus, input values, scroll) instead of recreating them.\n\nexport type MorphOptions = {\n /** Attribute that marks a subtree to leave alone when present on both sides. */\n ignoreAttr?: string;\n /** Attribute listing space-separated attribute names to keep from the old element. */\n preserveAttr?: string;\n};\n\ntype Ctx = { keep:
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": []
|
|
4
|
+
"sourcesContent": ["// Id-aware DOM morphing: update `target` in place to match `next`, keeping elements whose ids\n// appear in both trees (and their state: focus, input values, scroll) instead of recreating them.\n\nexport type MorphOptions = {\n /** Attribute that marks a subtree to leave alone when present on both sides. */\n ignoreAttr?: string;\n /** Attribute listing space-separated attribute names to keep from the old element. */\n preserveAttr?: string;\n};\n\n/** `keep`: id \u2192 the old element to reuse wherever the new tree wants that id. */\ntype Ctx = { keep: Map<string, Element>; ignore: string; preserve: string };\n\nconst ran = new WeakSet<Node>();\nconst isEl = (n: Node | null): n is Element => n?.nodeType === 1;\n\n/** Scripts inserted through the DOM do not execute; replace them with fresh copies that do. */\nexport const activateScripts = (root: Node): void => {\n if (!isEl(root)) return;\n for (const old of [root, ...root.querySelectorAll('script')] as HTMLScriptElement[]) {\n if (!old.matches('script') || ran.has(old)) continue;\n const s = document.createElement('script');\n for (const { name, value } of old.attributes) s.setAttribute(name, value);\n s.text = old.text;\n ran.add(s);\n old.replaceWith(s);\n }\n};\n\n/** Import `b` into the document, `insert` it, and run its scripts. */\nconst fresh = (b: Node, insert: (n: Node) => void): void => {\n const n = document.importNode(b, true);\n insert(n);\n activateScripts(n);\n};\n\nconst idsIn = (n: Node): Element[] =>\n [n, ...((n as ParentNode).querySelectorAll?.('[id]') ?? [])].filter((e) => isEl(e) && e.id) as Element[];\nconst containsKept = (n: Node, ctx: Ctx): boolean => idsIn(n).some((e) => ctx.keep.has(e.id));\n\nconst place = (parent: Node, node: Node, before: Node | null): void => {\n try {\n (parent as any).moveBefore(node, before);\n } catch {\n parent.insertBefore(node, before);\n }\n};\n\n/** Same kind of node, and an old element with an id is only reused for the same id. */\nconst compatible = (old: Node, next: Node): boolean =>\n old.nodeName === next.nodeName && (!isEl(old) || !old.id || old.id === (next as Element).id);\n\nconst syncAttributes = (a: Element, b: Element, ctx: Ctx): void => {\n const keep = new Set((b.getAttribute(ctx.preserve) ?? a.getAttribute(ctx.preserve) ?? '').split(/\\s+/));\n const differs = (name: string) => !keep.has(name) && a.getAttribute(name) !== b.getAttribute(name);\n let changed = false;\n // Live form state (what the user typed or ticked) is only overwritten when the server's\n // default actually changed, i.e. the *attribute* differs between old and new markup.\n const live =\n a instanceof HTMLInputElement && a.type !== 'file'\n ? ['value', 'checked']\n : a instanceof HTMLOptionElement\n ? ['selected']\n : [];\n for (const name of live) {\n if (differs(name)) {\n (a as any)[name] = name === 'value' ? (b.getAttribute(name) ?? '') : b.hasAttribute(name);\n changed = true;\n }\n }\n if (\n a instanceof HTMLTextAreaElement &&\n !keep.has('value') &&\n a.defaultValue !== (b as HTMLTextAreaElement).defaultValue\n ) {\n a.value = (b as HTMLTextAreaElement).defaultValue;\n changed = true;\n }\n for (const { name, value } of b.attributes)\n if (!keep.has(name) && a.getAttribute(name) !== value) a.setAttribute(name, value);\n for (const { name } of [...a.attributes]) if (!keep.has(name) && !b.hasAttribute(name)) a.removeAttribute(name);\n\n if (changed)\n (a instanceof HTMLOptionElement ? a.closest('select') : a)?.dispatchEvent(\n new Event('sigmx-prop-change', { bubbles: true }),\n );\n};\n\nconst morphNode = (a: Node, b: Node, ctx: Ctx): void => {\n if (a.nodeName !== b.nodeName) {\n fresh(b, (n) => (a as ChildNode).replaceWith(n));\n return;\n }\n if (!isEl(a) || !isEl(b)) {\n if (a.nodeValue !== b.nodeValue) a.nodeValue = b.nodeValue;\n return;\n }\n // A changed script must run again; patching its text or src in place would not execute it.\n if (b.localName === 'script' && !a.isEqualNode(b)) {\n fresh(b, (n) => a.replaceWith(n));\n return;\n }\n if (a.hasAttribute(ctx.ignore) && b.hasAttribute(ctx.ignore)) return;\n syncAttributes(a, b, ctx);\n if (a instanceof HTMLTemplateElement) a.innerHTML = b.innerHTML;\n else if (!a.isEqualNode(b)) morphChildren(a, b, ctx);\n};\n\nconst morphChildren = (parent: Node, next: Node, ctx: Ctx): void => {\n let cur: Node | null = parent.firstChild;\n /**\n * Remove old siblings from `cur` up to (not including) `stop`; returns whether `stop` was reached.\n * Kept elements inside removed subtrees stay referenced by `ctx.keep` and are pulled back in later.\n */\n const dropUntil = (stop: Node | null): boolean => {\n while (cur && cur !== stop) {\n const n = cur;\n cur = cur.nextSibling;\n // A kept element stays connected until `place` moves it, so focus, media and iframe state survive.\n if (!(isEl(n) && ctx.keep.get(n.id) === n)) (n as ChildNode).remove();\n }\n return cur === stop;\n };\n for (const nb of [...next.childNodes]) {\n const kept = isEl(nb) && ctx.keep.get(nb.id);\n if (kept) {\n // Ahead among the old siblings: drop what precedes it. Elsewhere (moved, or inside a removed\n // subtree): pull it in here and leave the remaining old siblings for the next new children.\n let m: Node | null = cur;\n while (m && m !== kept) m = m.nextSibling;\n if (m) {\n dropUntil(kept);\n cur = kept.nextSibling;\n } else place(parent, kept, cur);\n morphNode(kept, nb, ctx);\n continue;\n }\n let m: Node | null = cur;\n while (m && !compatible(m, nb)) m = m.nextSibling;\n if (m) {\n dropUntil(m);\n cur = m.nextSibling;\n morphNode(m, nb, ctx);\n } else if (isEl(nb) && containsKept(nb, ctx)) {\n // Build a shell (same element, attributes, no children) so the wanted descendants can be pulled in.\n const shell = document.importNode(nb, false);\n parent.insertBefore(shell, cur);\n morphNode(shell, nb, ctx);\n } else {\n fresh(nb, (n) => parent.insertBefore(n, cur));\n }\n }\n dropUntil(null);\n};\n\nconst context = (target: Node, next: Node, o: MorphOptions): Ctx => {\n const wanted = new Map(idsIn(next).map((e) => [e.id, e.tagName]));\n return {\n keep: new Map(idsIn(target).flatMap((e) => (wanted.get(e.id) === e.tagName ? [[e.id, e] as const] : []))),\n ignore: o.ignoreAttr ?? 'data-ignore-morph',\n preserve: o.preserveAttr ?? 'data-preserve-attr',\n };\n};\n\nexport const morph = (target: Element, next: Element, o: MorphOptions = {}): void => {\n morphNode(target, next, context(target, next, o));\n};\n\nexport const morphInner = (target: Element, next: ParentNode, o: MorphOptions = {}): void => {\n morphChildren(target, next, context(target, next, o));\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["m"]
|
|
7
7
|
}
|