onejs-core 3.0.2 → 3.0.3

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 (110) hide show
  1. package/.gitattributes +2 -2
  2. package/.prettierrc +5 -5
  3. package/.vscode/settings.json +5 -5
  4. package/LICENSE +20 -20
  5. package/README.md +102 -102
  6. package/definitions/Assemblies/OneJS.Runtime.Ext.d.ts +6 -6
  7. package/definitions/Assemblies/OneJS.Runtime.d.ts +826 -826
  8. package/definitions/Assemblies/OneJS.Samples.d.ts +20 -20
  9. package/definitions/Assemblies/Unity.Mathematics.d.ts +9221 -9221
  10. package/definitions/Assemblies/UnityEditor.CoreModule.d.ts +32614 -32614
  11. package/definitions/Assemblies/UnityEngine.AIModule.d.ts +998 -998
  12. package/definitions/Assemblies/UnityEngine.AnimationModule.d.ts +3308 -3308
  13. package/definitions/Assemblies/UnityEngine.AssetBundleModule.d.ts +337 -337
  14. package/definitions/Assemblies/UnityEngine.AudioModule.d.ts +1154 -1154
  15. package/definitions/Assemblies/UnityEngine.CoreModule.d.ts +29587 -29587
  16. package/definitions/Assemblies/UnityEngine.PhysicsModule.d.ts +3137 -3137
  17. package/definitions/Assemblies/UnityEngine.TerrainModule.d.ts +1270 -1270
  18. package/definitions/Assemblies/UnityEngine.UIElementsModule.d.ts +32718 -32718
  19. package/definitions/Assemblies/UnityEngine.UnityAnalyticsCommonModule.d.ts +274 -274
  20. package/definitions/Assemblies/index.d.ts +16 -16
  21. package/definitions/Assemblies/mscorlib.d.ts +19416 -19416
  22. package/definitions/augments.d.ts +66 -66
  23. package/definitions/globals.d.ts +53 -53
  24. package/definitions/index.d.ts +11 -11
  25. package/definitions/jsx.d.ts +570 -570
  26. package/definitions/modules.d.ts +32 -32
  27. package/definitions/onejs.d.ts +171 -171
  28. package/definitions/preact.jsx.d.ts +6 -6
  29. package/definitions/proto-overrides.d.ts +41 -41
  30. package/definitions/puerts.d.ts +30 -30
  31. package/definitions/unity-engine.d.ts +22 -22
  32. package/dom/document.ts +115 -115
  33. package/dom/dom-style.ts +36 -36
  34. package/dom/dom.ts +376 -376
  35. package/dom/index.ts +3 -3
  36. package/index.ts +59 -59
  37. package/jsr.json +9 -9
  38. package/math/README.md +212 -212
  39. package/math/index.ts +487 -487
  40. package/package.json +33 -33
  41. package/preloads/inject.ts +43 -43
  42. package/scripts/esbuild/copy-assets.mjs +94 -94
  43. package/scripts/esbuild/decorator-fix.mjs +17 -17
  44. package/scripts/esbuild/import-transform.mjs +100 -100
  45. package/scripts/esbuild/index.mjs +3 -3
  46. package/scripts/esbuild/watch-output.mjs +38 -38
  47. package/scripts/postcss/cleanup-plugin.cjs +89 -89
  48. package/scripts/postcss/onejs-tw-config.cjs +252 -252
  49. package/scripts/postcss/optional-import-plugin.cjs +26 -26
  50. package/scripts/postcss/tailwind-logging-plugin.cjs +11 -11
  51. package/scripts/postcss/unwrap-is-plugin.cjs +16 -16
  52. package/scripts/postcss/uss-transform-plugin.cjs +92 -92
  53. package/scripts/switch.cjs +290 -290
  54. package/styling/index.tsx +32 -32
  55. package/styling/utils/generateAlphabeticName.ts +20 -20
  56. package/styling/utils/generateComponentId.ts +5 -5
  57. package/styling/utils/hash.ts +46 -46
  58. package/tsconfig.json +24 -24
  59. package/typings.d.ts +5 -5
  60. package/utils/arrays.ts +10 -10
  61. package/utils/color-palettes.ts +2 -2
  62. package/utils/color-parser.ts +248 -248
  63. package/utils/float-parser.ts +30 -30
  64. package/utils/index.ts +15 -15
  65. package/utils/subscribe.ts +16 -16
  66. package/utils/system.ts +16 -16
  67. package/dist/csharp/index.d.ts +0 -3
  68. package/dist/csharp/index.js +0 -3
  69. package/dist/dom/document.d.ts +0 -30
  70. package/dist/dom/document.js +0 -89
  71. package/dist/dom/dom-style.d.ts +0 -9
  72. package/dist/dom/dom-style.js +0 -27
  73. package/dist/dom/dom.d.ts +0 -83
  74. package/dist/dom/dom.js +0 -313
  75. package/dist/dom/index.d.ts +0 -4
  76. package/dist/dom/index.js +0 -4
  77. package/dist/dom/selector.d.ts +0 -0
  78. package/dist/dom/selector.js +0 -0
  79. package/dist/index.d.ts +0 -25
  80. package/dist/index.js +0 -45
  81. package/dist/math/index.d.ts +0 -86
  82. package/dist/math/index.js +0 -361
  83. package/dist/preloads/inject.d.ts +0 -3
  84. package/dist/preloads/inject.js +0 -36
  85. package/dist/styling/index.d.ts +0 -10
  86. package/dist/styling/index.js +0 -28
  87. package/dist/styling/utils/generateAlphabeticName.d.ts +0 -1
  88. package/dist/styling/utils/generateAlphabeticName.js +0 -16
  89. package/dist/styling/utils/generateComponentId.d.ts +0 -1
  90. package/dist/styling/utils/generateComponentId.js +0 -5
  91. package/dist/styling/utils/hash.d.ts +0 -5
  92. package/dist/styling/utils/hash.js +0 -34
  93. package/dist/utils/arrays.d.ts +0 -1
  94. package/dist/utils/arrays.js +0 -10
  95. package/dist/utils/color-palettes.d.ts +0 -2
  96. package/dist/utils/color-palettes.js +0 -2
  97. package/dist/utils/color-parser.d.ts +0 -161
  98. package/dist/utils/color-parser.js +0 -241
  99. package/dist/utils/float-parser.d.ts +0 -7
  100. package/dist/utils/float-parser.js +0 -23
  101. package/dist/utils/index.d.ts +0 -12
  102. package/dist/utils/index.js +0 -15
  103. package/dist/utils/responsive.d.ts +0 -4
  104. package/dist/utils/responsive.js +0 -23
  105. package/dist/utils/subscribe.d.ts +0 -4
  106. package/dist/utils/subscribe.js +0 -10
  107. package/dist/utils/system.d.ts +0 -1
  108. package/dist/utils/system.js +0 -16
  109. package/dist/utils/toJsArray.d.ts +0 -1
  110. package/dist/utils/toJsArray.js +0 -10
package/dom/dom.ts CHANGED
@@ -1,377 +1,377 @@
1
- import { EventBase } from "UnityEngine/UIElements"
2
- import { DomStyleWrapper } from "./dom-style"
3
-
4
- export class DomWrapper {
5
- public get _dom(): CS.OneJS.Dom.Dom { return this.dom }
6
- public get ve(): CS.UnityEngine.UIElements.VisualElement { return this.dom.ve }
7
- public get childNodes(): DomWrapper[] {
8
- if (this.cachedChildNodes) return this.cachedChildNodes
9
- this.cachedChildNodes = new Array(this.dom.childNodes.Length) as DomWrapper[]
10
- var i = this.dom.childNodes.Length
11
- while (i--) {
12
- this.cachedChildNodes[i] = new DomWrapper(this.dom.childNodes.get_Item(i))
13
- }
14
- return this.cachedChildNodes
15
- }
16
- public get firstChild(): DomWrapper | null {
17
- return this.dom.firstChild ? new DomWrapper(this.dom.firstChild) : null
18
- }
19
- public get parentNode(): DomWrapper | null {
20
- return this.dom.parentNode ? new DomWrapper(this.dom.parentNode) : null
21
- }
22
- public get nextSibling(): DomWrapper | null {
23
- return this.dom.nextSibling ? new DomWrapper(this.dom.nextSibling) : null
24
- }
25
-
26
- public get nodeType(): number { return this.dom.nodeType }
27
- public get style(): DomStyleWrapper { return this.domStyleWrapper }
28
- public get Id(): string { return this.dom.Id }
29
- public set Id(value: string) { this.dom.Id = value }
30
- public get key(): string { return this.dom.key }
31
- public set key(value: string) { this.dom.key = value }
32
- public get value(): any { return this.dom.value }
33
- public get checked(): boolean { return this.dom.checked }
34
- public get data(): any { return this.dom.data }
35
- public set data(value: any) { this.dom.data = value }
36
-
37
- public get className(): string { return this.dom.className }
38
- public set className(value: string) { this.dom.className = value }
39
- public get classList(): DomTokenList { return this.domTokenList }
40
-
41
- /**
42
- * Not using private fields because of issues with the `#private;` line
43
- * generated by tsc
44
- */
45
- dom: CS.OneJS.Dom.Dom
46
- domStyleWrapper: DomStyleWrapper
47
- domTokenList: DomTokenList
48
-
49
- cachedChildNodes: DomWrapper[] | null = null
50
- boundListeners = new WeakMap();
51
-
52
- constructor(dom: CS.OneJS.Dom.Dom) {
53
- this.dom = dom
54
- this.domStyleWrapper = new DomStyleWrapper(dom.style)
55
- this.domTokenList = new DomTokenList(dom)
56
- }
57
-
58
- // MARK: Manipulation
59
-
60
- appendChild(child: DomWrapper) {
61
- if (!child) return
62
- this.dom.appendChild(child.dom)
63
- this.cachedChildNodes = null
64
- return child
65
- }
66
-
67
- removeChild(child: DomWrapper) {
68
- if (!child) return
69
- this.dom.removeChild(child.dom)
70
- this.cachedChildNodes = null
71
- }
72
-
73
- insertBefore(a: DomWrapper, b: DomWrapper) {
74
- this.dom.insertBefore(a?._dom, b?._dom)
75
- this.cachedChildNodes = null
76
- }
77
-
78
- insertAfter(a: DomWrapper, b: DomWrapper) {
79
- this.dom.insertAfter(a?._dom, b?._dom)
80
- this.cachedChildNodes = null
81
- }
82
-
83
- before(other: DomWrapper) { // TODO: variable length args
84
- if (this.parentNode) {
85
- this.parentNode.insertBefore(other, this)
86
- }
87
- }
88
-
89
- clearChildren() {
90
- this.dom.clearChildren()
91
- this.cachedChildNodes = null
92
- }
93
-
94
- setAttribute(name: string, value: any) {
95
- this.dom.setAttribute(name, value)
96
- }
97
-
98
- removeAttribute(name: string) {
99
- this.dom.removeAttribute(name)
100
- }
101
-
102
- // MARK: Node.prototype
103
-
104
- append(child: DomWrapper) {
105
- if (!child) return
106
- this.dom.appendChild(child.dom)
107
- this.cachedChildNodes = null
108
- }
109
-
110
- cloneNode(deep: boolean = false): DomWrapper {
111
- return this
112
- }
113
-
114
- remove() {
115
- if (this.parentNode) {
116
- this.parentNode.removeChild(this)
117
- }
118
- }
119
-
120
- // MARK: Misc
121
-
122
- contains(child: DomWrapper) {
123
- if (!child) return false
124
- return this.dom.contains(child._dom)
125
- }
126
-
127
- focus() {
128
- this.dom.focus()
129
- }
130
-
131
- // MARK: Event
132
-
133
- addEventListener(type: string, listener: (event: EventBase) => void, options?: boolean | { once?: boolean }) {
134
- let boundListener = this.boundListeners.get(listener)
135
- if (!boundListener) {
136
- boundListener = listener.bind(this)
137
- this.boundListeners.set(listener, boundListener)
138
- }
139
-
140
- if (typeof options === 'object' && options.once) {
141
- const onceWrapper = (event: EventBase) => {
142
- boundListener(event)
143
- this.dom.removeEventListener(type, onceWrapper, false)
144
- }
145
- this.dom.addEventListener(type, onceWrapper, false)
146
- } else {
147
- this.dom.addEventListener(type, boundListener, options ? true : false)
148
- }
149
- }
150
-
151
- removeEventListener(type: string, listener: (event: EventBase) => void, useCapture?: boolean) {
152
- const boundListener = this.boundListeners.get(listener)
153
- if (boundListener) {
154
- this.dom.removeEventListener(type, boundListener, useCapture ? true : false)
155
- this.boundListeners.delete(listener) // isn't strictly necessary for WeakMap, but still good practice
156
- }
157
- }
158
-
159
- /**
160
- * Returns all elements matching the specified selector.
161
- * Supports basic selectors:
162
- * - Tag names: 'div'
163
- * - IDs: '#myId'
164
- * - Classes: '.myClass'
165
- * - Combinations: 'div.myClass#myId'
166
- */
167
- querySelectorAll(selector: string): DomWrapper[] {
168
- const selectorInfo = parseSelector(selector)
169
- const results: DomWrapper[] = []
170
-
171
- function traverse(element: DomWrapper) {
172
- if (elementMatchesSelector(element, selectorInfo)) {
173
- results.push(element)
174
- }
175
-
176
- for (const child of element.childNodes) {
177
- traverse(child)
178
- }
179
- }
180
-
181
- traverse(this)
182
- return results
183
- }
184
-
185
- /**
186
- * Returns the first element matching the specified selector.
187
- * Supports the same basic selectors as querySelectorAll.
188
- */
189
- querySelector(selector: string): DomWrapper | null {
190
- const selectorInfo = parseSelector(selector)
191
-
192
- function traverse(element: DomWrapper): DomWrapper | null {
193
- if (elementMatchesSelector(element, selectorInfo)) {
194
- return element
195
- }
196
-
197
- for (const child of element.childNodes) {
198
- const match = traverse(child)
199
- if (match) {
200
- return match
201
- }
202
- }
203
-
204
- return null
205
- }
206
-
207
- return traverse(this)
208
- }
209
- }
210
-
211
- interface SelectorInfo {
212
- tag?: string
213
- id?: string
214
- classes: string[]
215
- }
216
-
217
- function parseSelector(selector: string): SelectorInfo {
218
- const selectorInfo: SelectorInfo = {
219
- classes: []
220
- }
221
-
222
- // Handle ID
223
- const idMatch = selector.match(/#([^.#\s]+)/)
224
- if (idMatch) {
225
- selectorInfo.id = idMatch[1]
226
- selector = selector.replace(idMatch[0], '')
227
- }
228
-
229
- // Handle classes
230
- const classMatches = selector.match(/\.([^.#\s]+)/g)
231
- if (classMatches) {
232
- selectorInfo.classes = classMatches.map(c => c.substring(1))
233
- selector = selector.replace(/\.[^.#\s]+/g, '')
234
- }
235
-
236
- // Handle tag name (what's left after removing id and classes)
237
- const tagName = selector.trim()
238
- if (tagName) {
239
- selectorInfo.tag = tagName.toLowerCase()
240
- }
241
-
242
- return selectorInfo
243
- }
244
-
245
- function elementMatchesSelector(element: DomWrapper, selectorInfo: SelectorInfo): boolean {
246
- // Check tag name
247
- if (selectorInfo.tag && element.ve.GetType().Name.toLowerCase() !== selectorInfo.tag) {
248
- return false
249
- }
250
-
251
- // Check ID
252
- if (selectorInfo.id && element.Id !== selectorInfo.id) {
253
- return false
254
- }
255
-
256
- // Check classes
257
- if (selectorInfo.classes.length > 0) {
258
- const elementClasses = element.className.split(' ').filter(c => c)
259
- for (const className of selectorInfo.classes) {
260
- if (!elementClasses.includes(className)) {
261
- return false
262
- }
263
- }
264
- }
265
-
266
- return true
267
- }
268
-
269
- export function querySelectorAll(root: DomWrapper, selector: string): DomWrapper[] {
270
- const results: DomWrapper[] = []
271
- const selectorInfo = parseSelector(selector)
272
-
273
- function traverse(element: DomWrapper) {
274
- // Check if current element matches
275
- if (elementMatchesSelector(element, selectorInfo)) {
276
- results.push(element)
277
- }
278
-
279
- // Recursively check children
280
- for (const child of element.childNodes) {
281
- traverse(child)
282
- }
283
- }
284
-
285
- traverse(root)
286
- return results
287
- }
288
-
289
- export function querySelector(root: DomWrapper, selector: string): DomWrapper | null {
290
- const selectorInfo = parseSelector(selector)
291
-
292
- function traverse(element: DomWrapper): DomWrapper | null {
293
- // Check if current element matches
294
- if (elementMatchesSelector(element, selectorInfo)) {
295
- return element
296
- }
297
-
298
- // Recursively check children
299
- for (const child of element.childNodes) {
300
- const match = traverse(child)
301
- if (match) {
302
- return match
303
- }
304
- }
305
-
306
- return null
307
- }
308
-
309
- return traverse(root)
310
- }
311
-
312
- class DomTokenList {
313
- dom: CS.OneJS.Dom.Dom
314
-
315
- constructor(dom: CS.OneJS.Dom.Dom) {
316
- this.dom = dom
317
- }
318
-
319
- _tokens(): string[] {
320
- return this.dom.className.trim().split(/\s+/).filter(Boolean)
321
- }
322
-
323
- _update(tokens: string[]) {
324
- this.dom.className = tokens.join(' ')
325
- }
326
-
327
- add(...tokens: string[]) {
328
- const set = new Set(this._tokens())
329
- tokens.forEach(t => t && set.add(t))
330
- this._update(Array.from(set))
331
- }
332
-
333
- remove(...tokens: string[]) {
334
- const set = new Set(this._tokens())
335
- tokens.forEach(t => set.delete(t))
336
- this._update(Array.from(set))
337
- }
338
-
339
- toggle(token: string, force?: boolean): boolean {
340
- if (!token) return false
341
- const has = this.contains(token)
342
- if (force === true || (!has && force !== false)) {
343
- this.add(token)
344
- return true
345
- }
346
- if (has && (force === false || force === undefined)) {
347
- this.remove(token)
348
- return false
349
- }
350
- return has
351
- }
352
-
353
- contains(token: string): boolean {
354
- return this._tokens().includes(token)
355
- }
356
-
357
- replace(oldToken: string, newToken: string): boolean {
358
- if (!this.contains(oldToken)) return false
359
- const tokens = this._tokens().map(t => (t === oldToken ? newToken : t))
360
- this._update(tokens)
361
- return true
362
- }
363
-
364
- toString(): string {
365
- return this.dom.className
366
- }
367
-
368
- get length(): number { return this._tokens().length }
369
- item(index: number): string | null {
370
- const t = this._tokens()
371
- return index >= 0 && index < t.length ? t[index] : null
372
- }
373
-
374
- [Symbol.iterator](): Iterator<string> {
375
- return this._tokens()[Symbol.iterator]()
376
- }
1
+ import { EventBase } from "UnityEngine/UIElements"
2
+ import { DomStyleWrapper } from "./dom-style"
3
+
4
+ export class DomWrapper {
5
+ public get _dom(): CS.OneJS.Dom.Dom { return this.dom }
6
+ public get ve(): CS.UnityEngine.UIElements.VisualElement { return this.dom.ve }
7
+ public get childNodes(): DomWrapper[] {
8
+ if (this.cachedChildNodes) return this.cachedChildNodes
9
+ this.cachedChildNodes = new Array(this.dom.childNodes.Length) as DomWrapper[]
10
+ var i = this.dom.childNodes.Length
11
+ while (i--) {
12
+ this.cachedChildNodes[i] = new DomWrapper(this.dom.childNodes.get_Item(i))
13
+ }
14
+ return this.cachedChildNodes
15
+ }
16
+ public get firstChild(): DomWrapper | null {
17
+ return this.dom.firstChild ? new DomWrapper(this.dom.firstChild) : null
18
+ }
19
+ public get parentNode(): DomWrapper | null {
20
+ return this.dom.parentNode ? new DomWrapper(this.dom.parentNode) : null
21
+ }
22
+ public get nextSibling(): DomWrapper | null {
23
+ return this.dom.nextSibling ? new DomWrapper(this.dom.nextSibling) : null
24
+ }
25
+
26
+ public get nodeType(): number { return this.dom.nodeType }
27
+ public get style(): DomStyleWrapper { return this.domStyleWrapper }
28
+ public get Id(): string { return this.dom.Id }
29
+ public set Id(value: string) { this.dom.Id = value }
30
+ public get key(): string { return this.dom.key }
31
+ public set key(value: string) { this.dom.key = value }
32
+ public get value(): any { return this.dom.value }
33
+ public get checked(): boolean { return this.dom.checked }
34
+ public get data(): any { return this.dom.data }
35
+ public set data(value: any) { this.dom.data = value }
36
+
37
+ public get className(): string { return this.dom.className }
38
+ public set className(value: string) { this.dom.className = value }
39
+ public get classList(): DomTokenList { return this.domTokenList }
40
+
41
+ /**
42
+ * Not using private fields because of issues with the `#private;` line
43
+ * generated by tsc
44
+ */
45
+ dom: CS.OneJS.Dom.Dom
46
+ domStyleWrapper: DomStyleWrapper
47
+ domTokenList: DomTokenList
48
+
49
+ cachedChildNodes: DomWrapper[] | null = null
50
+ boundListeners = new WeakMap();
51
+
52
+ constructor(dom: CS.OneJS.Dom.Dom) {
53
+ this.dom = dom
54
+ this.domStyleWrapper = new DomStyleWrapper(dom.style)
55
+ this.domTokenList = new DomTokenList(dom)
56
+ }
57
+
58
+ // MARK: Manipulation
59
+
60
+ appendChild(child: DomWrapper) {
61
+ if (!child) return
62
+ this.dom.appendChild(child.dom)
63
+ this.cachedChildNodes = null
64
+ return child
65
+ }
66
+
67
+ removeChild(child: DomWrapper) {
68
+ if (!child) return
69
+ this.dom.removeChild(child.dom)
70
+ this.cachedChildNodes = null
71
+ }
72
+
73
+ insertBefore(a: DomWrapper, b: DomWrapper) {
74
+ this.dom.insertBefore(a?._dom, b?._dom)
75
+ this.cachedChildNodes = null
76
+ }
77
+
78
+ insertAfter(a: DomWrapper, b: DomWrapper) {
79
+ this.dom.insertAfter(a?._dom, b?._dom)
80
+ this.cachedChildNodes = null
81
+ }
82
+
83
+ before(other: DomWrapper) { // TODO: variable length args
84
+ if (this.parentNode) {
85
+ this.parentNode.insertBefore(other, this)
86
+ }
87
+ }
88
+
89
+ clearChildren() {
90
+ this.dom.clearChildren()
91
+ this.cachedChildNodes = null
92
+ }
93
+
94
+ setAttribute(name: string, value: any) {
95
+ this.dom.setAttribute(name, value)
96
+ }
97
+
98
+ removeAttribute(name: string) {
99
+ this.dom.removeAttribute(name)
100
+ }
101
+
102
+ // MARK: Node.prototype
103
+
104
+ append(child: DomWrapper) {
105
+ if (!child) return
106
+ this.dom.appendChild(child.dom)
107
+ this.cachedChildNodes = null
108
+ }
109
+
110
+ cloneNode(deep: boolean = false): DomWrapper {
111
+ return this
112
+ }
113
+
114
+ remove() {
115
+ if (this.parentNode) {
116
+ this.parentNode.removeChild(this)
117
+ }
118
+ }
119
+
120
+ // MARK: Misc
121
+
122
+ contains(child: DomWrapper) {
123
+ if (!child) return false
124
+ return this.dom.contains(child._dom)
125
+ }
126
+
127
+ focus() {
128
+ this.dom.focus()
129
+ }
130
+
131
+ // MARK: Event
132
+
133
+ addEventListener(type: string, listener: (event: EventBase) => void, options?: boolean | { once?: boolean }) {
134
+ let boundListener = this.boundListeners.get(listener)
135
+ if (!boundListener) {
136
+ boundListener = listener.bind(this)
137
+ this.boundListeners.set(listener, boundListener)
138
+ }
139
+
140
+ if (typeof options === 'object' && options.once) {
141
+ const onceWrapper = (event: EventBase) => {
142
+ boundListener(event)
143
+ this.dom.removeEventListener(type, onceWrapper, false)
144
+ }
145
+ this.dom.addEventListener(type, onceWrapper, false)
146
+ } else {
147
+ this.dom.addEventListener(type, boundListener, options ? true : false)
148
+ }
149
+ }
150
+
151
+ removeEventListener(type: string, listener: (event: EventBase) => void, useCapture?: boolean) {
152
+ const boundListener = this.boundListeners.get(listener)
153
+ if (boundListener) {
154
+ this.dom.removeEventListener(type, boundListener, useCapture ? true : false)
155
+ this.boundListeners.delete(listener) // isn't strictly necessary for WeakMap, but still good practice
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Returns all elements matching the specified selector.
161
+ * Supports basic selectors:
162
+ * - Tag names: 'div'
163
+ * - IDs: '#myId'
164
+ * - Classes: '.myClass'
165
+ * - Combinations: 'div.myClass#myId'
166
+ */
167
+ querySelectorAll(selector: string): DomWrapper[] {
168
+ const selectorInfo = parseSelector(selector)
169
+ const results: DomWrapper[] = []
170
+
171
+ function traverse(element: DomWrapper) {
172
+ if (elementMatchesSelector(element, selectorInfo)) {
173
+ results.push(element)
174
+ }
175
+
176
+ for (const child of element.childNodes) {
177
+ traverse(child)
178
+ }
179
+ }
180
+
181
+ traverse(this)
182
+ return results
183
+ }
184
+
185
+ /**
186
+ * Returns the first element matching the specified selector.
187
+ * Supports the same basic selectors as querySelectorAll.
188
+ */
189
+ querySelector(selector: string): DomWrapper | null {
190
+ const selectorInfo = parseSelector(selector)
191
+
192
+ function traverse(element: DomWrapper): DomWrapper | null {
193
+ if (elementMatchesSelector(element, selectorInfo)) {
194
+ return element
195
+ }
196
+
197
+ for (const child of element.childNodes) {
198
+ const match = traverse(child)
199
+ if (match) {
200
+ return match
201
+ }
202
+ }
203
+
204
+ return null
205
+ }
206
+
207
+ return traverse(this)
208
+ }
209
+ }
210
+
211
+ interface SelectorInfo {
212
+ tag?: string
213
+ id?: string
214
+ classes: string[]
215
+ }
216
+
217
+ function parseSelector(selector: string): SelectorInfo {
218
+ const selectorInfo: SelectorInfo = {
219
+ classes: []
220
+ }
221
+
222
+ // Handle ID
223
+ const idMatch = selector.match(/#([^.#\s]+)/)
224
+ if (idMatch) {
225
+ selectorInfo.id = idMatch[1]
226
+ selector = selector.replace(idMatch[0], '')
227
+ }
228
+
229
+ // Handle classes
230
+ const classMatches = selector.match(/\.([^.#\s]+)/g)
231
+ if (classMatches) {
232
+ selectorInfo.classes = classMatches.map(c => c.substring(1))
233
+ selector = selector.replace(/\.[^.#\s]+/g, '')
234
+ }
235
+
236
+ // Handle tag name (what's left after removing id and classes)
237
+ const tagName = selector.trim()
238
+ if (tagName) {
239
+ selectorInfo.tag = tagName.toLowerCase()
240
+ }
241
+
242
+ return selectorInfo
243
+ }
244
+
245
+ function elementMatchesSelector(element: DomWrapper, selectorInfo: SelectorInfo): boolean {
246
+ // Check tag name
247
+ if (selectorInfo.tag && element.ve.GetType().Name.toLowerCase() !== selectorInfo.tag) {
248
+ return false
249
+ }
250
+
251
+ // Check ID
252
+ if (selectorInfo.id && element.Id !== selectorInfo.id) {
253
+ return false
254
+ }
255
+
256
+ // Check classes
257
+ if (selectorInfo.classes.length > 0) {
258
+ const elementClasses = element.className.split(' ').filter(c => c)
259
+ for (const className of selectorInfo.classes) {
260
+ if (!elementClasses.includes(className)) {
261
+ return false
262
+ }
263
+ }
264
+ }
265
+
266
+ return true
267
+ }
268
+
269
+ export function querySelectorAll(root: DomWrapper, selector: string): DomWrapper[] {
270
+ const results: DomWrapper[] = []
271
+ const selectorInfo = parseSelector(selector)
272
+
273
+ function traverse(element: DomWrapper) {
274
+ // Check if current element matches
275
+ if (elementMatchesSelector(element, selectorInfo)) {
276
+ results.push(element)
277
+ }
278
+
279
+ // Recursively check children
280
+ for (const child of element.childNodes) {
281
+ traverse(child)
282
+ }
283
+ }
284
+
285
+ traverse(root)
286
+ return results
287
+ }
288
+
289
+ export function querySelector(root: DomWrapper, selector: string): DomWrapper | null {
290
+ const selectorInfo = parseSelector(selector)
291
+
292
+ function traverse(element: DomWrapper): DomWrapper | null {
293
+ // Check if current element matches
294
+ if (elementMatchesSelector(element, selectorInfo)) {
295
+ return element
296
+ }
297
+
298
+ // Recursively check children
299
+ for (const child of element.childNodes) {
300
+ const match = traverse(child)
301
+ if (match) {
302
+ return match
303
+ }
304
+ }
305
+
306
+ return null
307
+ }
308
+
309
+ return traverse(root)
310
+ }
311
+
312
+ class DomTokenList {
313
+ dom: CS.OneJS.Dom.Dom
314
+
315
+ constructor(dom: CS.OneJS.Dom.Dom) {
316
+ this.dom = dom
317
+ }
318
+
319
+ _tokens(): string[] {
320
+ return this.dom.className.trim().split(/\s+/).filter(Boolean)
321
+ }
322
+
323
+ _update(tokens: string[]) {
324
+ this.dom.className = tokens.join(' ')
325
+ }
326
+
327
+ add(...tokens: string[]) {
328
+ const set = new Set(this._tokens())
329
+ tokens.forEach(t => t && set.add(t))
330
+ this._update(Array.from(set))
331
+ }
332
+
333
+ remove(...tokens: string[]) {
334
+ const set = new Set(this._tokens())
335
+ tokens.forEach(t => set.delete(t))
336
+ this._update(Array.from(set))
337
+ }
338
+
339
+ toggle(token: string, force?: boolean): boolean {
340
+ if (!token) return false
341
+ const has = this.contains(token)
342
+ if (force === true || (!has && force !== false)) {
343
+ this.add(token)
344
+ return true
345
+ }
346
+ if (has && (force === false || force === undefined)) {
347
+ this.remove(token)
348
+ return false
349
+ }
350
+ return has
351
+ }
352
+
353
+ contains(token: string): boolean {
354
+ return this._tokens().includes(token)
355
+ }
356
+
357
+ replace(oldToken: string, newToken: string): boolean {
358
+ if (!this.contains(oldToken)) return false
359
+ const tokens = this._tokens().map(t => (t === oldToken ? newToken : t))
360
+ this._update(tokens)
361
+ return true
362
+ }
363
+
364
+ toString(): string {
365
+ return this.dom.className
366
+ }
367
+
368
+ get length(): number { return this._tokens().length }
369
+ item(index: number): string | null {
370
+ const t = this._tokens()
371
+ return index >= 0 && index < t.length ? t[index] : null
372
+ }
373
+
374
+ [Symbol.iterator](): Iterator<string> {
375
+ return this._tokens()[Symbol.iterator]()
376
+ }
377
377
  }