unicode-input-toolconverter 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.
Files changed (123) hide show
  1. package/.babelrc.json +3 -0
  2. package/.editorconfig +16 -0
  3. package/.nojekyll +0 -0
  4. package/.nyc_output/out.json +173804 -0
  5. package/.nyc_output/processinfo/index.json +1 -0
  6. package/CHANGES.md +11 -0
  7. package/COPYING.LESSER.txt +180 -0
  8. package/COPYING.txt +687 -0
  9. package/README.md +65 -0
  10. package/_locales/en-US/messages.json +3126 -0
  11. package/_locales/hu-HU/messages.json +3126 -0
  12. package/_locales/pt-BR/messages.json +3126 -0
  13. package/_locales/sv-SE/messages.json +3126 -0
  14. package/babel.config.json +5 -0
  15. package/browser_action/characterSelection.js +91 -0
  16. package/browser_action/charrefConverters.js +389 -0
  17. package/browser_action/chartBuild.js +155 -0
  18. package/browser_action/encodingBehaviors.js +72 -0
  19. package/browser_action/encodings.js +97 -0
  20. package/browser_action/entities.js +12 -0
  21. package/browser_action/entityBehaviors.js +96 -0
  22. package/browser_action/index-es.html +15 -0
  23. package/browser_action/index-instrumented.html +16 -0
  24. package/browser_action/index-pages.html +16 -0
  25. package/browser_action/index.html +16 -0
  26. package/browser_action/index.iife.min.js +2 -0
  27. package/browser_action/index.iife.min.js.map +1 -0
  28. package/browser_action/index.instrumented.iife.min.js +2 -0
  29. package/browser_action/index.instrumented.iife.min.js.map +1 -0
  30. package/browser_action/index.js +95 -0
  31. package/browser_action/preferences/prefDefaults.js +52 -0
  32. package/browser_action/service-worker/sw-activateCallback.js +53 -0
  33. package/browser_action/service-worker/sw-locales.json +6 -0
  34. package/browser_action/service-worker/sw-resources.json +73 -0
  35. package/browser_action/service-worker/sw-unicode-data.json +3 -0
  36. package/browser_action/styles/unicode-dialog.css +391 -0
  37. package/browser_action/templateUtils/elements.js +12 -0
  38. package/browser_action/templateUtils/fill.js +3 -0
  39. package/browser_action/templateUtils/validation.js +6 -0
  40. package/browser_action/templates/chartBuild.js +185 -0
  41. package/browser_action/templates/index.js +1304 -0
  42. package/browser_action/templatesElementCustomization/widgets.js +56 -0
  43. package/browser_action/unicode/UnicodeConverter.js +793 -0
  44. package/browser_action/unicode/charrefunicodeDb.js +258 -0
  45. package/browser_action/unicode/getScriptInfoForCodePoint.js +646 -0
  46. package/browser_action/unicode/hangul.js +253 -0
  47. package/browser_action/unicode/lastScriptNames.json +5 -0
  48. package/browser_action/unicode/parseUnihanFromTextFileStrings.js +56 -0
  49. package/browser_action/unicode/unicodeFieldInfo.js +120 -0
  50. package/browser_action/unicode/unicodeScripts.js +4912 -0
  51. package/browser_action/unicode/unihan.js +129 -0
  52. package/browser_action/unicode/unihanDbPopulate.js +26 -0
  53. package/browser_action/unicodecharref.js +1480 -0
  54. package/browser_action/utils/DOMUtils.js +122 -0
  55. package/browser_action/utils/FetchUtils.js +19 -0
  56. package/browser_action/utils/TextUtils.js +28 -0
  57. package/browser_action/utils/TypedArrayUtils.js +21 -0
  58. package/browser_action/utils/semicolonSeparatedToArray.js +12 -0
  59. package/browser_action/utils/setupServiceWorker.js +193 -0
  60. package/cypress.config.js +21 -0
  61. package/download/entities/copyright-software-20021231.html +70 -0
  62. package/download/entities/html5-uppercase.ent +54 -0
  63. package/download/entities/htmlmathml-f.ent +2164 -0
  64. package/download/entities/isoamsa.ent +201 -0
  65. package/download/entities/isoamsb.ent +177 -0
  66. package/download/entities/isoamsc.ent +77 -0
  67. package/download/entities/isoamsn.ent +148 -0
  68. package/download/entities/isoamso.ent +107 -0
  69. package/download/entities/isoamsr.ent +238 -0
  70. package/download/entities/isobox.ent +95 -0
  71. package/download/entities/isocyr1.ent +122 -0
  72. package/download/entities/isocyr2.ent +81 -0
  73. package/download/entities/isodia.ent +69 -0
  74. package/download/entities/isogrk1.ent +104 -0
  75. package/download/entities/isogrk2.ent +75 -0
  76. package/download/entities/isogrk3.ent +98 -0
  77. package/download/entities/isogrk4.ent +98 -0
  78. package/download/entities/isolat1.ent +117 -0
  79. package/download/entities/isolat2.ent +176 -0
  80. package/download/entities/isomfrk.ent +107 -0
  81. package/download/entities/isomopf.ent +81 -0
  82. package/download/entities/isomscr.ent +107 -0
  83. package/download/entities/isonum.ent +131 -0
  84. package/download/entities/isopub.ent +140 -0
  85. package/download/entities/isotech.ent +216 -0
  86. package/download/entities/mmlalias.ent +598 -0
  87. package/download/entities/mmlextra.ent +154 -0
  88. package/download/entities/predefined.ent +52 -0
  89. package/download/entities/w3centities-f.ent +2276 -0
  90. package/download/entities/xhtml1-lat1.ent +143 -0
  91. package/download/entities/xhtml1-special.ent +78 -0
  92. package/download/entities/xhtml1-symbol.ent +171 -0
  93. package/eslint.config.js +66 -0
  94. package/icons/openWindow16.png +0 -0
  95. package/icons/openWindow24.png +0 -0
  96. package/lgtm.yml +5 -0
  97. package/lib/background.html +11 -0
  98. package/lib/background.js +13 -0
  99. package/lib/overlay.css +6 -0
  100. package/manifest.json +37 -0
  101. package/package.json +121 -0
  102. package/polyfills/browser-polyfill.min.js +8 -0
  103. package/polyfills/browser-polyfill.min.js.map +1 -0
  104. package/server.js +61 -0
  105. package/sw.js +259 -0
  106. package/tools/entities-import.js +62 -0
  107. package/tools/findEsResources.js +30 -0
  108. package/tools/list-locales.js +17 -0
  109. package/tools/parseUnicodeCharts.js +428 -0
  110. package/tools/ucd-import.js +24 -0
  111. package/tools/unicode-charts.html +596 -0
  112. package/tools/unihan-import.js +72 -0
  113. package/unicode_copyright.txt +46 -0
  114. package/vendor/camelcase/index.js +110 -0
  115. package/vendor/fflate/esm/browser.js +2665 -0
  116. package/vendor/intl-dom/dist/index.esm.js +2962 -0
  117. package/vendor/jamilih/dist/jml-es.js +2359 -0
  118. package/vendor/jquery/dist/jquery.js +10716 -0
  119. package/vendor/json-6/dist/index.mjs +1783 -0
  120. package/vendor/miller-columns/dist/index-es.min.js +1 -0
  121. package/vendor/miller-columns/miller-columns.css +75 -0
  122. package/vendor/simple-prefs/dist/index.esm.js +286 -0
  123. package/web-ext-config.cjs +13 -0
@@ -0,0 +1,2359 @@
1
+ var _win;
2
+ /*
3
+ Possible todos:
4
+ 0. Add XSLT to JML-string stylesheet (or even vice versa)
5
+ 0. IE problem: Add JsonML code to handle name attribute (during element creation)
6
+ 0. Element-specific: IE object-param handling
7
+
8
+ Todos inspired by JsonML: https://github.com/mckamey/jsonml/blob/master/jsonml-html.js
9
+
10
+ 0. duplicate attributes?
11
+ 0. expand ATTR_MAP
12
+ 0. equivalent of markup, to allow strings to be embedded within an object (e.g., {$value: '<div>id</div>'}); advantage over innerHTML in that it wouldn't need to work as the entire contents (nor destroy any existing content or handlers)
13
+ 0. More validation?
14
+ 0. JsonML DOM Level 0 listener
15
+ 0. Whitespace trimming?
16
+
17
+ JsonML element-specific:
18
+ 0. table appending
19
+ 0. canHaveChildren necessary? (attempts to append to script and img)
20
+
21
+ Other Todos:
22
+ 0. Note to self: Integrate research from other jml notes
23
+ 0. Allow Jamilih to be seeded with an existing element, so as to be able to add/modify attributes and children
24
+ 0. Allow array as single first argument
25
+ 0. Settle on whether need to use null as last argument to return array (or fragment) or other way to allow appending? Options object at end instead to indicate whether returning array, fragment, first element, etc.?
26
+ 0. Allow building of generic XML (pass configuration object)
27
+ 0. Allow building content internally as a string (though allowing DOM methods, etc.?)
28
+ 0. Support JsonML empty string element name to represent fragments?
29
+ 0. Redo browser testing of jml (including ensuring IE7 can work even if test framework can't work)
30
+ */
31
+
32
+ /**
33
+ * @typedef {Window & {DocumentFragment: any}} HTMLWindow
34
+ */
35
+
36
+ /**
37
+ * @typedef {any} ArbitraryValue
38
+ */
39
+
40
+ /**
41
+ * @typedef {number} Integer
42
+ */
43
+
44
+ /**
45
+ * @typedef {{
46
+ * element: Document|HTMLElement|DocumentFragment,
47
+ * attribute: {name: string|null, value: JamilihAttValue},
48
+ * opts: JamilihOptions
49
+ * }} PluginSettings
50
+ */
51
+
52
+ /**
53
+ * @typedef {object} JamilihPlugin
54
+ * @property {string} name
55
+ * @property {(opts: PluginSettings) => string|Promise<void>} set
56
+ */
57
+
58
+ /**
59
+ * @type {import('jsdom').DOMWindow|HTMLWindow|undefined}
60
+ */
61
+ let win;
62
+
63
+ /* c8 ignore next 3 */
64
+ if (typeof window !== 'undefined' && window) {
65
+ win = window;
66
+ }
67
+
68
+ /* c8 ignore next */
69
+ let doc = typeof document !== 'undefined' && document || ((_win = win) === null || _win === void 0 ? void 0 : _win.document);
70
+
71
+ // STATIC PROPERTIES
72
+
73
+ const possibleOptions = ['$plugins',
74
+ // '$mode', // Todo (SVG/XML)
75
+ // '$state', // Used internally
76
+ '$map' // Add any other options here
77
+ ];
78
+
79
+ const NS_HTML = 'http://www.w3.org/1999/xhtml',
80
+ hyphenForCamelCase = /-([a-z])/gu;
81
+ const ATTR_MAP = new Map([['maxlength', 'maxLength'], ['minlength', 'minLength'], ['readonly', 'readOnly']]);
82
+
83
+ // We define separately from ATTR_DOM for clarity (and parity with JsonML) but no current need
84
+ // We don't set attribute esp. for boolean atts as we want to allow setting of `undefined`
85
+ // (e.g., from an empty variable) on templates to have no effect
86
+ const BOOL_ATTS = ['checked', 'defaultChecked', 'defaultSelected', 'disabled', 'indeterminate', 'open',
87
+ // Dialog elements
88
+ 'readOnly', 'selected'];
89
+
90
+ // From JsonML
91
+ const ATTR_DOM = [...BOOL_ATTS, 'accessKey',
92
+ // HTMLElement
93
+ 'async', 'autocapitalize',
94
+ // HTMLElement
95
+ 'autofocus', 'contentEditable',
96
+ // HTMLElement through ElementContentEditable
97
+ 'defaultValue', 'defer', 'draggable',
98
+ // HTMLElement
99
+ 'formnovalidate', 'hidden',
100
+ // HTMLElement
101
+ 'innerText',
102
+ // HTMLElement
103
+ 'inputMode',
104
+ // HTMLElement through ElementContentEditable
105
+ 'ismap', 'multiple', 'novalidate', 'pattern', 'required', 'spellcheck',
106
+ // HTMLElement
107
+ 'translate',
108
+ // HTMLElement
109
+ 'value', 'willvalidate'];
110
+ // Todo: Add more to this as useful for templating
111
+ // to avoid setting through nullish value
112
+ const NULLABLES = ['autocomplete', 'dir',
113
+ // HTMLElement
114
+ 'integrity',
115
+ // script, link
116
+ 'lang',
117
+ // HTMLElement
118
+ 'max', 'min', 'minLength', 'maxLength', 'title' // HTMLElement
119
+ ];
120
+
121
+ /**
122
+ * @param {string} sel
123
+ * @returns {HTMLElement|null}
124
+ */
125
+ const $ = sel => {
126
+ if (!doc) {
127
+ throw new Error('No document object');
128
+ }
129
+ return doc.querySelector(sel);
130
+ };
131
+
132
+ /**
133
+ * @param {string} sel
134
+ * @returns {HTMLElement[]}
135
+ */
136
+ const $$ = sel => {
137
+ if (!doc) {
138
+ throw new Error('No document object');
139
+ }
140
+ return [... /** @type {NodeListOf<HTMLElement>} */doc.querySelectorAll(sel)];
141
+ };
142
+
143
+ /**
144
+ * Retrieve the (lower-cased) HTML name of a node.
145
+ * @static
146
+ * @param {Node} node The HTML node
147
+ * @returns {string} The lower-cased node name
148
+ */
149
+ function _getHTMLNodeName(node) {
150
+ return node.nodeName && node.nodeName.toLowerCase();
151
+ }
152
+
153
+ /**
154
+ * @private
155
+ * @static
156
+ * @param {Document|DocumentFragment|HTMLElement} parent The parent to which to append the element
157
+ * @param {Node|string} child The element or other node to append to the parent
158
+ * @throws {Error} Rethrow if problem with `append` and unhandled
159
+ * @returns {void}
160
+ */
161
+ function _appendNode(parent, child) {
162
+ const parentName = _getHTMLNodeName(parent);
163
+ if (parentName === 'template') {
164
+ /** @type {HTMLTemplateElement} */parent.content.append(child);
165
+ return;
166
+ }
167
+ parent.append(child); // IE9 is now ok with this
168
+ }
169
+
170
+ /**
171
+ * Attach event in a cross-browser fashion.
172
+ * @static
173
+ * @param {HTMLElement} el DOM element to which to attach the event
174
+ * @param {string} type The DOM event (without 'on') to attach to the element
175
+ * @param {(evt: Event & {target: HTMLElement}) => void} handler The event handler to attach to the element
176
+ * @param {boolean} [capturing] Whether or not the event should be
177
+ * capturing (W3C-browsers only); default is false; NOT IN USE
178
+ * @returns {void}
179
+ */
180
+ function _addEvent(el, type, handler, capturing) {
181
+ // @ts-expect-error It's ok
182
+ el.addEventListener(type, handler, Boolean(capturing));
183
+ }
184
+
185
+ /**
186
+ * Creates a text node of the result of resolving an entity or character reference.
187
+ * @param {'entity'|'decimal'|'hexadecimal'} type Type of reference
188
+ * @param {string} prefix Text to prefix immediately after the "&"
189
+ * @param {string} arg The body of the reference
190
+ * @throws {TypeError}
191
+ * @returns {Text} The text node of the resolved reference
192
+ */
193
+ function _createSafeReference(type, prefix, arg) {
194
+ /* c8 ignore next 3 */
195
+ if (!doc) {
196
+ throw new Error('No document defined');
197
+ }
198
+ // For security reasons related to innerHTML, we ensure this string only
199
+ // contains potential entity characters
200
+ if (!/^\w+$/u.test(arg)) {
201
+ throw new TypeError(`Bad ${type} reference; with prefix "${prefix}" and arg "${arg}"`);
202
+ }
203
+ const elContainer = doc.createElement('div');
204
+ // Todo: No workaround for XML?
205
+ // eslint-disable-next-line no-unsanitized/property
206
+ elContainer.innerHTML = '&' + prefix + arg + ';';
207
+ return doc.createTextNode(elContainer.innerHTML);
208
+ }
209
+
210
+ /**
211
+ * @param {string} n0 Whole expression match (including "-")
212
+ * @param {string} n1 Lower-case letter match
213
+ * @returns {string} Uppercased letter
214
+ */
215
+ function _upperCase(n0, n1) {
216
+ return n1.toUpperCase();
217
+ }
218
+
219
+ // Todo: Make as public utility
220
+ /**
221
+ * @param {ArbitraryValue} o
222
+ * @returns {boolean}
223
+ */
224
+ function _isNullish(o) {
225
+ return o === null || o === undefined;
226
+ }
227
+
228
+ // Todo: Make as public utility, but also return types for undefined, boolean, number, document, etc.
229
+ /**
230
+ * @private
231
+ * @static
232
+ * @param {string|JamilihAttributes|JamilihArray|JamilihChildren|
233
+ * JamilihDocumentFragment|JamilihAttributeNode|
234
+ * JamilihOptions|HTMLElement|Document|DocumentFragment|null|undefined} item
235
+ * @returns {"string"|"null"|"array"|"element"|"fragment"|"object"|
236
+ * "symbol"|"bigint"|"function"|"number"|"boolean"|"undefined"|
237
+ * "document"|"non-container node"}
238
+ */
239
+ function _getType(item) {
240
+ const type = typeof item;
241
+
242
+ // Appease TS
243
+ if (typeof item === 'string' || typeof item === 'undefined') {
244
+ return 'string';
245
+ }
246
+ switch (type) {
247
+ case 'object':
248
+ if (item === null) {
249
+ return 'null';
250
+ }
251
+ if (Array.isArray(item)) {
252
+ return 'array';
253
+ }
254
+ if ('nodeType' in item) {
255
+ switch (item.nodeType) {
256
+ case 1:
257
+ return 'element';
258
+ case 9:
259
+ return 'document';
260
+ case 11:
261
+ return 'fragment';
262
+ default:
263
+ return 'non-container node';
264
+ }
265
+ }
266
+ // Fallthrough
267
+ default:
268
+ return type;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * @private
274
+ * @static
275
+ * @param {DocumentFragment} frag
276
+ * @param {Node} node
277
+ * @returns {DocumentFragment}
278
+ */
279
+ function _fragReducer(frag, node) {
280
+ frag.append(node);
281
+ return frag;
282
+ }
283
+
284
+ /**
285
+ * @private
286
+ * @static
287
+ * @param {Object<string, string>} xmlnsObj
288
+ * @returns {(...n: string[]) => string}
289
+ */
290
+ function _replaceDefiner(xmlnsObj) {
291
+ /**
292
+ * @param {string[]} n
293
+ * @returns {string}
294
+ */
295
+ return function (...n) {
296
+ const n0 = n[0];
297
+ let retStr = xmlnsObj[''] ? ' xmlns="' + xmlnsObj[''] + '"' : n0; // Preserve XHTML
298
+ for (const [ns, xmlnsVal] of Object.entries(xmlnsObj)) {
299
+ if (ns !== '') {
300
+ retStr += ' xmlns:' + ns + '="' + xmlnsVal + '"';
301
+ }
302
+ }
303
+ return retStr;
304
+ };
305
+ }
306
+
307
+ /**
308
+ * @callback ChildrenToJMLCallback
309
+ * @param {JamilihArray|JamilihChildType|string} childNodeJML
310
+ * @param {Integer} i
311
+ * @returns {void}
312
+ */
313
+
314
+ /**
315
+ * @private
316
+ * @static
317
+ * @param {Node} node
318
+ * @returns {ChildrenToJMLCallback}
319
+ */
320
+ function _childrenToJML(node) {
321
+ return function (childNodeJML, i) {
322
+ const cn = node.childNodes[i];
323
+ const j = Array.isArray(childNodeJML) ? jml(... /** @type {JamilihArray} */childNodeJML) : jml(childNodeJML);
324
+ cn.replaceWith(j);
325
+ };
326
+ }
327
+
328
+ /**
329
+ * Keep this in sync with `JamilihArray`'s first argument (minus `Document`).
330
+ * @typedef {JamilihDoc|JamilihDoctype|JamilihTextNode|
331
+ * JamilihAttributeNode|JamilihOptions|ElementName|HTMLElement|
332
+ * JamilihDocumentFragment
333
+ * } JamilihFirstArg
334
+ */
335
+
336
+ /**
337
+ * @callback JamilihAppender
338
+ * @param {JamilihArray|JamilihFirstArg|Node|TextNodeString} childJML
339
+ * @returns {void}
340
+ */
341
+
342
+ /**
343
+ * @private
344
+ * @static
345
+ * @param {ParentNode} node
346
+ * @returns {JamilihAppender}
347
+ */
348
+ function _appendJML(node) {
349
+ return function (childJML) {
350
+ if (typeof childJML === 'string' || typeof childJML === 'number') {
351
+ throw new TypeError('Unexpected text string/number in the head');
352
+ }
353
+ if (Array.isArray(childJML)) {
354
+ node.append(jml(...childJML));
355
+ } else if (typeof childJML === 'object' && 'nodeType' in childJML) {
356
+ node.append(childJML);
357
+ } else {
358
+ node.append(jml(childJML));
359
+ }
360
+ };
361
+ }
362
+
363
+ /**
364
+ * @callback appender
365
+ * @param {JamilihArray|JamilihFirstArg|Node|TextNodeString} childJML
366
+ * @returns {void}
367
+ */
368
+
369
+ /**
370
+ * @private
371
+ * @static
372
+ * @param {ParentNode} node
373
+ * @returns {appender}
374
+ */
375
+ function _appendJMLOrText(node) {
376
+ return function (childJML) {
377
+ if (typeof childJML === 'string' || typeof childJML === 'number') {
378
+ node.append(String(childJML));
379
+ } else if (Array.isArray(childJML)) {
380
+ node.append(jml(...childJML));
381
+ } else if (typeof childJML === 'object' && 'nodeType' in childJML) {
382
+ node.append(childJML);
383
+ } else {
384
+ node.append(jml(childJML));
385
+ }
386
+ };
387
+ }
388
+
389
+ /**
390
+ * @private
391
+ * @static
392
+ */
393
+ /*
394
+ function _DOMfromJMLOrString (childNodeJML) {
395
+ if (typeof childNodeJML === 'string') {
396
+ return doc.createTextNode(childNodeJML);
397
+ }
398
+ return jml(...childNodeJML);
399
+ }
400
+ */
401
+
402
+ /**
403
+ * @typedef {HTMLElement|DocumentFragment|Comment|Attr|
404
+ * Text|Document|DocumentType|ProcessingInstruction|CDATASection} JamilihReturn
405
+ */
406
+ // 'string|JamilihOptions|JamilihDocumentFragment|JamilihAttributes|(string|JamilihArray)[]
407
+
408
+ /**
409
+ * Can either be an array of:
410
+ * 1. JamilihAttributes followed by an array of JamilihArrays or Elements.
411
+ * (Cannot be multiple single JamilihArrays despite TS type).
412
+ * 2. Any number of JamilihArrays.
413
+ * @typedef {[(JamilihAttributes|JamilihArray|JamilihArray[]|HTMLElement), ...(JamilihArray|JamilihArray[]|HTMLElement)[]]} TemplateJamilihArray
414
+ */
415
+
416
+ /**
417
+ * @typedef {(JamilihArray|HTMLElement)[]} ShadowRootJamilihArrayContainer
418
+ */
419
+
420
+ /**
421
+ * @typedef {{
422
+ * open?: boolean|ShadowRootJamilihArrayContainer,
423
+ * closed?: boolean|ShadowRootJamilihArrayContainer,
424
+ * template?: string|HTMLTemplateElement|TemplateJamilihArray,
425
+ * content?: ShadowRootJamilihArrayContainer|DocumentFragment
426
+ * }} JamilihShadowRootObject
427
+ */
428
+
429
+ /**
430
+ * @typedef {{[key: string]: string}} XmlnsAttributeObject
431
+ */
432
+
433
+ /**
434
+ * @typedef {null|XmlnsAttributeObject} XmlnsAttributeValue
435
+ */
436
+
437
+ /**
438
+ * @typedef {{
439
+ * [key: string]: string|number|null|undefined|DatasetAttributeObject
440
+ * }} DatasetAttributeObject
441
+ */
442
+
443
+ /**
444
+ * @typedef {string|undefined|{[key: string]: string|null}} StyleAttributeValue
445
+ */
446
+
447
+ /**
448
+ * @typedef {(this: HTMLElement, event: Event & {target: HTMLElement}) => void} EventHandler
449
+ */
450
+
451
+ /**
452
+ * @typedef {{
453
+ * [key: string]: EventHandler|[EventHandler, boolean]
454
+ * }} OnAttributeObject
455
+ */
456
+
457
+ /**
458
+ * @typedef {{
459
+ * $on?: OnAttributeObject|null
460
+ * }} OnAttribute
461
+ */
462
+
463
+ /**
464
+ * @typedef {boolean} BooleanAttribute
465
+ */
466
+
467
+ /**
468
+ * @typedef {((this: HTMLElement, event?: Event) => void)} HandlerAttributeValue
469
+ */
470
+
471
+ /* eslint-disable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
472
+ /**
473
+ * @typedef {{
474
+ * [key: string]: HandlerAttributeValue
475
+ * }} OnHandlerObject
476
+ */
477
+
478
+ /**
479
+ * @typedef {number} StringifiableNumber
480
+ */
481
+
482
+ /**
483
+ * @typedef {{
484
+ * name: string,
485
+ * systemId?: string,
486
+ * publicId?: string
487
+ * }} JamilihDocumentType
488
+ */
489
+
490
+ /**
491
+ * @typedef {string|{extends?: string}} DefineOptions
492
+ */
493
+
494
+ /**
495
+ * @typedef {{[key: string]: string|number|boolean|((this: DefineMixin, ...args: any[]) => any)}} DefineMixin
496
+ */
497
+
498
+ /**
499
+ * @typedef {{
500
+ * new (): HTMLElement;
501
+ * prototype: HTMLElement & {[key: string]: any}
502
+ * }} DefineConstructor
503
+ */
504
+ /* eslint-enable jsdoc/valid-types -- https://github.com/jsdoc-type-pratt-parser/jsdoc-type-pratt-parser/issues/131 */
505
+
506
+ /**
507
+ * @typedef {(this: HTMLElement) => void} DefineUserConstructor
508
+ */
509
+
510
+ /**
511
+ * @typedef {[DefineConstructor|DefineUserConstructor|DefineMixin, DefineOptions?]|[DefineConstructor|DefineUserConstructor, DefineMixin?, DefineOptions?]} DefineObjectArray
512
+ */
513
+
514
+ /**
515
+ * @typedef {DefineObjectArray|DefineConstructor|DefineMixin|DefineUserConstructor} DefineObject
516
+ */
517
+
518
+ /**
519
+ * @typedef {{elem?: HTMLElement, [key: string]: any}} SymbolObject
520
+ */
521
+
522
+ /**
523
+ * @typedef {[symbol|string, ((this: HTMLElement, ...args: any[]) => any)|SymbolObject]} SymbolArray
524
+ */
525
+
526
+ /**
527
+ * @typedef {null|undefined} NullableAttributeValue
528
+ */
529
+
530
+ /**
531
+ * @typedef {[string, object]|string|{[key: string]: any}} PluginValue
532
+ */
533
+
534
+ /**
535
+ * @typedef {(string|NullableAttributeValue|BooleanAttribute|
536
+ * JamilihArray|JamilihShadowRootObject|StringifiableNumber|
537
+ * JamilihDocumentType|JamilihDocument|XmlnsAttributeValue|
538
+ * OnAttributeObject|
539
+ * HandlerAttributeValue|DefineObject|SymbolArray|PluginReference|
540
+ * PluginValue
541
+ * )} JamilihAttValue
542
+ */
543
+
544
+ /**
545
+ * @typedef {{
546
+ * [key: string]: string|number|((this: HTMLElement, ...args: any[]) => any)
547
+ * }} DataAttributeObject
548
+ */
549
+
550
+ /**
551
+ * @typedef {{
552
+ * $data?: true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject|
553
+ * [undefined, DataAttributeObject]|
554
+ * [Map<any, any>|WeakMap<any, any>|undefined, DataAttributeObject]
555
+ * }} DataAttribute
556
+ */
557
+
558
+ /**
559
+ * @typedef {{
560
+ * dataset?: DatasetAttributeObject
561
+ * }} DatasetAttribute
562
+ */
563
+
564
+ /**
565
+ * @typedef {{
566
+ * style?: StyleAttributeValue
567
+ * }} StyleAttribute
568
+ */
569
+
570
+ /**
571
+ * @typedef {{
572
+ * $shadow?: JamilihShadowRootObject
573
+ * }} JamilihShadowRootAttribute
574
+ */
575
+
576
+ /* eslint-disable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
577
+ /**
578
+ * @typedef {{
579
+ * is?: string|null,
580
+ * $define?: DefineObject
581
+ * }} DefineAttribute
582
+ */
583
+ /* eslint-enable jsdoc/valid-types -- jsdoc-type-pratt-parser Bug */
584
+
585
+ /**
586
+ * @typedef {{
587
+ * $custom?: {[key: string]: any}
588
+ * }} CustomAttribute
589
+ */
590
+
591
+ /**
592
+ * @typedef {{
593
+ * $symbol?: SymbolArray
594
+ * }} SymbolAttribute
595
+ */
596
+
597
+ /**
598
+ * @typedef {{
599
+ * xmlns?: string|null|XmlnsAttributeObject
600
+ * }} XmlnsAttribute
601
+ */
602
+
603
+ /**
604
+ * `OnHandlerObject &` wasn't working, so added `HandlerAttributeValue`.
605
+ * @typedef {DataAttribute & StyleAttribute & JamilihShadowRootAttribute &
606
+ * DefineAttribute & DatasetAttribute & CustomAttribute & SymbolAttribute &
607
+ * OnAttribute & XmlnsAttribute &
608
+ * Partial<JamilihAttributeNode> & Partial<JamilihTextNode> &
609
+ * Partial<JamilihDoc> & Partial<JamilihDoctype> & {
610
+ * [key: string]: JamilihAttValue|HandlerAttributeValue,
611
+ * }} JamilihAttributes
612
+ */
613
+
614
+ /**
615
+ * @typedef {{
616
+ * title?: string,
617
+ * childNodes?: JamilihChildType[],
618
+ * $DOCTYPE?: JamilihDocumentType,
619
+ * head?: JamilihChildren
620
+ * body?: JamilihChildren
621
+ * }} JamilihDocument
622
+ */
623
+
624
+ /**
625
+ * @typedef {{
626
+ * $document: JamilihDocument
627
+ * }} JamilihDoc
628
+ */
629
+
630
+ /**
631
+ * @typedef {{$DOCTYPE: JamilihDocumentType}} JamilihDoctype
632
+ */
633
+
634
+ /**
635
+ * @typedef {JamilihArray|TextNodeString|HTMLElement} JamilihDocumentFragmentContent
636
+ */
637
+
638
+ /**
639
+ * @typedef {{'#': JamilihDocumentFragmentContent[]}} JamilihDocumentFragment
640
+ */
641
+
642
+ /**
643
+ * @typedef {string} ElementName
644
+ */
645
+
646
+ /**
647
+ * @typedef {string|number} TextNodeString
648
+ */
649
+
650
+ /**
651
+ * @typedef {{[key: string]: string}} PluginReference
652
+ */
653
+
654
+ /**
655
+ * @typedef {(
656
+ * JamilihArray|TextNodeString|HTMLElement|Comment|ProcessingInstruction|
657
+ * Text|DocumentFragment|JamilihProcessingInstruction|JamilihDocumentFragment|
658
+ * PluginReference
659
+ * )[]} JamilihChildren
660
+ */
661
+
662
+ // Todo: DocumentType, Comment, ProcessingInstruction, Text
663
+ // Todo: JamilihCDATANode, JamilihComment, JamilihProcessingInstruction
664
+ /**
665
+ * @typedef {Document|ElementName|HTMLElement|DocumentFragment|
666
+ * JamilihDocumentFragment|JamilihDoc|JamilihDoctype|JamilihTextNode|
667
+ * JamilihAttributeNode} JamilihFirstArgument
668
+ */
669
+
670
+ /**
671
+ * This would be clearer with overrides, but using as typedef.
672
+ *
673
+ * The optional 0th argument is an Jamilih options object or fragment.
674
+ *
675
+ * The first argument is the element to create (by lower-case name) or DOM element.
676
+ *
677
+ * The second optional argument are attributes to add with the key as the
678
+ * attribute name and value as the attribute value.
679
+ * The third optional argument are an array of children for this element
680
+ * (but raw DOM elements are required to be specified within arrays since
681
+ * could not otherwise be distinguished from siblings being added).
682
+ * The fourth optional argument are a sequence of sibling Elements, represented
683
+ * as DOM elements, or string/attributes/children sequences.
684
+ * The fifth optional argument is the parent to which to attach the element
685
+ * (always the last unless followed by null, in which case it is the
686
+ * second-to-last).
687
+ * The sixth last optional argument is null, used to indicate an array of elements
688
+ * should be returned.
689
+ * @typedef {[
690
+ * JamilihOptions|JamilihFirstArgument,
691
+ * (JamilihFirstArgument|
692
+ * JamilihAttributes|
693
+ * JamilihChildren|
694
+ * HTMLElement|ShadowRoot|
695
+ * null)?,
696
+ * (JamilihAttributes|
697
+ * JamilihChildren|
698
+ * HTMLElement|ShadowRoot|
699
+ * ElementName|null)?,
700
+ * ...(JamilihAttributes|
701
+ * JamilihChildren|
702
+ * HTMLElement|ShadowRoot|
703
+ * ElementName|null)[]
704
+ * ]} JamilihArray
705
+ */
706
+
707
+ /**
708
+ * @typedef {[
709
+ * (string|HTMLElement|ShadowRoot), (JamilihArray[]|JamilihAttributes|HTMLElement|ShadowRoot|null)?, ...(JamilihArray[]|HTMLElement|JamilihAttributes|ShadowRoot|null)[]
710
+ * ]} JamilihArrayPostOptions
711
+ */
712
+
713
+ /**
714
+ * @typedef {{
715
+ * root: [Map<HTMLElement,any>|WeakMap<HTMLElement,any>, any],
716
+ * [key: string]: [Map<HTMLElement,any>|WeakMap<HTMLElement,any>, any]
717
+ * }} MapWithRoot
718
+ */
719
+
720
+ /**
721
+ * @typedef {"root"|"attributeValue"|"element"|"fragment"|"children"|"fragmentChildren"} TraversalState
722
+ */
723
+
724
+ /**
725
+ * @typedef {object} JamilihOptions
726
+ * @property {TraversalState} [$state]
727
+ * @property {JamilihPlugin[]} [$plugins]
728
+ * @property {MapWithRoot|[Map<HTMLElement,any>|WeakMap<HTMLElement,any>, any]} [$map]
729
+ */
730
+
731
+ /**
732
+ * @param {Document|HTMLElement|DocumentFragment} elem
733
+ * @param {string|null} att
734
+ * @param {JamilihAttValue} attVal
735
+ * @param {JamilihOptions} opts
736
+ * @param {TraversalState} [state]
737
+ * @returns {Promise<void>|string|null}
738
+ */
739
+ function checkPluginValue(elem, att, attVal, opts, state) {
740
+ opts.$state = state !== null && state !== void 0 ? state : 'attributeValue';
741
+ if (attVal && typeof attVal === 'object') {
742
+ const matchingPlugin = getMatchingPlugin(opts, Object.keys(attVal)[0]);
743
+ if (matchingPlugin) {
744
+ return matchingPlugin.set({
745
+ opts,
746
+ element: elem,
747
+ attribute: {
748
+ name: att,
749
+ value: attVal
750
+ }
751
+ });
752
+ }
753
+ }
754
+ return (/** @type {string} */attVal
755
+ );
756
+ }
757
+
758
+ /**
759
+ * @param {JamilihOptions} opts
760
+ * @param {string} pluginName
761
+ * @returns {JamilihPlugin|undefined}
762
+ */
763
+ function getMatchingPlugin(opts, pluginName) {
764
+ return opts.$plugins && opts.$plugins.find(p => {
765
+ return p.name === pluginName;
766
+ });
767
+ }
768
+
769
+ /* eslint-disable jsdoc/valid-types -- pratt parser bug */
770
+ /**
771
+ * @template T
772
+ * @typedef {T[keyof T]} ValueOf
773
+ */
774
+ /* eslint-enable jsdoc/valid-types -- pratt parser bug */
775
+
776
+ /* eslint-disable jsdoc/valid-types -- pratt parser bug */
777
+ /**
778
+ * Creates an XHTML or HTML element (XHTML is preferred, but only in browsers
779
+ * that support); any element after element can be omitted, and any subsequent
780
+ * type or types added afterwards.
781
+ * @template {JamilihArray} T
782
+ * @param {T} args
783
+ * @returns {T extends [keyof HTMLElementTagNameMap, any?, any?, any?]
784
+ * ? HTMLElementTagNameMap[T[0]] : JamilihReturn}
785
+ * The newly created (and possibly already appended)
786
+ * element or array of elements
787
+ */
788
+ const jml = function jml(...args) {
789
+ /* eslint-enable jsdoc/valid-types -- pratt parser bug */
790
+ if (!win) {
791
+ throw new Error('No window object');
792
+ }
793
+ if (!doc) {
794
+ throw new Error('No document object');
795
+ }
796
+
797
+ /** @type {(Document|DocumentFragment|HTMLElement) & {[key: string]: any}} */
798
+ let elem = doc.createDocumentFragment();
799
+ /**
800
+ *
801
+ * @param {JamilihAttributes} atts
802
+ * @throws {TypeError}
803
+ * @returns {void}
804
+ */
805
+ function _checkAtts(atts) {
806
+ /* c8 ignore next 3 */
807
+ if (!doc) {
808
+ throw new Error('No document object');
809
+ }
810
+ for (let [att, attVal] of Object.entries(atts)) {
811
+ var _ATTR_MAP$get;
812
+ att = (_ATTR_MAP$get = ATTR_MAP.get(att)) !== null && _ATTR_MAP$get !== void 0 ? _ATTR_MAP$get : att;
813
+
814
+ /**
815
+ * @typedef {any} ElementExpando
816
+ */
817
+
818
+ if (NULLABLES.includes(att)) {
819
+ attVal = checkPluginValue(elem, att, /** @type {string|JamilihArray} */attVal, opts);
820
+ if (!_isNullish(attVal)) {
821
+ /** @type {ElementExpando} */elem[att] = attVal;
822
+ }
823
+ continue;
824
+ } else if (ATTR_DOM.includes(att)) {
825
+ attVal = checkPluginValue(elem, att, /** @type {string|JamilihArray} */attVal, opts);
826
+ /** @type {ElementExpando} */
827
+ elem[att] = attVal;
828
+ continue;
829
+ }
830
+ switch (att) {
831
+ /*
832
+ Todos:
833
+ 0. JSON mode to prevent event addition
834
+ 0. {$xmlDocument: []} // doc.implementation.createDocument
835
+ 0. Accept array for any attribute with first item as prefix and second as value?
836
+ 0. {$: ['xhtml', 'div']} for prefixed elements
837
+ case '$': // Element with prefix?
838
+ nodes[nodes.length] = elem = doc.createElementNS(attVal[0], attVal[1]);
839
+ break;
840
+ */
841
+ case '#':
842
+ {
843
+ // Document fragment
844
+ opts.$state = 'fragmentChildren';
845
+ nodes[nodes.length] = jml(opts, /** @type {JamilihArray[]} */attVal);
846
+ break;
847
+ }
848
+ case '$shadow':
849
+ {
850
+ const {
851
+ open,
852
+ closed
853
+ } = /** @type {JamilihShadowRootObject} */attVal;
854
+ let {
855
+ content,
856
+ template
857
+ } = /** @type {JamilihShadowRootObject} */attVal;
858
+ const shadowRoot = /** @type {HTMLElement} */elem.attachShadow({
859
+ mode: closed || open === false ? 'closed' : 'open'
860
+ });
861
+ if (template) {
862
+ if (Array.isArray(template)) {
863
+ template = /** @type {HTMLTemplateElement} */
864
+ _getType(template[0]) === 'object' ? jml('template', ...
865
+ /**
866
+ * @type {[
867
+ * JamilihAttributes, ...(JamilihArray[]|HTMLElement)[]
868
+ * ]}
869
+ */
870
+ template, doc.body) : jml('template',
871
+ /**
872
+ * @type {JamilihArray[]|HTMLElement}
873
+ */
874
+ template, doc.body);
875
+ } else if (typeof template === 'string') {
876
+ template = /** @type {HTMLTemplateElement} */$(template);
877
+ }
878
+ jml( /** @type {HTMLTemplateElement} */
879
+ /** @type {HTMLTemplateElement} */template.content.cloneNode(true), shadowRoot);
880
+ } else {
881
+ if (!content) {
882
+ if (open !== true) {
883
+ content = open || typeof closed === 'boolean' ? content : closed;
884
+ }
885
+ }
886
+ if (content && typeof content !== 'boolean') {
887
+ if (Array.isArray(content)) {
888
+ jml({
889
+ '#': content
890
+ }, shadowRoot);
891
+ } else {
892
+ jml(content, shadowRoot);
893
+ }
894
+ }
895
+ }
896
+ break;
897
+ }
898
+ case '$state':
899
+ {
900
+ // Handled internally
901
+ break;
902
+ }
903
+ case 'is':
904
+ {
905
+ // Currently only in Chrome
906
+ // Handled during element creation
907
+ break;
908
+ }
909
+ case '$custom':
910
+ {
911
+ Object.assign(elem, attVal);
912
+ break;
913
+ }
914
+ case '$define':
915
+ {
916
+ if (!('localName' in elem)) {
917
+ throw new Error('Element expected for `$define`');
918
+ }
919
+ const localName = elem.localName.toLowerCase();
920
+ // Note: customized built-ins sadly not working yet
921
+ const customizedBuiltIn = !localName.includes('-');
922
+
923
+ // We check attribute in case this is a preexisting DOM element
924
+ // const {is} = atts;
925
+ let is;
926
+ if (customizedBuiltIn) {
927
+ is = elem.getAttribute('is');
928
+ if (!is) {
929
+ if (!Object.hasOwn(atts, 'is')) {
930
+ throw new TypeError(`Expected \`is\` with \`$define\` on built-in; args: ${JSON.stringify(args)}`);
931
+ }
932
+ atts.is = /** @type {string} */checkPluginValue(elem, 'is', atts.is, opts);
933
+ elem.setAttribute('is', atts.is);
934
+ ({
935
+ is
936
+ } = atts);
937
+ }
938
+ }
939
+ const def = customizedBuiltIn ? /** @type {string} */is : localName;
940
+ if (window.customElements.get(def)) {
941
+ break;
942
+ }
943
+
944
+ /**
945
+ * @param {DefineUserConstructor} [cnstrct]
946
+ * @returns {DefineConstructor}
947
+ */
948
+ const getConstructor = cnstrct => {
949
+ /* c8 ignore next 3 */
950
+ if (!doc) {
951
+ throw new Error('No document object');
952
+ }
953
+ const baseClass = typeof options === 'object' && typeof options.extends === 'string' ? /** @type {typeof HTMLElement} */doc.createElement(options.extends).constructor : customizedBuiltIn ? /** @type {typeof HTMLElement} */doc.createElement(localName).constructor : window.HTMLElement;
954
+
955
+ /**
956
+ * Class wrapping base class.
957
+ */
958
+ return cnstrct ? class extends baseClass {
959
+ /**
960
+ * Calls user constructor.
961
+ */
962
+ constructor() {
963
+ super();
964
+ /** @type {DefineUserConstructor} */
965
+ cnstrct.call(this);
966
+ }
967
+ } : class extends baseClass {};
968
+ };
969
+
970
+ /** @type {DefineConstructor|DefineUserConstructor|DefineMixin} */
971
+ let cnstrctr;
972
+
973
+ /**
974
+ * @type {DefineOptions|undefined}
975
+ */
976
+ let options;
977
+ let mixin;
978
+ const defineObj = /** @type {DefineObject} */attVal;
979
+ if (Array.isArray(defineObj)) {
980
+ if (defineObj.length <= 2) {
981
+ [cnstrctr, options] = defineObj;
982
+ if (typeof options === 'string') {
983
+ // Todo: Allow creating a definition without using it;
984
+ // that may be the only reason to have a string here which
985
+ // differs from the `localName` anyways
986
+ options = {
987
+ extends: options
988
+ };
989
+ } else if (options && !Object.hasOwn(options, 'extends')) {
990
+ mixin = options;
991
+ }
992
+ if (typeof cnstrctr === 'object') {
993
+ mixin = cnstrctr;
994
+ cnstrctr = getConstructor();
995
+ }
996
+ } else {
997
+ [cnstrctr, mixin, options] = defineObj;
998
+ if (typeof options === 'string') {
999
+ options = {
1000
+ extends: options
1001
+ };
1002
+ }
1003
+ }
1004
+ } else if (typeof defineObj === 'function') {
1005
+ cnstrctr = /** @type {DefineConstructor} */defineObj;
1006
+ } else {
1007
+ mixin = defineObj;
1008
+ cnstrctr = getConstructor();
1009
+ }
1010
+ if (!cnstrctr.toString().startsWith('class')) {
1011
+ cnstrctr = getConstructor( /** @type {DefineUserConstructor} */cnstrctr);
1012
+ }
1013
+ if (!options && customizedBuiltIn) {
1014
+ options = {
1015
+ extends: localName
1016
+ };
1017
+ }
1018
+ if (mixin) {
1019
+ Object.entries(mixin).forEach(([methodName, method]) => {
1020
+ /** @type {DefineConstructor} */cnstrctr.prototype[methodName] = method;
1021
+ });
1022
+ }
1023
+ // console.log('def', def, '::', typeof options === 'object' ? options : undefined);
1024
+ window.customElements.define(def, /** @type {DefineConstructor} */cnstrctr, typeof options === 'object' ? options : undefined);
1025
+ break;
1026
+ }
1027
+ case '$symbol':
1028
+ {
1029
+ const [symbol, func] = /** @type {SymbolArray} */attVal;
1030
+ if (typeof func === 'function') {
1031
+ const funcBound = func.bind( /** @type {HTMLElement} */elem);
1032
+ if (typeof symbol === 'string') {
1033
+ // @ts-expect-error
1034
+ elem[Symbol.for(symbol)] = funcBound;
1035
+ } else {
1036
+ // @ts-expect-error
1037
+ elem[symbol] = funcBound;
1038
+ }
1039
+ } else {
1040
+ const obj = func;
1041
+ obj.elem = /** @type {HTMLElement} */elem;
1042
+ if (typeof symbol === 'string') {
1043
+ // @ts-expect-error
1044
+ elem[Symbol.for(symbol)] = obj;
1045
+ } else {
1046
+ // @ts-expect-error
1047
+ elem[symbol] = obj;
1048
+ }
1049
+ }
1050
+ break;
1051
+ }
1052
+ case '$data':
1053
+ {
1054
+ setMap( /** @type {true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject} */
1055
+ attVal);
1056
+ break;
1057
+ }
1058
+ case '$attribute':
1059
+ {
1060
+ // Attribute node
1061
+ const attr = /** @type {JamilihAttributeNodeValue} */attVal;
1062
+ const node = attr.length === 3 ? doc.createAttributeNS(attr[0], attr[1]) : doc.createAttribute( /** @type {string} */attr[0]);
1063
+ node.value = /** @type {string} */attr[attr.length - 1];
1064
+ nodes[nodes.length] = node;
1065
+ break;
1066
+ }
1067
+ case '$text':
1068
+ {
1069
+ // Todo: Also allow as jml(['a text node']) (or should that become a fragment)?
1070
+ const node = doc.createTextNode( /** @type {string} */attVal);
1071
+ nodes[nodes.length] = node;
1072
+ break;
1073
+ }
1074
+ case '$document':
1075
+ {
1076
+ // Todo: Conditionally create XML document
1077
+ const docNode = doc.implementation.createHTMLDocument();
1078
+ if (!attVal) {
1079
+ throw new Error('Bad attribute value');
1080
+ }
1081
+ const jamlihDoc = /** @type {JamilihDocument} */attVal;
1082
+ if (jamlihDoc.childNodes) {
1083
+ // Remove any extra nodes created by createHTMLDocument().
1084
+ const j = jamlihDoc.childNodes.length;
1085
+ while (docNode.childNodes[j]) {
1086
+ const cn = docNode.childNodes[j];
1087
+ cn.remove();
1088
+ // `j` should stay the same as removing will cause node to be present
1089
+ }
1090
+
1091
+ jamlihDoc.childNodes.forEach(_childrenToJML(docNode));
1092
+ } else {
1093
+ if (jamlihDoc.$DOCTYPE) {
1094
+ var _docNode$firstChild;
1095
+ const dt = {
1096
+ $DOCTYPE: jamlihDoc.$DOCTYPE
1097
+ };
1098
+ const doctype = jml(dt);
1099
+ (_docNode$firstChild = docNode.firstChild) === null || _docNode$firstChild === void 0 ? void 0 : _docNode$firstChild.replaceWith(doctype);
1100
+ }
1101
+ const html = docNode.querySelector('html');
1102
+ const head = html === null || html === void 0 ? void 0 : html.querySelector('head');
1103
+ const body = html === null || html === void 0 ? void 0 : html.querySelector('body');
1104
+ if (jamlihDoc.title || jamlihDoc.head) {
1105
+ const meta = doc.createElement('meta');
1106
+ // eslint-disable-next-line unicorn/text-encoding-identifier-case -- HTML
1107
+ meta.setAttribute('charset', 'utf-8');
1108
+ head === null || head === void 0 ? void 0 : head.append(meta);
1109
+ if (jamlihDoc.title) {
1110
+ docNode.title = jamlihDoc.title; // Appends after meta
1111
+ }
1112
+
1113
+ if (jamlihDoc.head && head) {
1114
+ // each child of `head` is:
1115
+ // (JamilihArray|TextNodeString|HTMLElement|Comment|ProcessingInstruction|
1116
+ // Text|DocumentFragment|JamilihProcessingInstruction|JamilihDocumentFragment)
1117
+
1118
+ // * @typedef {JamilihDoc|JamilihDoctype|JamilihTextNode|
1119
+ // * JamilihAttributeNode|JamilihOptions|ElementName|HTMLElement|
1120
+ // * JamilihDocumentFragment
1121
+ // * } JamilihFirstArg
1122
+ // appender childJML param is: JamilihArray|JamilihFirstArg
1123
+
1124
+ jamlihDoc.head.forEach(_appendJML(head));
1125
+ }
1126
+ }
1127
+ if (jamlihDoc.body && body) {
1128
+ jamlihDoc.body.forEach(_appendJMLOrText(body));
1129
+ }
1130
+ }
1131
+ nodes[nodes.length] = docNode;
1132
+ break;
1133
+ }
1134
+ case '$DOCTYPE':
1135
+ {
1136
+ const doctype = /** @type {JamilihDocumentType} */attVal;
1137
+ const node = doc.implementation.createDocumentType(doctype.name, doctype.publicId || '', doctype.systemId || '');
1138
+ nodes[nodes.length] = node;
1139
+ break;
1140
+ }
1141
+ case '$on':
1142
+ {
1143
+ // Events
1144
+ // Allow for no-op by defaulting to `{}`
1145
+ for (let [p2, val] of Object.entries( /** @type {OnAttributeObject} */attVal || {})) {
1146
+ if (typeof val === 'function') {
1147
+ val = [val, false];
1148
+ }
1149
+ if (typeof val[0] !== 'function') {
1150
+ throw new TypeError(`Expect a function for \`$on\`; args: ${JSON.stringify(args)}`);
1151
+ }
1152
+ _addEvent( /** @type {HTMLElement} */elem, p2, val[0], val[1]); // element, event name, handler, capturing
1153
+ }
1154
+
1155
+ break;
1156
+ }
1157
+ case 'className':
1158
+ case 'class':
1159
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
1160
+ if (!_isNullish(attVal)) {
1161
+ elem.className = attVal;
1162
+ }
1163
+ break;
1164
+ case 'dataset':
1165
+ {
1166
+ // Map can be keyed with hyphenated or camel-cased properties
1167
+ /**
1168
+ * @param {DatasetAttributeObject} atVal
1169
+ * @param {string} startProp
1170
+ * @returns {void}
1171
+ */
1172
+ const recurse = (atVal, startProp) => {
1173
+ let prop = '';
1174
+ const pastInitialProp = startProp !== '';
1175
+ Object.keys(atVal).forEach(key => {
1176
+ const value = atVal[key];
1177
+ prop = pastInitialProp ? startProp + key.replace(hyphenForCamelCase, _upperCase).replace(/^([a-z])/u, _upperCase) : startProp + key.replace(hyphenForCamelCase, _upperCase);
1178
+ if (value === null || typeof value !== 'object') {
1179
+ if (!_isNullish(value)) {
1180
+ elem.dataset[prop] = value;
1181
+ }
1182
+ prop = startProp;
1183
+ return;
1184
+ }
1185
+ recurse(value, prop);
1186
+ });
1187
+ };
1188
+ recurse( /** @type {DatasetAttributeObject} */attVal, '');
1189
+ break;
1190
+ // Todo: Disable this by default unless configuration explicitly allows (for security)
1191
+ }
1192
+ // #if IS_REMOVE
1193
+ // Don't remove this `if` block (for sake of no-innerHTML build)
1194
+ case 'innerHTML':
1195
+ if (!_isNullish(attVal)) {
1196
+ // eslint-disable-next-line no-unsanitized/property
1197
+ elem.innerHTML = attVal;
1198
+ }
1199
+ break;
1200
+ // #endif
1201
+ case 'htmlFor':
1202
+ case 'for':
1203
+ if (elStr === 'label') {
1204
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
1205
+ if (!_isNullish(attVal)) {
1206
+ elem.htmlFor = attVal;
1207
+ }
1208
+ break;
1209
+ }
1210
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
1211
+ elem.setAttribute(att, attVal);
1212
+ break;
1213
+ case 'xmlns':
1214
+ // Already handled
1215
+ break;
1216
+ default:
1217
+ {
1218
+ if (att.startsWith('on')) {
1219
+ attVal = checkPluginValue(elem, att, /** @type {HandlerAttributeValue} */attVal, opts);
1220
+ elem[att] = attVal;
1221
+ // _addEvent(elem, att.slice(2), attVal, false); // This worked, but perhaps the user wishes only one event
1222
+ break;
1223
+ }
1224
+ if (att === 'style') {
1225
+ attVal = /** @type {string} */
1226
+ checkPluginValue(elem, att, /** @type {StyleAttributeValue} */attVal, opts);
1227
+ if (_isNullish(attVal)) {
1228
+ break;
1229
+ }
1230
+ if (typeof attVal === 'object') {
1231
+ for (const [p2, styleVal] of Object.entries(attVal)) {
1232
+ if (!_isNullish(styleVal)) {
1233
+ // Todo: Handle aggregate properties like "border"
1234
+ if (p2 === 'float') {
1235
+ elem.style.cssFloat = styleVal;
1236
+ elem.style.styleFloat = styleVal; // Harmless though we could make conditional on older IE instead
1237
+ } else {
1238
+ elem.style[p2.replace(hyphenForCamelCase, _upperCase)] = styleVal;
1239
+ }
1240
+ }
1241
+ }
1242
+ break;
1243
+ }
1244
+
1245
+ // setAttribute unfortunately erases any existing styles
1246
+ elem.setAttribute(att, attVal);
1247
+ /*
1248
+ // The following reorders which is troublesome for serialization, e.g., as used in our testing
1249
+ if (elem.style.cssText !== undefined) {
1250
+ elem.style.cssText += attVal;
1251
+ } else { // Opera
1252
+ elem.style += attVal;
1253
+ }
1254
+ */
1255
+ break;
1256
+ }
1257
+ const pluginName = att;
1258
+ const matchingPlugin = getMatchingPlugin(opts, pluginName);
1259
+ if (matchingPlugin) {
1260
+ matchingPlugin.set({
1261
+ opts,
1262
+ element: /** @type {HTMLElement} */nodes[0],
1263
+ attribute: {
1264
+ name: pluginName,
1265
+ value: /** @type {PluginReference} */attVal
1266
+ }
1267
+ });
1268
+ break;
1269
+ }
1270
+ attVal = checkPluginValue(elem, att, /** @type {string} */attVal, opts);
1271
+ elem.setAttribute(att, attVal);
1272
+ break;
1273
+ }
1274
+ }
1275
+ }
1276
+ }
1277
+
1278
+ /**
1279
+ * @type {JamilihReturn[]}
1280
+ */
1281
+ const nodes = [];
1282
+
1283
+ /** @type {string} */
1284
+ let elStr;
1285
+
1286
+ /** @type {JamilihOptions} */
1287
+ let opts;
1288
+ let isRoot = false;
1289
+ let argStart = 0;
1290
+ if (_getType(args[0]) === 'object' && Object.keys(args[0]).some(key => possibleOptions.includes(key))) {
1291
+ opts = /** @type {JamilihOptions} */args[0];
1292
+ if (opts.$state === undefined) {
1293
+ isRoot = true;
1294
+ opts.$state = 'root';
1295
+ }
1296
+ if (Array.isArray(opts.$map)) {
1297
+ opts.$map = {
1298
+ root: opts.$map
1299
+ };
1300
+ }
1301
+ if ('$plugins' in opts) {
1302
+ if (!Array.isArray(opts.$plugins)) {
1303
+ throw new TypeError(`\`$plugins\` must be an array; args: ${JSON.stringify(args)}`);
1304
+ }
1305
+ opts.$plugins.forEach(pluginObj => {
1306
+ if (!pluginObj || typeof pluginObj !== 'object') {
1307
+ throw new TypeError(`Plugin must be an object; args: ${JSON.stringify(args)}`);
1308
+ }
1309
+ if (!pluginObj.name || !pluginObj.name.startsWith('$_')) {
1310
+ throw new TypeError(`Plugin object name must be present and begin with \`$_\`; args: ${JSON.stringify(args)}`);
1311
+ }
1312
+ if (typeof pluginObj.set !== 'function') {
1313
+ throw new TypeError(`Plugin object must have a \`set\` method; args: ${JSON.stringify(args)}`);
1314
+ }
1315
+ });
1316
+ }
1317
+ argStart = 1;
1318
+ } else {
1319
+ opts = {
1320
+ $state: undefined
1321
+ };
1322
+ }
1323
+ const argc = args.length;
1324
+ const defaultMap = opts.$map && /** @type {MapWithRoot} */opts.$map.root;
1325
+
1326
+ /**
1327
+ * @param {true|string[]|Map<any, any>|WeakMap<any, any>|DataAttributeObject} dataVal
1328
+ * @returns {void}
1329
+ */
1330
+ const setMap = dataVal => {
1331
+ let map, obj;
1332
+ const defMap = /** @type {[Map<HTMLElement, any> | WeakMap<HTMLElement, any>, any]} */defaultMap;
1333
+ // Boolean indicating use of default map and object
1334
+ if (dataVal === true) {
1335
+ [map, obj] = defMap;
1336
+ } else if (Array.isArray(dataVal)) {
1337
+ // Array of strings mapping to default
1338
+ if (typeof dataVal[0] === 'string') {
1339
+ dataVal.forEach(dVal => {
1340
+ setMap( /** @type {MapWithRoot} */opts.$map[dVal]);
1341
+ });
1342
+ return;
1343
+ // Array of Map and non-map data object
1344
+ }
1345
+
1346
+ map = dataVal[0] || defMap[0];
1347
+ obj = dataVal[1] || defMap[1];
1348
+ // Map
1349
+ } else if (/^\[object (?:Weak)?Map\]$/u.test([].toString.call(dataVal))) {
1350
+ map = dataVal;
1351
+ obj = defMap[1];
1352
+ // Non-map data object
1353
+ } else {
1354
+ map = defMap[0];
1355
+ obj = dataVal;
1356
+ }
1357
+ /** @type {Map<HTMLElement, any> | WeakMap<HTMLElement, any>} */
1358
+ map.set( /** @type {HTMLElement} */
1359
+ elem, obj);
1360
+ };
1361
+ for (let i = argStart; i < argc; i++) {
1362
+ let arg = args[i];
1363
+ const type = _getType(arg);
1364
+ switch (type) {
1365
+ case 'null':
1366
+ // null always indicates a place-holder (only needed for last argument if want array returned)
1367
+ if (i === argc - 1) {
1368
+ // Casting needing unless changing `jml()` signature with overloads
1369
+ return (/** @type {ArbitraryValue} */nodes.length <= 1 ? nodes[0]
1370
+ // eslint-disable-next-line unicorn/no-array-callback-reference
1371
+ : nodes.reduce(_fragReducer, doc.createDocumentFragment())
1372
+ ); // nodes;
1373
+ }
1374
+
1375
+ throw new TypeError(`\`null\` values not allowed except as final Jamilih argument; index ${i} on args: ${JSON.stringify(args)}`);
1376
+ case 'string':
1377
+ // Strings normally indicate elements
1378
+ switch (arg) {
1379
+ case '!':
1380
+ nodes[nodes.length] = doc.createComment( /** @type {string} */args[++i]);
1381
+ break;
1382
+ case '?':
1383
+ {
1384
+ arg = /** @type {string} */args[++i];
1385
+ let procValue = /** @type {string} */args[++i];
1386
+ const val = procValue;
1387
+ if (val && typeof val === 'object') {
1388
+ const procValues = [];
1389
+ for (const [p, procInstVal] of Object.entries(val)) {
1390
+ procValues.push(p + '=' + '"' +
1391
+ // https://www.w3.org/TR/xml-stylesheet/#NT-PseudoAttValue
1392
+ procInstVal.replace(/"/gu, '&quot;') + '"');
1393
+ }
1394
+ procValue = procValues.join(' ');
1395
+ }
1396
+ // Firefox allows instructions with ">" in this method, but not if placed directly!
1397
+ try {
1398
+ nodes[nodes.length] = doc.createProcessingInstruction(arg, procValue);
1399
+ } catch (e) {
1400
+ // Getting NotSupportedError in IE, so we try to imitate a processing instruction with a comment
1401
+ // innerHTML didn't work
1402
+ // var elContainer = doc.createElement('div');
1403
+ // elContainer.innerHTML = '<?' + doc.createTextNode(arg + ' ' + procValue).nodeValue + '?>';
1404
+ // nodes[nodes.length] = elContainer.innerHTML;
1405
+ // Todo: any other way to resolve? Just use XML?
1406
+ nodes[nodes.length] = doc.createComment('?' + arg + ' ' + procValue + '?');
1407
+ }
1408
+ break;
1409
+ // Browsers don't support doc.createEntityReference, so we just use this as a convenience
1410
+ }
1411
+ case '&':
1412
+ nodes[nodes.length] = _createSafeReference('entity', '', /** @type {string} */
1413
+ args[++i]);
1414
+ break;
1415
+ case '#':
1416
+ // // Decimal character reference - ['#', '01234'] // &#01234; // probably easier to use JavaScript Unicode escapes
1417
+ nodes[nodes.length] = _createSafeReference('decimal', arg, String(args[++i]));
1418
+ break;
1419
+ case '#x':
1420
+ // Hex character reference - ['#x', '123a'] // &#x123a; // probably easier to use JavaScript Unicode escapes
1421
+ nodes[nodes.length] = _createSafeReference('hexadecimal', arg, /** @type {string} */
1422
+ args[++i]);
1423
+ break;
1424
+ case '![':
1425
+ // '![', ['escaped <&> text'] // <![CDATA[escaped <&> text]]>
1426
+ // CDATA valid in XML only, so we'll just treat as text for mutual compatibility
1427
+ // Todo: config (or detection via some kind of doc.documentType property?) of whether in XML
1428
+ try {
1429
+ nodes[nodes.length] = doc.createCDATASection( /** @type {string} */args[++i]);
1430
+ } catch (e2) {
1431
+ nodes[nodes.length] = doc.createTextNode( /** @type {string} */
1432
+ args[i]); // i already incremented
1433
+ }
1434
+
1435
+ break;
1436
+ case '':
1437
+ nodes[nodes.length] = elem = doc.createDocumentFragment();
1438
+ // Todo: Report to plugins
1439
+ opts.$state = 'fragment';
1440
+ break;
1441
+ default:
1442
+ {
1443
+ // An element
1444
+ elStr = /** @type {string} */arg;
1445
+ const atts = args[i + 1];
1446
+ if (atts && _getType(atts) === 'object' && /** @type {JamilihAttributes} */atts.is) {
1447
+ const {
1448
+ is
1449
+ } = /** @type {JamilihAttributes} */atts;
1450
+ /* c8 ignore next 4 */
1451
+ elem = doc.createElementNS
1452
+ // Should create separate file for this
1453
+ /* eslint-disable object-shorthand -- Casting */ ? /** @type {HTMLElement} */doc.createElementNS(NS_HTML, elStr, {
1454
+ is: /** @type {string} */is
1455
+ })
1456
+ /* c8 ignore next 1 */ : doc.createElement(elStr, {
1457
+ is: /** @type {string} */is
1458
+ });
1459
+ /* eslint-enable object-shorthand -- Casting */
1460
+ } else /* c8 ignore next */if (doc.createElementNS) {
1461
+ elem = doc.createElementNS(NS_HTML, elStr);
1462
+ /* c8 ignore next 3 */
1463
+ } else {
1464
+ elem = doc.createElement(elStr);
1465
+ }
1466
+ // Todo: Report to plugins
1467
+ opts.$state = 'element';
1468
+ nodes[nodes.length] = elem; // Add to parent
1469
+ break;
1470
+ }
1471
+ }
1472
+ break;
1473
+ case 'object':
1474
+ {
1475
+ // Non-DOM-element objects indicate attribute-value pairs
1476
+ /* c8 ignore next 3 */
1477
+ if (!arg || typeof arg !== 'object') {
1478
+ throw new Error('Null should not reach here');
1479
+ }
1480
+ const atts = arg;
1481
+ if ('xmlns' in atts) {
1482
+ // We handle this here, as otherwise may lose events, etc.
1483
+ // As namespace of element already set as XHTML, we need to change the namespace
1484
+ // elem.setAttribute('xmlns', atts.xmlns); // Doesn't work
1485
+ // Can't set namespaceURI dynamically, renameNode() is not supported, and setAttribute() doesn't work to change the namespace, so we resort to this hack
1486
+ const xmlnsObj = /** @type {XmlnsAttributeObject} */atts;
1487
+ const replacer = xmlnsObj.xmlns && typeof xmlnsObj.xmlns === 'object' ? _replaceDefiner(xmlnsObj.xmlns) : ' xmlns="' + xmlnsObj.xmlns + '"';
1488
+ // try {
1489
+ // Also fix DOMParser to work with text/html
1490
+ elem = nodes[nodes.length - 1] =
1491
+ // Why doesn't `HTMLWindow` have `DOMParser`?
1492
+ new /** @type {import('jsdom').DOMWindow} */win.DOMParser().parseFromString(new /** @type {import('jsdom').DOMWindow} */win.XMLSerializer().serializeToString(elem)
1493
+ // Mozilla adds XHTML namespace
1494
+ .replace(' xmlns="' + NS_HTML + '"',
1495
+ // Needed to cast here, despite either overload working
1496
+ /** @type {string} */
1497
+ replacer), 'application/xml').documentElement;
1498
+ // Todo: Report to plugins
1499
+ opts.$state = 'element';
1500
+ // }catch(e) {alert(elem.outerHTML);throw e;}
1501
+ }
1502
+
1503
+ _checkAtts( /** @type {JamilihAttributes} */atts);
1504
+ break;
1505
+ }
1506
+ case 'document':
1507
+ case 'fragment':
1508
+ case 'element':
1509
+ /*
1510
+ 1) Last element always the parent (put null if don't want parent and want to return array) unless only atts and children (no other elements)
1511
+ 2) Individual elements (DOM elements or sequences of string[/object/array]) get added to parent first-in, first-added
1512
+ */
1513
+ if (i === 0) {
1514
+ // Allow wrapping of element, fragment, or document
1515
+ elem = /** @type {Document|DocumentFragment|HTMLElement} */arg;
1516
+ // Todo: Report to plugins and change for document/fragment
1517
+ opts.$state = 'element';
1518
+ }
1519
+ if (i === argc - 1 || i === argc - 2 && args[i + 1] === null) {
1520
+ // parent
1521
+ const elsl = nodes.length;
1522
+ for (let k = 0; k < elsl; k++) {
1523
+ _appendNode( /** @type {Document|DocumentFragment|HTMLElement} */arg, nodes[k]);
1524
+ }
1525
+ } else {
1526
+ nodes[nodes.length] = /** @type {Document|DocumentFragment|HTMLElement} */arg;
1527
+ }
1528
+ break;
1529
+ case 'array':
1530
+ {
1531
+ // Arrays or arrays of arrays indicate child nodes
1532
+ const child = /** @type {JamilihChildren} */arg;
1533
+ const cl = child.length;
1534
+ for (let j = 0; j < cl; j++) {
1535
+ // Go through children array container to handle elements
1536
+ const childContent = child[j];
1537
+ const childContentType = typeof childContent;
1538
+ if (childContent === null || _isNullish(childContent)) {
1539
+ throw new TypeError(`Bad children (parent array: ${JSON.stringify(args)}; index ${j} of child: ${JSON.stringify(child)})`);
1540
+ }
1541
+ switch (childContentType) {
1542
+ // Todo: determine whether null or function should have special handling or be converted to text
1543
+ case 'string':
1544
+ case 'number':
1545
+ case 'boolean':
1546
+ _appendNode(elem, doc.createTextNode(String(childContent)));
1547
+ break;
1548
+ default:
1549
+ // bigint, symbol, function
1550
+ if (typeof childContent !== 'object') {
1551
+ throw new TypeError(`Bad children (parent array: ${JSON.stringify(args)}; index ${j} of child: ${JSON.stringify(child)})`);
1552
+ }
1553
+ if (Array.isArray(childContent)) {
1554
+ // Arrays representing child elements
1555
+ opts.$state = 'children';
1556
+ _appendNode(elem, jml(opts, ...childContent));
1557
+ } else if ('#' in childContent) {
1558
+ // Fragment
1559
+ opts.$state = 'fragmentChildren';
1560
+ _appendNode(elem, jml(opts, childContent['#']));
1561
+ } else {
1562
+ // Single DOM element children or plugin
1563
+ let newChildContent;
1564
+ if (!('nodeType' in childContent)) {
1565
+ newChildContent = /** @type {string} */
1566
+ checkPluginValue(elem, null, childContent, opts, 'children');
1567
+ }
1568
+ _appendNode(elem, /** @type {string|HTMLElement|DocumentFragment|Comment} */
1569
+ newChildContent || childContent);
1570
+ }
1571
+ break;
1572
+ }
1573
+ }
1574
+ break;
1575
+ }
1576
+ default:
1577
+ throw new TypeError(`Unexpected type: ${type}; arg: ${arg}; index ${i} on args: ${JSON.stringify(args)}`);
1578
+ }
1579
+ }
1580
+ const ret = nodes[0] || elem;
1581
+ if (isRoot && opts.$map && /** @type {MapWithRoot} */opts.$map.root) {
1582
+ setMap(true);
1583
+ }
1584
+
1585
+ // Casting needing unless changing `jml()` signature with overloads
1586
+ return (/** @type {ArbitraryValue} */ret
1587
+ );
1588
+ };
1589
+
1590
+ /**
1591
+ * Configuration object.
1592
+ * @typedef {object} ToJmlConfig
1593
+ * @property {boolean} [stringOutput=false] Whether to output the Jamilih object as a string.
1594
+ * @property {boolean} [reportInvalidState=true] If true (the default), will report invalid state errors
1595
+ * @property {boolean} [stripWhitespace=false] Strip whitespace for text nodes
1596
+ */
1597
+
1598
+ /**
1599
+ * @typedef {[namespace: string|null, name: string, value?: string]} JamilihAttributeNodeValue
1600
+ */
1601
+
1602
+ /**
1603
+ * @typedef {{
1604
+ * $attribute: JamilihAttributeNodeValue
1605
+ * }} JamilihAttributeNode
1606
+ */
1607
+
1608
+ /**
1609
+ * @typedef {{
1610
+ * $text: string
1611
+ * }} JamilihTextNode
1612
+ */
1613
+
1614
+ /**
1615
+ * @typedef {['![', string]} JamilihCDATANode
1616
+ */
1617
+
1618
+ /**
1619
+ * @typedef {['&', string]} JamilihEntityReference
1620
+ */
1621
+
1622
+ /**
1623
+ * @typedef {[code: '?', target: string, value: string]} JamilihProcessingInstruction
1624
+ */
1625
+
1626
+ /**
1627
+ * @typedef {[code: '!', value: string]} JamilihComment
1628
+ */
1629
+
1630
+ /**
1631
+ * @typedef {{
1632
+ * nodeType: number,
1633
+ * nodeName: string
1634
+ * }} Entity
1635
+ */
1636
+
1637
+ /* eslint-disable no-shadow, unicorn/custom-error-definition */
1638
+ /**
1639
+ * Polyfill for `DOMException`.
1640
+ */
1641
+ class DOMException extends Error {
1642
+ /* eslint-enable no-shadow, unicorn/custom-error-definition */
1643
+ /**
1644
+ * @param {string} message
1645
+ * @param {string} name
1646
+ */
1647
+ constructor(message, name) {
1648
+ super(message);
1649
+ this.code = 0;
1650
+ // eslint-disable-next-line unicorn/custom-error-definition
1651
+ this.name = name;
1652
+ }
1653
+ }
1654
+
1655
+ /**
1656
+ * @typedef {JamilihArray|JamilihDoctype|
1657
+ * JamilihCDATANode|JamilihEntityReference|JamilihProcessingInstruction|
1658
+ * JamilihComment|JamilihDocumentFragment} JamilihChildType
1659
+ */
1660
+
1661
+ /**
1662
+ * @typedef {JamilihDoc|JamilihAttributeNode|JamilihChildType} JamilihType
1663
+ */
1664
+
1665
+ /**
1666
+ * Converts a DOM object or a string of HTML into a Jamilih object (or string).
1667
+ * @param {string|HTMLElement|Node|Entity} nde If a string, will parse as document
1668
+ * @param {ToJmlConfig} [config] Configuration object
1669
+ * @throws {TypeError}
1670
+ * @returns {JamilihType|string} Array containing the elements which represent
1671
+ * a Jamilih object, or, if `stringOutput` is true, it will be the stringified
1672
+ * version of such an object
1673
+ */
1674
+ jml.toJML = function (nde, {
1675
+ stringOutput = false,
1676
+ reportInvalidState = true,
1677
+ stripWhitespace = false
1678
+ } = {}) {
1679
+ if (!win) {
1680
+ throw new Error('No window object set');
1681
+ }
1682
+ if (typeof nde === 'string') {
1683
+ nde = new /** @type {import('jsdom').DOMWindow} */win.DOMParser().parseFromString(nde, 'text/html'); // todo: Give option for XML once implemented and change JSDoc to allow for Element
1684
+ }
1685
+
1686
+ const dom = /** @type {HTMLElement|Node|Entity} */nde;
1687
+
1688
+ /**
1689
+ * @todo Find more specific type than `any`
1690
+ * @typedef {{[key: (number|string)]: any}} IndexableObject
1691
+ */
1692
+
1693
+ const ret = /** @type {IndexableObject} */[];
1694
+ let parent = ret;
1695
+ let parentIdx = 0;
1696
+
1697
+ /**
1698
+ * @param {string} msg
1699
+ * @throws {DOMException}
1700
+ * @returns {void}
1701
+ */
1702
+ function invalidStateError(msg) {
1703
+ // These are probably only necessary if working with text/html
1704
+ if (reportInvalidState) {
1705
+ // INVALID_STATE_ERR per section 9.3 XHTML 5: http://www.w3.org/TR/html5/the-xhtml-syntax.html
1706
+ const e = new DOMException(msg, 'INVALID_STATE_ERR');
1707
+ e.code = 11;
1708
+ throw e;
1709
+ }
1710
+ }
1711
+
1712
+ /**
1713
+ *
1714
+ * @param {JamilihDocumentType} obj
1715
+ * @param {DocumentType} node
1716
+ * @returns {void}
1717
+ */
1718
+ function addExternalID(obj, node) {
1719
+ if (node.systemId.includes('"') && node.systemId.includes("'")) {
1720
+ invalidStateError('systemId cannot have both single and double quotes.');
1721
+ }
1722
+ const {
1723
+ publicId,
1724
+ systemId
1725
+ } = node;
1726
+ if (systemId) {
1727
+ obj.systemId = systemId;
1728
+ }
1729
+ if (publicId) {
1730
+ obj.publicId = publicId;
1731
+ }
1732
+ }
1733
+
1734
+ /**
1735
+ *
1736
+ * @param {ArbitraryValue} val
1737
+ * @returns {void}
1738
+ */
1739
+ function set(val) {
1740
+ parent[parentIdx] = val;
1741
+ parentIdx++;
1742
+ }
1743
+
1744
+ /**
1745
+ * @returns {void}
1746
+ */
1747
+ function setChildren() {
1748
+ set([]);
1749
+ parent = parent[parentIdx - 1];
1750
+ parentIdx = 0;
1751
+ }
1752
+
1753
+ /**
1754
+ *
1755
+ * @param {string} prop1
1756
+ * @param {string} [prop2]
1757
+ * @returns {void}
1758
+ */
1759
+ function setObj(prop1, prop2) {
1760
+ parent = parent[parentIdx - 1][prop1];
1761
+ parentIdx = 0;
1762
+ if (prop2) {
1763
+ parent = parent[prop2];
1764
+ }
1765
+ }
1766
+
1767
+ /**
1768
+ *
1769
+ * @param {Node|Entity} nodeOrEntity
1770
+ * @param {Object<string, string|null>} namespaces
1771
+ * @throws {TypeError}
1772
+ * @returns {void}
1773
+ */
1774
+ function parseDOM(nodeOrEntity, namespaces) {
1775
+ // namespaces = clone(namespaces) || {}; // Ensure we're working with a copy, so different levels in the hierarchy can treat it differently
1776
+
1777
+ /*
1778
+ if ((nodeOrEntity.prefix && nodeOrEntity.prefix.includes(':')) || (nodeOrEntity.localName && nodeOrEntity.localName.includes(':'))) {
1779
+ invalidStateError('Prefix cannot have a colon');
1780
+ }
1781
+ */
1782
+
1783
+ const type = 'nodeType' in nodeOrEntity ? nodeOrEntity.nodeType : null;
1784
+ if (!type) {
1785
+ throw new TypeError('Not an XML type');
1786
+ }
1787
+ if (type === 5) {
1788
+ // ENTITY REFERENCE (though not in browsers (was already resolved
1789
+ // anyways), ok to keep for parity with our "entity" shorthand)
1790
+ set(['&', nodeOrEntity.nodeName]);
1791
+ return;
1792
+ }
1793
+ namespaces = {
1794
+ ...namespaces
1795
+ };
1796
+ const xmlChars = /^([\u0009\u000A\u000D\u0020-\uD7FF\uE000-\uFFFD]|[\uD800-\uDBFF][\uDC00-\uDFFF])*$/u; // eslint-disable-line no-control-regex
1797
+ if ([2, 3, 4, 7, 8].includes(type) && /** @type {Node} */nodeOrEntity.nodeValue && !xmlChars.test( /** @type {Node} */nodeOrEntity.nodeValue)) {
1798
+ invalidStateError('Node has bad XML character value');
1799
+ }
1800
+
1801
+ /**
1802
+ * @type {IndexableObject}
1803
+ */
1804
+ let tmpParent;
1805
+
1806
+ /**
1807
+ * @type {Integer}
1808
+ */
1809
+ let tmpParentIdx;
1810
+
1811
+ /**
1812
+ * @returns {void}
1813
+ */
1814
+ function setTemp() {
1815
+ tmpParent = parent;
1816
+ tmpParentIdx = parentIdx;
1817
+ }
1818
+ /**
1819
+ * @returns {void}
1820
+ */
1821
+ function resetTemp() {
1822
+ parent = tmpParent;
1823
+ parentIdx = tmpParentIdx;
1824
+ parentIdx++; // Increment index in parent container of this element
1825
+ }
1826
+
1827
+ switch (type) {
1828
+ case 1:
1829
+ {
1830
+ // ELEMENT
1831
+ const node = /** @type {HTMLElement} */nodeOrEntity;
1832
+ setTemp();
1833
+ const nodeName = node.nodeName.toLowerCase(); // Todo: for XML, should not lower-case
1834
+
1835
+ setChildren(); // Build child array since elements are, except at the top level, encapsulated in arrays
1836
+ set(nodeName);
1837
+
1838
+ /**
1839
+ * @type {{[key: string]: string|null} & {xmlns?: string|null}}
1840
+ */
1841
+ const start = {};
1842
+ let hasNamespaceDeclaration = false;
1843
+ if (namespaces[node.prefix || ''] !== node.namespaceURI) {
1844
+ namespaces[node.prefix || ''] = node.namespaceURI;
1845
+ if (node.prefix) {
1846
+ start['xmlns:' + node.prefix] = node.namespaceURI;
1847
+ } else if (node.namespaceURI) {
1848
+ start.xmlns = node.namespaceURI;
1849
+ } else {
1850
+ start.xmlns = null;
1851
+ }
1852
+ hasNamespaceDeclaration = true;
1853
+ }
1854
+ if (node.attributes.length) {
1855
+ set([...node.attributes].reduce(function (obj, att) {
1856
+ obj[att.name] = att.value; // Attr.nodeName and Attr.nodeValue are deprecated as of DOM4 as Attr no longer inherits from Node, so we can safely use name and value
1857
+ return obj;
1858
+ }, start));
1859
+ } else if (hasNamespaceDeclaration) {
1860
+ set(start);
1861
+ }
1862
+ const {
1863
+ childNodes
1864
+ } = node;
1865
+ if (childNodes.length) {
1866
+ setChildren(); // Element children array container
1867
+ [...childNodes].forEach(function (childNode) {
1868
+ parseDOM(childNode, namespaces);
1869
+ });
1870
+ }
1871
+ resetTemp();
1872
+ break;
1873
+ }
1874
+ case 2:
1875
+ {
1876
+ // ATTRIBUTE (should only get here if passing in an attribute node)
1877
+ const node = /** @type {Attr} */nodeOrEntity;
1878
+ set({
1879
+ $attribute: [node.namespaceURI, node.name, node.value]
1880
+ });
1881
+ break;
1882
+ }
1883
+ case 3:
1884
+ {
1885
+ // TEXT
1886
+ const node = /** @type {Text} */nodeOrEntity;
1887
+ /* c8 ignore next 3 */
1888
+ if (!node.nodeValue) {
1889
+ throw new Error('Unexpected null comment value');
1890
+ }
1891
+ if (stripWhitespace && /^\s+$/u.test(node.nodeValue)) {
1892
+ set('');
1893
+ return;
1894
+ }
1895
+ set(node.nodeValue);
1896
+ break;
1897
+ }
1898
+ case 4:
1899
+ {
1900
+ var _node$nodeValue;
1901
+ // CDATA
1902
+ const node = /** @type {CDATASection} */nodeOrEntity;
1903
+ if ((_node$nodeValue = node.nodeValue) !== null && _node$nodeValue !== void 0 && _node$nodeValue.includes(']]' + '>')) {
1904
+ invalidStateError('CDATA cannot end with closing ]]>');
1905
+ }
1906
+ set(['![', node.nodeValue]);
1907
+ break;
1908
+ }
1909
+ // case 5:
1910
+ // Handled earlier
1911
+ case 7:
1912
+ {
1913
+ // PROCESSING INSTRUCTION
1914
+ const node = /** @type {ProcessingInstruction} */nodeOrEntity;
1915
+ if (/^xml$/iu.test(node.target)) {
1916
+ invalidStateError('Processing instructions cannot be "xml".');
1917
+ }
1918
+ if (node.target.includes('?>')) {
1919
+ invalidStateError('Processing instruction targets cannot include ?>');
1920
+ }
1921
+ if (node.target.includes(':')) {
1922
+ invalidStateError('The processing instruction target cannot include ":"');
1923
+ }
1924
+ if (node.data.includes('?>')) {
1925
+ invalidStateError('Processing instruction data cannot include ?>');
1926
+ }
1927
+ set(['?', node.target, node.data]); // Todo: Could give option to attempt to convert value back into object if has pseudo-attributes
1928
+ break;
1929
+ }
1930
+ case 8:
1931
+ {
1932
+ // COMMENT
1933
+ const node = /** @type {Comment} */nodeOrEntity;
1934
+ /* c8 ignore next 3 */
1935
+ if (!node.nodeValue) {
1936
+ throw new Error('Unexpected null comment value');
1937
+ }
1938
+ if (node.nodeValue.includes('--') || node.nodeValue.length && node.nodeValue.lastIndexOf('-') === node.nodeValue.length - 1) {
1939
+ invalidStateError('Comments cannot include --');
1940
+ }
1941
+ set(['!', node.nodeValue]);
1942
+ break;
1943
+ }
1944
+ case 9:
1945
+ {
1946
+ // DOCUMENT
1947
+ const node = /** @type {Document} */nodeOrEntity;
1948
+ setTemp();
1949
+ const docObj = {
1950
+ $document: {
1951
+ childNodes: []
1952
+ }
1953
+ };
1954
+ set(docObj); // doc.implementation.createHTMLDocument
1955
+
1956
+ // Set position to fragment's array children
1957
+ setObj('$document', 'childNodes');
1958
+ const {
1959
+ childNodes
1960
+ } = node;
1961
+ if (!childNodes.length) {
1962
+ invalidStateError('Documents must have a child node');
1963
+ }
1964
+ // set({$xmlDocument: []}); // doc.implementation.createDocument // Todo: use this conditionally
1965
+
1966
+ [...childNodes].forEach(function (childNode) {
1967
+ // Can't just do documentElement as there may be doctype, comments, etc.
1968
+ // No need for setChildren, as we have already built the container array
1969
+ parseDOM(childNode, namespaces);
1970
+ });
1971
+ resetTemp();
1972
+ break;
1973
+ }
1974
+ case 10:
1975
+ {
1976
+ // DOCUMENT TYPE
1977
+ const node = /** @type {DocumentType} */nodeOrEntity;
1978
+ setTemp();
1979
+
1980
+ // Can create directly by doc.implementation.createDocumentType
1981
+ const start = {
1982
+ $DOCTYPE: {
1983
+ name: /** @type {DocumentType} */node.name
1984
+ }
1985
+ };
1986
+ const pubIdChar = /^(\u0020|\u000D|\u000A|[a-zA-Z0-9]|[-'()+,./:=?;!*#@$_%])*$/u; // eslint-disable-line no-control-regex
1987
+ if (!pubIdChar.test( /** @type {DocumentType} */node.publicId)) {
1988
+ invalidStateError('A publicId must have valid characters.');
1989
+ }
1990
+ addExternalID(start.$DOCTYPE, node);
1991
+ // Fit in internal subset along with entities?: probably don't need as these would only differ if from DTD, and we're not rebuilding the DTD
1992
+ set(start); // Auto-generate the internalSubset instead?
1993
+
1994
+ resetTemp();
1995
+ break;
1996
+ }
1997
+ case 11:
1998
+ {
1999
+ // DOCUMENT FRAGMENT
2000
+ const node = /** @type {DocumentFragment} */nodeOrEntity;
2001
+ setTemp();
2002
+ set({
2003
+ '#': []
2004
+ });
2005
+
2006
+ // Set position to fragment's array children
2007
+ setObj('#');
2008
+ const {
2009
+ childNodes
2010
+ } = node;
2011
+ [...childNodes].forEach(function (childNode) {
2012
+ // No need for setChildren, as we have already built the container array
2013
+ parseDOM(childNode, namespaces);
2014
+ });
2015
+ resetTemp();
2016
+ break;
2017
+ }
2018
+ default:
2019
+ throw new TypeError('Not an XML type');
2020
+ }
2021
+ }
2022
+ parseDOM(dom, {});
2023
+ if (stringOutput) {
2024
+ return JSON.stringify(ret[0]);
2025
+ }
2026
+ return ret[0];
2027
+ };
2028
+
2029
+ /**
2030
+ * @param {string|HTMLElement} dom
2031
+ * @param {ToJmlConfig} [config]
2032
+ * @returns {string}
2033
+ */
2034
+ jml.toJMLString = function (dom, config) {
2035
+ return (/** @type {string} */
2036
+ jml.toJML(dom, Object.assign(config || {}, {
2037
+ stringOutput: true
2038
+ }))
2039
+ );
2040
+ };
2041
+
2042
+ /**
2043
+ *
2044
+ * @param {JamilihArray} args
2045
+ * @returns {JamilihReturn}
2046
+ */
2047
+ jml.toDOM = function (...args) {
2048
+ // Alias for jml()
2049
+ return jml(...args);
2050
+ };
2051
+
2052
+ /**
2053
+ *
2054
+ * @param {JamilihArray} args
2055
+ * @returns {string}
2056
+ */
2057
+ jml.toHTML = function (...args) {
2058
+ // Todo: Replace this with version of jml() that directly builds a string
2059
+ const ret = jml(...args);
2060
+ switch (ret.nodeType) {
2061
+ case 1:
2062
+ {
2063
+ // Element
2064
+ // Todo: deal with serialization of properties like 'selected',
2065
+ // 'checked', 'value', 'defaultValue', 'for', 'dataset', 'on*',
2066
+ // 'style'! (i.e., need to build a string ourselves)
2067
+ return (/** @type {HTMLElement} */ret.outerHTML
2068
+ );
2069
+ }
2070
+ case 2:
2071
+ {
2072
+ // ATTR
2073
+ return `${
2074
+ /** @type {Attr} */ret.name}="${
2075
+ /** @type {Attr} */ret.value.replace(/"/gu, '&quot;')}"`;
2076
+ }
2077
+ case 3:
2078
+ {
2079
+ // TEXT
2080
+ // Fallthrough
2081
+ // } case 4: { // CDATA
2082
+ /* c8 ignore next 3 */
2083
+ if (!ret.nodeValue) {
2084
+ throw new TypeError('Unexpected null Text node');
2085
+ }
2086
+ return (/** @type {Text|CDATASection} */ret.nodeValue
2087
+ );
2088
+ // case 5: // Entity Reference Node
2089
+ // No 6: Entity Node
2090
+ // No 12: Notation Node
2091
+ // } case 7: { // PROCESSING INSTRUCTION
2092
+ // const node = /** @type {ProcessingInstruction} */ (ret);
2093
+ // return `<?${node.target} ${node.data}?>`;
2094
+ // } case 8: { // Comment
2095
+ // return `<!--${ret.nodeValue}-->`;
2096
+ }
2097
+ case 9:
2098
+ case 11:
2099
+ {
2100
+ // DOCUMENT FRAGMENT
2101
+ const node = /** @type {DocumentFragment} */ret;
2102
+ return [...node.childNodes].map(childNode => {
2103
+ return jml.toHTML( /** @type {JamilihFirstArgument} */childNode);
2104
+ }).join('');
2105
+ }
2106
+ case 10:
2107
+ {
2108
+ // DOCUMENT TYPE
2109
+ const node = /** @type {DocumentType} */ret;
2110
+ return `<!DOCTYPE ${node.name}${node.publicId ? ` PUBLIC "${node.publicId}" "${node.systemId}"` : node.systemId ? ` SYSTEM "${node.systemId}"` : ``}>`;
2111
+ /* c8 ignore next 3 */
2112
+ }
2113
+ default:
2114
+ throw new Error('Unexpected node type');
2115
+ }
2116
+ };
2117
+
2118
+ /**
2119
+ *
2120
+ * @param {JamilihArray} args
2121
+ * @returns {string}
2122
+ */
2123
+ jml.toDOMString = function (...args) {
2124
+ // Alias for jml.toHTML for parity with jml.toJMLString
2125
+ return jml.toHTML(...args);
2126
+ };
2127
+
2128
+ /**
2129
+ *
2130
+ * @param {JamilihArray} args
2131
+ * @returns {string}
2132
+ */
2133
+ jml.toXML = function (...args) {
2134
+ if (!win) {
2135
+ throw new Error('No window object set');
2136
+ }
2137
+ const ret = jml(...args);
2138
+ return new /** @type {import('jsdom').DOMWindow} */win.XMLSerializer().serializeToString(ret);
2139
+ };
2140
+
2141
+ /**
2142
+ *
2143
+ * @param {JamilihArray} args
2144
+ * @returns {string}
2145
+ */
2146
+ jml.toXMLDOMString = function (...args) {
2147
+ // Alias for jml.toXML for parity with jml.toJMLString
2148
+ return jml.toXML(...args);
2149
+ };
2150
+
2151
+ /**
2152
+ * Element-aware wrapper for `Map`.
2153
+ */
2154
+ class JamilihMap extends Map {
2155
+ /**
2156
+ * @param {?(string|HTMLElement)} element
2157
+ * @returns {ArbitraryValue}
2158
+ */
2159
+ get(element) {
2160
+ const elem = typeof element === 'string' ? $(element) : element;
2161
+ return super.get.call(this, elem);
2162
+ }
2163
+ /**
2164
+ * @param {string|HTMLElement} element
2165
+ * @param {ArbitraryValue} value
2166
+ * @returns {ArbitraryValue}
2167
+ */
2168
+ set(element, value) {
2169
+ const elem = typeof element === 'string' ? $(element) : element;
2170
+ return super.set.call(this, elem, value);
2171
+ }
2172
+ /**
2173
+ * @param {string|HTMLElement} element
2174
+ * @param {string} methodName
2175
+ * @param {...ArbitraryValue} args
2176
+ * @returns {ArbitraryValue}
2177
+ */
2178
+ invoke(element, methodName, ...args) {
2179
+ const elem = typeof element === 'string' ? $(element) : element;
2180
+ return this.get(elem)[methodName](elem, ...args);
2181
+ }
2182
+ }
2183
+
2184
+ /**
2185
+ * Element-aware wrapper for `WeakMap`.
2186
+ * @extends {WeakMap<any>}
2187
+ */
2188
+ class JamilihWeakMap extends WeakMap {
2189
+ /**
2190
+ * @param {HTMLElement} element
2191
+ * @returns {ArbitraryValue}
2192
+ */
2193
+ get(element) {
2194
+ const elem = typeof element === 'string' ? $(element) : element;
2195
+ if (!elem) {
2196
+ throw new Error("Can't find the element");
2197
+ }
2198
+ return super.get.call(this, elem);
2199
+ }
2200
+ /**
2201
+ * @param {HTMLElement} element
2202
+ * @param {ArbitraryValue} value
2203
+ * @returns {ArbitraryValue}
2204
+ */
2205
+ set(element, value) {
2206
+ const elem = typeof element === 'string' ? $(element) : element;
2207
+ if (!elem) {
2208
+ throw new Error("Can't find the element");
2209
+ }
2210
+ return super.set.call(this, elem, value);
2211
+ }
2212
+ /**
2213
+ * @param {string|HTMLElement} element
2214
+ * @param {string} methodName
2215
+ * @param {...ArbitraryValue} args
2216
+ * @returns {ArbitraryValue}
2217
+ */
2218
+ invoke(element, methodName, ...args) {
2219
+ const elem = typeof element === 'string' ? $(element) : element;
2220
+ if (!elem) {
2221
+ throw new Error("Can't find the element");
2222
+ }
2223
+ return this.get(elem)[methodName](elem, ...args);
2224
+ }
2225
+ }
2226
+ jml.Map = JamilihMap;
2227
+ jml.WeakMap = JamilihWeakMap;
2228
+
2229
+ /**
2230
+ * @typedef {[JamilihWeakMap|JamilihMap, HTMLElement]} MapAndElementArray
2231
+ */
2232
+
2233
+ /**
2234
+ * @param {{[key: string]: any}} obj
2235
+ * @param {JamilihArrayPostOptions} args
2236
+ * @returns {MapAndElementArray}
2237
+ */
2238
+ jml.weak = function (obj, ...args) {
2239
+ const map = new JamilihWeakMap();
2240
+ const elem = jml({
2241
+ $map: [map, obj]
2242
+ }, ...args);
2243
+ return [map, /** @type {HTMLElement} */elem];
2244
+ };
2245
+
2246
+ /**
2247
+ * @param {ArbitraryValue} obj
2248
+ * @param {JamilihArrayPostOptions} args
2249
+ * @returns {MapAndElementArray}
2250
+ */
2251
+ jml.strong = function (obj, ...args) {
2252
+ const map = new JamilihMap();
2253
+ const elem = jml({
2254
+ $map: [map, obj]
2255
+ }, ...args);
2256
+ return [map, /** @type {HTMLElement} */elem];
2257
+ };
2258
+
2259
+ /**
2260
+ * @param {string|HTMLElement} element If a string, will be interpreted as a selector
2261
+ * @param {symbol|string} sym If a string, will be used with `Symbol.for`
2262
+ * @returns {ArbitraryValue} The value associated with the symbol
2263
+ */
2264
+ jml.symbol = jml.sym = jml.for = function (element, sym) {
2265
+ const elem = typeof element === 'string' ? $(element) : element;
2266
+
2267
+ // @ts-expect-error Should be ok
2268
+ return elem[typeof sym === 'symbol' ? sym : Symbol.for(sym)];
2269
+ };
2270
+
2271
+ /**
2272
+ * @typedef {((elem: HTMLElement, ...args: any[]) => void)|{[key: string]: (elem: HTMLElement, ...args: any[]) => void}} MapCommand
2273
+ */
2274
+
2275
+ /**
2276
+ * @param {?(string|HTMLElement)} elem If a string, will be interpreted as a selector
2277
+ * @param {symbol|string|Map<HTMLElement, MapCommand>|WeakMap<HTMLElement, MapCommand>} symOrMap If a string, will be used with `Symbol.for`
2278
+ * @param {string|any} methodName Can be `any` if the symbol or map directly
2279
+ * points to a function (it is then used as the first argument).
2280
+ * @param {ArbitraryValue[]} args
2281
+ * @returns {ArbitraryValue}
2282
+ */
2283
+ jml.command = function (elem, symOrMap, methodName, ...args) {
2284
+ elem = typeof elem === 'string' ? $(elem) : elem;
2285
+ if (!elem) {
2286
+ throw new Error('No element found');
2287
+ }
2288
+ let func;
2289
+ if (['symbol', 'string'].includes(typeof symOrMap)) {
2290
+ func = jml.sym(elem, /** @type {symbol|string} */symOrMap);
2291
+ if (typeof func === 'function') {
2292
+ return func(methodName, ...args); // Already has `this` bound to `elem`
2293
+ }
2294
+
2295
+ return func[methodName](...args);
2296
+ }
2297
+ func = /** @type {Map<HTMLElement, MapCommand>|WeakMap<HTMLElement, MapCommand>} */symOrMap.get(elem);
2298
+ if (!func) {
2299
+ throw new Error('No map found');
2300
+ }
2301
+ if (typeof func === 'function') {
2302
+ return func.call(elem, methodName, ...args);
2303
+ }
2304
+ return func[methodName](elem, ...args);
2305
+ // return func[methodName].call(elem, ...args);
2306
+ };
2307
+
2308
+ /**
2309
+ * Expects properties `document`, `XMLSerializer`, and `DOMParser`.
2310
+ * Also updates `body` with `document.body`.
2311
+ * @param {import('jsdom').DOMWindow|HTMLWindow|undefined} wind
2312
+ * @returns {void}
2313
+ */
2314
+ jml.setWindow = wind => {
2315
+ var _win2;
2316
+ win = wind;
2317
+ doc = (_win2 = win) === null || _win2 === void 0 ? void 0 : _win2.document;
2318
+ if (doc && doc.body) {
2319
+ // eslint-disable-next-line prefer-destructuring -- Needed for typing
2320
+ body = /** @type {HTMLBodyElement} */doc.body;
2321
+ }
2322
+ };
2323
+
2324
+ /**
2325
+ * @returns {import('jsdom').DOMWindow|HTMLWindow}
2326
+ */
2327
+ jml.getWindow = () => {
2328
+ if (!win) {
2329
+ throw new Error('No window object set');
2330
+ }
2331
+ return win;
2332
+ };
2333
+
2334
+ /**
2335
+ * Does not run Jamilih so can be further processed.
2336
+ * @param {ArbitraryValue[]} array
2337
+ * @param {ArbitraryValue} glu
2338
+ * @returns {ArbitraryValue[]}
2339
+ */
2340
+ function glue(array, glu) {
2341
+ return [...array].reduce((arr, item) => {
2342
+ arr.push(item, glu);
2343
+ return arr;
2344
+ }, []).slice(0, -1);
2345
+ }
2346
+
2347
+ /**
2348
+ * @type {HTMLBodyElement}
2349
+ */
2350
+ let body; // eslint-disable-line import/no-mutable-exports
2351
+
2352
+ /* c8 ignore next 4 */
2353
+ if (doc && doc.body) {
2354
+ // eslint-disable-next-line prefer-destructuring -- Needed for type
2355
+ body = /** @type {HTMLBodyElement} */doc.body;
2356
+ }
2357
+ const nbsp = '\u00A0'; // Very commonly needed in templates
2358
+
2359
+ export { $, $$, DOMException, body, jml as default, glue, jml, nbsp };