jsmdcui 0.10.1 → 0.11.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.
@@ -0,0 +1,623 @@
1
+
2
+
3
+
4
+
5
+
6
+ const crlf="\r\n"
7
+
8
+ let apilist = new Map()
9
+
10
+ export const jss = JSON.stringify
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 isWebHeading(element)
70
+ {
71
+ return /^h[1-6]$/i.test(String(element?.tagName ?? ""));
72
+ }
73
+
74
+ function firstHeadingList(heading)
75
+ {
76
+ for (let sibling = heading?.nextElementSibling; sibling; sibling = sibling.nextElementSibling) {
77
+ if (isWebHeading(sibling) || String(sibling.tagName ?? "").toLowerCase() === "section")
78
+ break;
79
+ if (sibling.matches?.("ul, ol")) return sibling;
80
+ const list = sibling.querySelector?.("ul, ol");
81
+ if (list) return list;
82
+ }
83
+ return null;
84
+ }
85
+
86
+ function directTaskCheckbox(item)
87
+ {
88
+ for (const checkbox of item?.querySelectorAll?.('input[type="checkbox"]') ?? []) {
89
+ if (checkbox.closest?.("li.task-list-item") === item) return checkbox;
90
+ }
91
+ return null;
92
+ }
93
+
94
+ function webTaskItemValue(item, checkbox)
95
+ {
96
+ const label = checkbox?.closest?.("label");
97
+ if (label && label.closest?.("li.task-list-item") === item)
98
+ return String(label.textContent ?? "").trim();
99
+
100
+ const copy = item.cloneNode?.(true);
101
+ for (const nested of copy?.querySelectorAll?.("ul, ol") ?? []) nested.remove?.();
102
+ for (const input of copy?.querySelectorAll?.('input[type="checkbox"]') ?? []) input.remove?.();
103
+ return String(copy?.textContent ?? "").trim();
104
+ }
105
+
106
+ function webHeadingValue(heading)
107
+ {
108
+ const single = String(heading?.id ?? "").startsWith("select");
109
+ const list = firstHeadingList(heading);
110
+ if (!list) return single ? null : [];
111
+
112
+ const selected = [];
113
+ for (const item of list.children ?? []) {
114
+ if (!item.matches?.("li.task-list-item")) continue;
115
+ const checkbox = directTaskCheckbox(item);
116
+ if (!checkbox?.checked) continue;
117
+ const value = webTaskItemValue(item, checkbox);
118
+ if (single) return value;
119
+ selected.push(value);
120
+ }
121
+ return single ? null : selected;
122
+ }
123
+
124
+ function directWebTaskItems(list)
125
+ {
126
+ const items = [];
127
+ for (const item of list?.children ?? []) {
128
+ if (!item.matches?.("li.task-list-item")) continue;
129
+ if (directTaskCheckbox(item)) items.push(item);
130
+ }
131
+ return items;
132
+ }
133
+
134
+ function webTaskItemSnapshot(item)
135
+ {
136
+ const checkbox = directTaskCheckbox(item);
137
+ return {
138
+ value: webTaskItemValue(item, checkbox),
139
+ checked: Boolean(checkbox?.checked),
140
+ };
141
+ }
142
+
143
+ function normalizedTaskItem(input)
144
+ {
145
+ if (input && typeof input === "object") {
146
+ return {
147
+ value: String(input.value ?? input.label ?? "").replace(/\r?\n/g, " "),
148
+ checked: Boolean(input.checked),
149
+ };
150
+ }
151
+ return {
152
+ value: String(input ?? "").replace(/\r?\n/g, " "),
153
+ checked: false,
154
+ };
155
+ }
156
+
157
+ function normalizedSpliceRange(length, argumentCount, start, deleteCount)
158
+ {
159
+ if (argumentCount === 0) return { start: 0, deleteCount: 0 };
160
+ let relativeStart = Number(start);
161
+ if (Number.isNaN(relativeStart)) relativeStart = 0;
162
+ relativeStart = Math.trunc(relativeStart);
163
+ const actualStart = relativeStart < 0
164
+ ? Math.max(length + relativeStart, 0)
165
+ : Math.min(relativeStart, length);
166
+ if (argumentCount === 1) return { start: actualStart, deleteCount: length - actualStart };
167
+ let requestedDelete = Number(deleteCount);
168
+ if (Number.isNaN(requestedDelete)) requestedDelete = 0;
169
+ requestedDelete = Math.max(0, Math.trunc(requestedDelete));
170
+ return {
171
+ start: actualStart,
172
+ deleteCount: Math.min(requestedDelete, length - actualStart),
173
+ };
174
+ }
175
+
176
+ function appendWebTaskItem(list, input, before = null)
177
+ {
178
+ const documentObject = list?.ownerDocument;
179
+ if (!documentObject?.createElement || !documentObject?.createTextNode) return false;
180
+
181
+ const itemValue = normalizedTaskItem(input);
182
+ const item = documentObject.createElement("li");
183
+ item.classList?.add("task-list-item");
184
+ const label = documentObject.createElement("label");
185
+ const checkbox = documentObject.createElement("input");
186
+ checkbox.type = "checkbox";
187
+ checkbox.classList?.add("task-list-item-checkbox");
188
+ checkbox.checked = itemValue.checked;
189
+ label.append(checkbox, documentObject.createTextNode(itemValue.value));
190
+ item.append(label);
191
+ list.insertBefore(item, before);
192
+ return true;
193
+ }
194
+
195
+ function mutateWebHeadingList(heading, method, args)
196
+ {
197
+ const list = firstHeadingList(heading);
198
+ if (!list) {
199
+ if (method === "push" || method === "unshift") return 0;
200
+ if (method === "splice") return [];
201
+ return undefined;
202
+ }
203
+
204
+ const items = directWebTaskItems(list);
205
+ if (method === "splice") {
206
+ const range = normalizedSpliceRange(items.length, args.length, args[0], args[1]);
207
+ const removed = items
208
+ .slice(range.start, range.start + range.deleteCount)
209
+ .map((item) => webTaskItemValue(item, directTaskCheckbox(item)));
210
+ const before = items[range.start] ?? null;
211
+ for (const input of args.slice(2)) appendWebTaskItem(list, input, before);
212
+ for (const item of items.slice(range.start, range.start + range.deleteCount)) item.remove?.();
213
+ return removed;
214
+ }
215
+ if (method === "pop" || method === "shift") {
216
+ const item = method === "pop" ? items.at(-1) : items[0];
217
+ if (!item) return undefined;
218
+ const value = webTaskItemValue(item, directTaskCheckbox(item));
219
+ item.remove?.();
220
+ return value;
221
+ }
222
+
223
+ if (method === "push") {
224
+ for (const input of args) appendWebTaskItem(list, input);
225
+ } else {
226
+ const before = items[0] ?? list.firstChild ?? null;
227
+ for (const input of args) appendWebTaskItem(list, input, before);
228
+ }
229
+ return directWebTaskItems(list).length;
230
+ }
231
+
232
+ function resizeWebTextarea(element)
233
+ {
234
+ if (!element || String(element.tagName ?? "").toLowerCase() !== "textarea") return;
235
+ try {
236
+ element.style.height = "auto";
237
+ element.style.height = `${element.scrollHeight}px`;
238
+ const lineHeight = Number.parseFloat(
239
+ element.ownerDocument?.defaultView?.getComputedStyle?.(element)?.lineHeight,
240
+ );
241
+ if (Number.isFinite(lineHeight) && lineHeight > 0)
242
+ element.rows = Math.max(1, Math.ceil(element.scrollHeight / lineHeight));
243
+ } catch {}
244
+ }
245
+
246
+ function installWebTextareaResize(target)
247
+ {
248
+ const documentObject = target?.document;
249
+ if (!documentObject || documentObject.__mdcuiTextareaResizeInstalled) return;
250
+ documentObject.__mdcuiTextareaResizeInstalled = true;
251
+ const resizeAll = () => {
252
+ for (const element of documentObject.querySelectorAll?.("textarea[data-mdcui-tag]") ?? [])
253
+ resizeWebTextarea(element);
254
+ };
255
+ documentObject.addEventListener?.("input", event => {
256
+ if (event.target?.matches?.("textarea[data-mdcui-tag]"))
257
+ resizeWebTextarea(event.target);
258
+ });
259
+ target.addEventListener?.("resize", resizeAll);
260
+ if (documentObject.readyState === "loading")
261
+ documentObject.addEventListener?.("DOMContentLoaded", resizeAll, { once: true });
262
+ else
263
+ queueMicrotask(resizeAll);
264
+ }
265
+
266
+ export function createWebDollar(documentObject = globalThis.document)
267
+ {
268
+ return function $(selectorText) {
269
+ const selector = parseDollarIdentity(selectorText, { selector: true });
270
+ const selection = {
271
+ html() {
272
+ try {
273
+ const element = findWebDollarElement(documentObject, selectorText, selector);
274
+ return element ? String(element.innerHTML ?? "") : "";
275
+ } catch {
276
+ return "";
277
+ }
278
+ },
279
+ val(...args) {
280
+ try {
281
+ const element = findWebDollarElement(documentObject, selectorText, selector);
282
+ if (!element) return args.length > 0 ? selection : "";
283
+ if (isWebHeading(element)) {
284
+ if (args.length > 0) return selection;
285
+ return webHeadingValue(element);
286
+ }
287
+ if (!selector) return args.length > 0 ? selection : "";
288
+ if (args.length > 0) {
289
+ const value = String(args[0] ?? "");
290
+ if ("value" in element) {
291
+ element.value = value;
292
+ resizeWebTextarea(element);
293
+ }
294
+ else element.textContent = value;
295
+ return selection;
296
+ }
297
+ return webDollarValue(element);
298
+ } catch {
299
+ return args.length > 0 ? selection : "";
300
+ }
301
+ },
302
+ push(...items) {
303
+ try {
304
+ const element = findWebDollarElement(documentObject, selectorText, selector);
305
+ return isWebHeading(element) ? mutateWebHeadingList(element, "push", items) : 0;
306
+ } catch {
307
+ return 0;
308
+ }
309
+ },
310
+ pop() {
311
+ try {
312
+ const element = findWebDollarElement(documentObject, selectorText, selector);
313
+ return isWebHeading(element) ? mutateWebHeadingList(element, "pop", []) : undefined;
314
+ } catch {
315
+ return undefined;
316
+ }
317
+ },
318
+ shift() {
319
+ try {
320
+ const element = findWebDollarElement(documentObject, selectorText, selector);
321
+ return isWebHeading(element) ? mutateWebHeadingList(element, "shift", []) : undefined;
322
+ } catch {
323
+ return undefined;
324
+ }
325
+ },
326
+ unshift(...items) {
327
+ try {
328
+ const element = findWebDollarElement(documentObject, selectorText, selector);
329
+ return isWebHeading(element) ? mutateWebHeadingList(element, "unshift", items) : 0;
330
+ } catch {
331
+ return 0;
332
+ }
333
+ },
334
+ splice(...args) {
335
+ try {
336
+ const element = findWebDollarElement(documentObject, selectorText, selector);
337
+ return isWebHeading(element) ? mutateWebHeadingList(element, "splice", args) : [];
338
+ } catch {
339
+ return [];
340
+ }
341
+ },
342
+ slice(...args) {
343
+ try {
344
+ const element = findWebDollarElement(documentObject, selectorText, selector);
345
+ if (!isWebHeading(element)) return [];
346
+ const list = firstHeadingList(element);
347
+ if (!list) return [];
348
+ return directWebTaskItems(list).map(webTaskItemSnapshot).slice(...args);
349
+ } catch {
350
+ return [];
351
+ }
352
+ },
353
+ };
354
+ return selection;
355
+ };
356
+ }
357
+
358
+ export function installWebDollar(target = globalThis)
359
+ {
360
+ if (!target?.document) return target?.$;
361
+ const $ = createWebDollar(target.document);
362
+ target.$ = $;
363
+ installWebTextareaResize(target);
364
+ return $;
365
+ }
366
+
367
+ if (typeof globalThis.document !== "undefined")
368
+ installWebDollar(globalThis);
369
+
370
+
371
+ export async function evalBack(backmod, qjson)
372
+ {
373
+
374
+ try{
375
+
376
+
377
+
378
+ /* Contract of requestJson(qjson)
379
+ type RpcPacket = [
380
+ func: string,
381
+ argv: unknown[],
382
+ envp?: Record<string, unknown>,
383
+ ];
384
+ */
385
+
386
+ // DiscoverApi ApiCaller
387
+
388
+
389
+ let [ func, argv, envp ] = qjson
390
+
391
+ func = (func || '')+'' ;
392
+
393
+ if(process.env.RPC_DEBUG)
394
+ console.log(func);
395
+
396
+ if(!apilist.get(backmod))
397
+ apilist.set(backmod,DiscoverApi(backmod));
398
+
399
+ const apilistMod = apilist.get(backmod)
400
+
401
+ if(func=="_discover")
402
+ {
403
+ return apilistMod ;
404
+ }
405
+ else if(apilistMod[func])
406
+ {
407
+ if(Array.isArray(argv))
408
+ return await backmod[func]?.apply?.(envp,argv);
409
+ else
410
+ return await ApiCaller(backmod,func,argv,envp);
411
+ }
412
+ else
413
+ {
414
+ return "Unknown func 未知函式: "+func+
415
+ crlf+JSON.stringify(argv);
416
+ }
417
+
418
+
419
+
420
+ }catch(e)
421
+ {
422
+ console.log(e);
423
+ return (e.stack);
424
+ }
425
+
426
+ } // end of evalBack
427
+
428
+
429
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
430
+
431
+ function safeFrontError(e)
432
+ {
433
+ return {
434
+ ok: false,
435
+ error: e?.stack || String(e),
436
+ };
437
+ }
438
+
439
+ function safeFrontValue(value)
440
+ {
441
+ if (typeof value !== "function") return value;
442
+ return function(...args) {
443
+ try {
444
+ const result = value.apply(this, args);
445
+ if (result && typeof result.then === "function")
446
+ return result.catch(safeFrontError);
447
+ return result;
448
+ } catch(e) {
449
+ return safeFrontError(e);
450
+ }
451
+ };
452
+ }
453
+
454
+ export async function evalFront(mod, text, scope = {})
455
+ {
456
+ try{
457
+
458
+ text = text.replace(/^javascript:/, "");
459
+
460
+ const entryMap = new Map(Object.entries(mod).filter(([name]) => name !== "$"));
461
+ if (typeof globalThis.$ === "function")
462
+ entryMap.set("$", globalThis.$);
463
+ for (const [name, value] of Object.entries(scope))
464
+ entryMap.set(name, value);
465
+ const entries = [...entryMap];
466
+ const names = entries.map(([name]) => name);
467
+ const values = entries.map(([, value]) => safeFrontValue(value));
468
+
469
+ try {
470
+ return await new AsyncFunction(...names, `return await (${text})`)(...values);
471
+ } catch(e) {
472
+ if (e instanceof SyntaxError)
473
+ return await new AsyncFunction(...names, text)(...values);
474
+ throw e;
475
+ }
476
+
477
+ }catch(e)
478
+ {
479
+ return safeFrontError(e);
480
+ }
481
+
482
+ }
483
+
484
+
485
+ export const rpcraw = async (func,argv,envp)=>{
486
+
487
+ const apilistMod = await FrontendDiscoverApi()
488
+
489
+ if(apilistMod[func])
490
+ return await fetch("rpc", {
491
+ method: "POST",
492
+ body: JSON.stringify([
493
+ func,argv,envp
494
+ ])
495
+ }).then(r=>r.json()).catch(e=>e) ;
496
+ else if(func=="_discover")
497
+ {
498
+ if(argv[0])
499
+ return jss(apilistMod,null,1) ;
500
+ else
501
+ return apilistMod ;
502
+ }
503
+ else
504
+ return "Unknown func 未知函式: "+func+
505
+ crlf+JSON.stringify(argv);
506
+ }
507
+
508
+ export const rpcproxy = new Proxy(rpcraw,{
509
+ get(target, prop, receiver) {
510
+ if (prop in target) {
511
+ return Reflect.get(target, prop, receiver);
512
+ }
513
+
514
+ if (typeof prop !== "string") {
515
+ return undefined;
516
+ }
517
+
518
+ target[prop] = async function(...argv) {
519
+ await FrontendDiscoverApi()
520
+ return await target(prop, argv, this);
521
+ };
522
+
523
+ return target[prop];
524
+ },
525
+ ownKeys() {
526
+ return Object.keys(apilist.get(0)||{});
527
+ },
528
+ getOwnPropertyDescriptor(target, prop) {
529
+ const apilistMod = apilist.get(0)||{};
530
+
531
+ if(prop in apilistMod) {
532
+ return {
533
+ enumerable: true,
534
+ configurable: true,
535
+ };
536
+ }
537
+
538
+ return Reflect.getOwnPropertyDescriptor(target, prop);
539
+ }
540
+ });
541
+
542
+ export var rpc = rpcproxy
543
+
544
+
545
+
546
+ // functions cannot contain ( ) ,
547
+ export function getfuncparams( func )
548
+ {
549
+ if( typeof(func) != "function" )
550
+ return [];
551
+
552
+ let rs = func.toString();
553
+ let m = rs.match( /\(([\s\S]*?)\)/ );
554
+ if( !m ) return [];
555
+ rs = m[1] ;
556
+
557
+ return rs.split(",").map(i=>i.trim()) ;
558
+ }
559
+
560
+
561
+ export async function FrontendDiscoverApi()
562
+ {
563
+ if(!apilist.get(0))
564
+ apilist.set(0, await fetch("rpc", {
565
+ method: "POST",
566
+ body: JSON.stringify(["_discover"])
567
+ }).then(r=>r.json()).catch(e=>e) ) ;
568
+
569
+ return apilist.get(0);
570
+ }
571
+
572
+
573
+ // filters out _ starting functions
574
+ export function DiscoverApi( obj ) // module_obj
575
+ {
576
+ let karr = Object.keys( obj );
577
+ karr = karr
578
+ .filter( i=>( !i.startsWith('_') &&
579
+ typeof(obj[i]) == "function" ) )
580
+ .map(i=>{
581
+ const fname = obj[i].name || obj[i].Name ;
582
+
583
+ if( fname && fname != i )
584
+ return [ i, [[ fname ]] ];
585
+
586
+ return [ i, getfuncparams(obj[i]) ];
587
+ });
588
+
589
+ return Object.fromEntries( karr ) ;
590
+ }
591
+
592
+
593
+
594
+ /*
595
+ * Module object from import * as modobj from 'mod.mjs'
596
+ * Parameters object
597
+ */
598
+ export async function ApiCaller( modobj, cmd, pobj,envp )
599
+ {
600
+ const func = modobj[cmd] ;
601
+
602
+ if(!pobj) pobj = {} ;
603
+
604
+ if( func && typeof(func) == "function" )
605
+ {
606
+ if(!func.args)
607
+ {
608
+ func.args = getfuncparams( func ) ;
609
+ func.args = func.args.map(i=>{
610
+ let ret = i.split("=")[0]
611
+ ret=(ret||"")+""
612
+ return ret.trim();
613
+ });
614
+ }
615
+
616
+ let parr = func.args.map(i=>pobj[i]) ;
617
+
618
+ return await func.apply(envp,parr) ;
619
+ }
620
+ else
621
+ return "Failed: No such function as" +
622
+ crlf + cmd ;
623
+ }