jsmdcui 0.3.0 → 0.5.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/src/cui/rpc.mjs CHANGED
@@ -9,6 +9,138 @@ let apilist = new Map()
9
9
 
10
10
  export const jss = JSON.stringify
11
11
 
12
+ function parseDollarIdentity(input, { selector = false } = {})
13
+ {
14
+ const text = String(input ?? "").trim();
15
+ const match = text.match(/^([A-Za-z_][\w:-]*)?(?:#([A-Za-z_][\w:-]*))?((?:\.[A-Za-z_][\w:-]*)*)$/);
16
+ if (!match || (!match[1] && !match[2] && !match[3])) return null;
17
+ if (!selector && !match[1]) return null;
18
+ return {
19
+ tag: match[1] || null,
20
+ id: match[2] || null,
21
+ classes: match[3] ? match[3].slice(1).split(".") : [],
22
+ };
23
+ }
24
+
25
+ function matchesDollarIdentity(identity, selector)
26
+ {
27
+ if (selector.tag && identity.tag !== selector.tag) return false;
28
+ if (selector.id && identity.id !== selector.id) return false;
29
+ return selector.classes.every(name => identity.classes.includes(name));
30
+ }
31
+
32
+ function findMarkdownCodeElement(documentObject, selector)
33
+ {
34
+ for (const code of documentObject?.querySelectorAll?.("pre > code") ?? []) {
35
+ for (const className of code.classList ?? []) {
36
+ if (!className.startsWith("language-")) continue;
37
+ const identity = parseDollarIdentity(className.slice("language-".length));
38
+ if (identity && matchesDollarIdentity(identity, selector)) return code;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
44
+ function findWebDollarElement(documentObject, selectorText, selector)
45
+ {
46
+ try {
47
+ const direct = documentObject?.querySelector?.(String(selectorText));
48
+ if (direct) return direct;
49
+ } catch {}
50
+
51
+ for (const element of documentObject?.querySelectorAll?.("[data-mdcui-tag]") ?? []) {
52
+ const identity = {
53
+ tag: element.getAttribute?.("data-mdcui-tag") || null,
54
+ id: element.id || null,
55
+ classes: [...(element.classList ?? [])],
56
+ };
57
+ if (matchesDollarIdentity(identity, selector)) return element;
58
+ }
59
+
60
+ return findMarkdownCodeElement(documentObject, selector);
61
+ }
62
+
63
+ function webDollarValue(element)
64
+ {
65
+ if (element && "value" in element) return String(element.value ?? "");
66
+ return String(element?.textContent ?? "").replace(/\n$/, "");
67
+ }
68
+
69
+ function resizeWebTextarea(element)
70
+ {
71
+ if (!element || String(element.tagName ?? "").toLowerCase() !== "textarea") return;
72
+ try {
73
+ element.style.height = "auto";
74
+ element.style.height = `${element.scrollHeight}px`;
75
+ const lineHeight = Number.parseFloat(
76
+ element.ownerDocument?.defaultView?.getComputedStyle?.(element)?.lineHeight,
77
+ );
78
+ if (Number.isFinite(lineHeight) && lineHeight > 0)
79
+ element.rows = Math.max(1, Math.ceil(element.scrollHeight / lineHeight));
80
+ } catch {}
81
+ }
82
+
83
+ function installWebTextareaResize(target)
84
+ {
85
+ const documentObject = target?.document;
86
+ if (!documentObject || documentObject.__mdcuiTextareaResizeInstalled) return;
87
+ documentObject.__mdcuiTextareaResizeInstalled = true;
88
+ const resizeAll = () => {
89
+ for (const element of documentObject.querySelectorAll?.("textarea[data-mdcui-tag]") ?? [])
90
+ resizeWebTextarea(element);
91
+ };
92
+ documentObject.addEventListener?.("input", event => {
93
+ if (event.target?.matches?.("textarea[data-mdcui-tag]"))
94
+ resizeWebTextarea(event.target);
95
+ });
96
+ target.addEventListener?.("resize", resizeAll);
97
+ if (documentObject.readyState === "loading")
98
+ documentObject.addEventListener?.("DOMContentLoaded", resizeAll, { once: true });
99
+ else
100
+ queueMicrotask(resizeAll);
101
+ }
102
+
103
+ export function createWebDollar(documentObject = globalThis.document)
104
+ {
105
+ return function $(selectorText) {
106
+ const selector = parseDollarIdentity(selectorText, { selector: true });
107
+ const selection = {
108
+ val(...args) {
109
+ try {
110
+ if (!selector) return args.length > 0 ? selection : "";
111
+ const element = findWebDollarElement(documentObject, selectorText, selector);
112
+ if (!element) return args.length > 0 ? selection : "";
113
+ if (args.length > 0) {
114
+ const value = String(args[0] ?? "");
115
+ if ("value" in element) {
116
+ element.value = value;
117
+ resizeWebTextarea(element);
118
+ }
119
+ else element.textContent = value;
120
+ return selection;
121
+ }
122
+ return webDollarValue(element);
123
+ } catch {
124
+ return args.length > 0 ? selection : "";
125
+ }
126
+ },
127
+ };
128
+ return selection;
129
+ };
130
+ }
131
+
132
+ export function installWebDollar(target = globalThis)
133
+ {
134
+ if (!target?.document) return target?.$;
135
+ const $ = createWebDollar(target.document);
136
+ target.$ = $;
137
+ installWebTextareaResize(target);
138
+ return $;
139
+ }
140
+
141
+ if (typeof globalThis.document !== "undefined")
142
+ installWebDollar(globalThis);
143
+
12
144
 
13
145
  export async function evalBack(backmod, qjson)
14
146
  {
@@ -99,8 +231,11 @@ try{
99
231
 
100
232
  text = text.replace(/^javascript:/, "");
101
233
 
102
- const names = Object.keys(mod);
103
- const values = Object.values(mod).map(safeFrontValue);
234
+ const entries = Object.entries(mod).filter(([name]) => name !== "$");
235
+ if (typeof globalThis.$ === "function")
236
+ entries.push(["$", globalThis.$]);
237
+ const names = entries.map(([name]) => name);
238
+ const values = entries.map(([, value]) => safeFrontValue(value));
104
239
 
105
240
  try {
106
241
  return await new AsyncFunction(...names, `return await (${text})`)(...values);