glass-easel 0.1.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 (237) hide show
  1. package/README.md +40 -0
  2. package/dist/glass_easel.all.d.ts +1 -0
  3. package/dist/glass_easel.all.js +2 -0
  4. package/dist/glass_easel.all.js.map +1 -0
  5. package/dist/glass_easel.domlike.global.d.ts +1 -0
  6. package/dist/glass_easel.domlike.global.js +2 -0
  7. package/dist/glass_easel.domlike.global.js.map +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +1 -0
  10. package/dist/types/src/backend/backend_protocol.d.ts +119 -0
  11. package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
  12. package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
  13. package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
  14. package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
  15. package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
  16. package/dist/types/src/backend/mode.d.ts +46 -0
  17. package/dist/types/src/backend/mode.d.ts.map +1 -0
  18. package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
  19. package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
  20. package/dist/types/src/behavior.d.ts +428 -0
  21. package/dist/types/src/behavior.d.ts.map +1 -0
  22. package/dist/types/src/class_list.d.ts +79 -0
  23. package/dist/types/src/class_list.d.ts.map +1 -0
  24. package/dist/types/src/component.d.ts +291 -0
  25. package/dist/types/src/component.d.ts.map +1 -0
  26. package/dist/types/src/component_params.d.ts +239 -0
  27. package/dist/types/src/component_params.d.ts.map +1 -0
  28. package/dist/types/src/component_space.d.ts +164 -0
  29. package/dist/types/src/component_space.d.ts.map +1 -0
  30. package/dist/types/src/data_path.d.ts +5 -0
  31. package/dist/types/src/data_path.d.ts.map +1 -0
  32. package/dist/types/src/data_proxy.d.ts +107 -0
  33. package/dist/types/src/data_proxy.d.ts.map +1 -0
  34. package/dist/types/src/data_utils.d.ts +3 -0
  35. package/dist/types/src/data_utils.d.ts.map +1 -0
  36. package/dist/types/src/element.d.ts +275 -0
  37. package/dist/types/src/element.d.ts.map +1 -0
  38. package/dist/types/src/element_iterator.d.ts +43 -0
  39. package/dist/types/src/element_iterator.d.ts.map +1 -0
  40. package/dist/types/src/event.d.ts +104 -0
  41. package/dist/types/src/event.d.ts.map +1 -0
  42. package/dist/types/src/external_shadow_tree.d.ts +20 -0
  43. package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
  44. package/dist/types/src/func_arr.d.ts +39 -0
  45. package/dist/types/src/func_arr.d.ts.map +1 -0
  46. package/dist/types/src/global_options.d.ts +111 -0
  47. package/dist/types/src/global_options.d.ts.map +1 -0
  48. package/dist/types/src/index.d.ts +43 -0
  49. package/dist/types/src/index.d.ts.map +1 -0
  50. package/dist/types/src/mutation_observer.d.ts +79 -0
  51. package/dist/types/src/mutation_observer.d.ts.map +1 -0
  52. package/dist/types/src/native_node.d.ts +8 -0
  53. package/dist/types/src/native_node.d.ts.map +1 -0
  54. package/dist/types/src/node.d.ts +49 -0
  55. package/dist/types/src/node.d.ts.map +1 -0
  56. package/dist/types/src/relation.d.ts +47 -0
  57. package/dist/types/src/relation.d.ts.map +1 -0
  58. package/dist/types/src/render.d.ts +3 -0
  59. package/dist/types/src/render.d.ts.map +1 -0
  60. package/dist/types/src/selector.d.ts +32 -0
  61. package/dist/types/src/selector.d.ts.map +1 -0
  62. package/dist/types/src/shadow_root.d.ts +136 -0
  63. package/dist/types/src/shadow_root.d.ts.map +1 -0
  64. package/dist/types/src/template_engine.d.ts +18 -0
  65. package/dist/types/src/template_engine.d.ts.map +1 -0
  66. package/dist/types/src/text_node.d.ts +32 -0
  67. package/dist/types/src/text_node.d.ts.map +1 -0
  68. package/dist/types/src/tmpl/index.d.ts +18 -0
  69. package/dist/types/src/tmpl/index.d.ts.map +1 -0
  70. package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
  71. package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
  72. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
  73. package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
  74. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
  75. package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
  76. package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
  77. package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
  78. package/dist/types/src/trait_behaviors.d.ts +38 -0
  79. package/dist/types/src/trait_behaviors.d.ts.map +1 -0
  80. package/dist/types/src/virtual_node.d.ts +10 -0
  81. package/dist/types/src/virtual_node.d.ts.map +1 -0
  82. package/dist/types/tests/backend/domlike.test.d.ts +2 -0
  83. package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
  84. package/dist/types/tests/base/env.d.ts +29 -0
  85. package/dist/types/tests/base/env.d.ts.map +1 -0
  86. package/dist/types/tests/base/match.d.ts +9 -0
  87. package/dist/types/tests/base/match.d.ts.map +1 -0
  88. package/dist/types/tests/core/backend.test.d.ts +2 -0
  89. package/dist/types/tests/core/backend.test.d.ts.map +1 -0
  90. package/dist/types/tests/core/behavior.test.d.ts +2 -0
  91. package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
  92. package/dist/types/tests/core/component_space.test.d.ts +2 -0
  93. package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
  94. package/dist/types/tests/core/data_update.test.d.ts +2 -0
  95. package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
  96. package/dist/types/tests/core/misc.test.d.ts +2 -0
  97. package/dist/types/tests/core/misc.test.d.ts.map +1 -0
  98. package/dist/types/tests/core/placeholder.test.d.ts +2 -0
  99. package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
  100. package/dist/types/tests/core/slot.test.d.ts +2 -0
  101. package/dist/types/tests/core/slot.test.d.ts.map +1 -0
  102. package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
  103. package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
  104. package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
  105. package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
  106. package/dist/types/tests/tmpl/event.test.d.ts +2 -0
  107. package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
  108. package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
  109. package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
  110. package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
  111. package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
  112. package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
  113. package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
  114. package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
  115. package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
  116. package/dist/types/tests/types/chaining.test.d.ts +2 -0
  117. package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
  118. package/dist/types/tests/types/createElement.test.d.ts +2 -0
  119. package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
  120. package/dist/types/tests/types/definition.test.d.ts +2 -0
  121. package/dist/types/tests/types/definition.test.d.ts.map +1 -0
  122. package/guide/zh_CN/advanced/binding_map_update.md +32 -0
  123. package/guide/zh_CN/advanced/build_args.md +28 -0
  124. package/guide/zh_CN/advanced/component_filter.md +70 -0
  125. package/guide/zh_CN/advanced/component_space.md +124 -0
  126. package/guide/zh_CN/advanced/custom_backend.md +53 -0
  127. package/guide/zh_CN/advanced/error_listener.md +32 -0
  128. package/guide/zh_CN/advanced/external_component.md +73 -0
  129. package/guide/zh_CN/advanced/template_engine.md +61 -0
  130. package/guide/zh_CN/appendix/backend_protocol.md +501 -0
  131. package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
  132. package/guide/zh_CN/basic/beginning.md +94 -0
  133. package/guide/zh_CN/basic/component.md +156 -0
  134. package/guide/zh_CN/basic/event.md +169 -0
  135. package/guide/zh_CN/basic/lifetime.md +66 -0
  136. package/guide/zh_CN/basic/method.md +62 -0
  137. package/guide/zh_CN/basic/template.md +135 -0
  138. package/guide/zh_CN/data_management/advanced_update.md +170 -0
  139. package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
  140. package/guide/zh_CN/data_management/data_observer.md +154 -0
  141. package/guide/zh_CN/data_management/property_early_init.md +31 -0
  142. package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
  143. package/guide/zh_CN/index.md +93 -0
  144. package/guide/zh_CN/interaction/behavior.md +52 -0
  145. package/guide/zh_CN/interaction/component_path.md +37 -0
  146. package/guide/zh_CN/interaction/generic.md +73 -0
  147. package/guide/zh_CN/interaction/placeholder.md +40 -0
  148. package/guide/zh_CN/interaction/relation.md +151 -0
  149. package/guide/zh_CN/interaction/slot.md +137 -0
  150. package/guide/zh_CN/interaction/template_import.md +94 -0
  151. package/guide/zh_CN/interaction/trait_behavior.md +117 -0
  152. package/guide/zh_CN/styling/external_class.md +46 -0
  153. package/guide/zh_CN/styling/style_isolation.md +54 -0
  154. package/guide/zh_CN/styling/virtual_host.md +52 -0
  155. package/guide/zh_CN/tree/element_iterator.md +54 -0
  156. package/guide/zh_CN/tree/mutation_observer.md +52 -0
  157. package/guide/zh_CN/tree/node_tree.md +142 -0
  158. package/guide/zh_CN/tree/node_tree_modification.md +78 -0
  159. package/guide/zh_CN/tree/selector.md +66 -0
  160. package/jest.config.js +6 -0
  161. package/jest.dts.config.js +9 -0
  162. package/jest.unit.config.js +14 -0
  163. package/package.json +28 -0
  164. package/src/backend/backend_protocol.ts +313 -0
  165. package/src/backend/composed_backend_protocol.ts +252 -0
  166. package/src/backend/domlike_backend_protocol.ts +370 -0
  167. package/src/backend/mode.ts +51 -0
  168. package/src/backend/suggested_backend_protocol.ts +83 -0
  169. package/src/behavior.ts +1655 -0
  170. package/src/bootstrap_dom_dev.js +22 -0
  171. package/src/class_list.ts +376 -0
  172. package/src/component.ts +1309 -0
  173. package/src/component_params.ts +461 -0
  174. package/src/component_space.ts +547 -0
  175. package/src/data_path.ts +225 -0
  176. package/src/data_proxy.ts +670 -0
  177. package/src/data_utils.ts +50 -0
  178. package/src/element.ts +1966 -0
  179. package/src/element_iterator.ts +158 -0
  180. package/src/event.ts +401 -0
  181. package/src/external_shadow_tree.ts +27 -0
  182. package/src/func_arr.ts +198 -0
  183. package/src/global_options.ts +242 -0
  184. package/src/index.ts +187 -0
  185. package/src/mutation_observer.ts +252 -0
  186. package/src/native_node.ts +74 -0
  187. package/src/node.ts +174 -0
  188. package/src/relation.ts +380 -0
  189. package/src/render.ts +25 -0
  190. package/src/selector.ts +218 -0
  191. package/src/shadow_root.ts +766 -0
  192. package/src/template_engine.ts +45 -0
  193. package/src/text_node.ts +149 -0
  194. package/src/tmpl/index.ts +199 -0
  195. package/src/tmpl/native_rendering.ts +175 -0
  196. package/src/tmpl/proc_gen_wrapper.ts +954 -0
  197. package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
  198. package/src/tmpl/range_list_diff.ts +443 -0
  199. package/src/trait_behaviors.ts +51 -0
  200. package/src/virtual_node.ts +51 -0
  201. package/tests/backend/domlike.test.ts +254 -0
  202. package/tests/base/env.ts +78 -0
  203. package/tests/base/match.ts +185 -0
  204. package/tests/core/backend.test.ts +144 -0
  205. package/tests/core/behavior.test.ts +546 -0
  206. package/tests/core/component_space.test.ts +212 -0
  207. package/tests/core/data_update.test.ts +461 -0
  208. package/tests/core/misc.test.ts +339 -0
  209. package/tests/core/placeholder.test.ts +180 -0
  210. package/tests/core/slot.test.ts +1495 -0
  211. package/tests/core/trait_behaviors.test.ts +153 -0
  212. package/tests/legacy/README.md +3 -0
  213. package/tests/legacy/behavior.test.js +293 -0
  214. package/tests/legacy/component.test.js +1247 -0
  215. package/tests/legacy/data_path.test.js +149 -0
  216. package/tests/legacy/data_proxy.test.js +759 -0
  217. package/tests/legacy/element_iterator.test.js +148 -0
  218. package/tests/legacy/event.test.js +849 -0
  219. package/tests/legacy/external.test.js +510 -0
  220. package/tests/legacy/extra_info.test.js +109 -0
  221. package/tests/legacy/generics.test.js +176 -0
  222. package/tests/legacy/mutation_observer.test.js +210 -0
  223. package/tests/legacy/relation.test.js +517 -0
  224. package/tests/legacy/selector.test.js +263 -0
  225. package/tests/legacy/slot.test.js +915 -0
  226. package/tests/legacy/virtual.test.js +394 -0
  227. package/tests/tmpl/binding_map.test.ts +208 -0
  228. package/tests/tmpl/event.test.ts +206 -0
  229. package/tests/tmpl/expression.test.ts +429 -0
  230. package/tests/tmpl/lvalue.test.ts +160 -0
  231. package/tests/tmpl/native_rendering.test.ts +155 -0
  232. package/tests/tmpl/structure.test.ts +998 -0
  233. package/tests/types/chaining.test.ts +614 -0
  234. package/tests/types/createElement.test.ts +82 -0
  235. package/tests/types/definition.test.ts +442 -0
  236. package/tsconfig.json +11 -0
  237. package/webpack.config.js +270 -0
package/src/element.ts ADDED
@@ -0,0 +1,1966 @@
1
+ import * as backend from './backend/backend_protocol'
2
+ import * as composedBackend from './backend/composed_backend_protocol'
3
+ import * as domlikeBackend from './backend/domlike_backend_protocol'
4
+ import {
5
+ globalOptions,
6
+ } from './global_options'
7
+ import {
8
+ MutationObserverChildEvent,
9
+ MutationObserverTarget,
10
+ } from './mutation_observer'
11
+ import {
12
+ Event,
13
+ EventListener,
14
+ EventListenerOptions,
15
+ EventOptions,
16
+ EventTarget,
17
+ FinalChanged,
18
+ } from './event'
19
+ import {
20
+ triggerWarning,
21
+ } from './func_arr'
22
+ import {
23
+ ParsedSelector,
24
+ } from './selector'
25
+ import {
26
+ BM,
27
+ BackendMode,
28
+ BoundingClientRect,
29
+ ScrollOffset,
30
+ } from './backend/mode'
31
+ import {
32
+ GeneralComponentInstance,
33
+ DataList,
34
+ PropertyList,
35
+ MethodList,
36
+ ComponentInstance,
37
+ } from './component_params'
38
+ import {
39
+ Node,
40
+ GeneralBackendContext,
41
+ GeneralBackendElement,
42
+ ClassList,
43
+ NativeNode,
44
+ VirtualNode,
45
+ ShadowRoot,
46
+ Component,
47
+ RelationType,
48
+ ExternalShadowRoot,
49
+ ComponentDefinition,
50
+ NodeCast,
51
+ } from '.'
52
+
53
+ /**
54
+ * The "style" attributes segments
55
+ *
56
+ * This allows different modules set the "style" attribute of an element
57
+ * without overriding each other.
58
+ * The final "style" attribute value is the concat of all segments.
59
+ * When calling `setNodeStyle` on an element,
60
+ * a segment can be specified.
61
+ */
62
+ export const enum StyleSegmentIndex {
63
+ /** The main style segment, generally managed by the template engine */
64
+ MAIN = 0,
65
+ /** The template style segment, preserved for template engine */
66
+ TEMPLATE_EXTRA = 1,
67
+ /** The animation style segment, preserved for temporary transition */
68
+ ANIMATION_EXTRA = 2,
69
+ /** The temporary style segment, preserved for high priority styles */
70
+ TEMP_EXTRA = 3,
71
+ }
72
+
73
+ type composedContext = composedBackend.Context | domlikeBackend.Context
74
+ type composedElement = composedBackend.Element | domlikeBackend.Element
75
+
76
+ /**
77
+ * A general element
78
+ *
79
+ * An element can be a `NativeNode` , a `Component` , or a `VirtualNode` .
80
+ */
81
+ export class Element implements NodeCast {
82
+ /** @internal */
83
+ private _$backendElement: GeneralBackendElement | null
84
+ /** @internal */
85
+ _$destroyOnDetach: boolean
86
+ /** @internal */
87
+ _$nodeTreeContext: GeneralBackendContext
88
+ /** @internal */
89
+ private _$nodeId: string
90
+ /** @internal */
91
+ private _$nodeAttributes: { [name: string]: unknown } | null
92
+ /** @internal */
93
+ _$nodeSlot: string
94
+ /** @internal */
95
+ _$slotName: string | null
96
+ /** @internal */
97
+ _$slotValues: { [name: string]: unknown } | null
98
+ /** @internal */
99
+ _$nodeSlotElement: Element | null
100
+ /** @internal */
101
+ private _$subtreeSlotCount: number
102
+ /** @internal */
103
+ _$inheritSlots: boolean
104
+ /** @internal */
105
+ _$placeholderHandler: (() => void) | undefined
106
+ /** @internal */
107
+ private _$virtual: boolean
108
+ dataset: { [name: string]: unknown } | null
109
+ /** @internal */
110
+ private _$marks: { [name: string]: unknown } | null
111
+ /** @internal */
112
+ private _$attached: boolean
113
+ /** The `ClassList` of the element (will never change and must not be modified!) */
114
+ classList: ClassList | null
115
+ /** @internal */
116
+ private _$styleSegments: string[]
117
+ /** The parent element (must not be modified directly!) */
118
+ parentNode: Element | null
119
+ /** The child nodes (must not be modified directly!) */
120
+ childNodes: Node[]
121
+ /** The shadow-root which owns the element (will never change and must not be modified!) */
122
+ ownerShadowRoot: ShadowRoot | null
123
+ /** @internal */
124
+ _$mutationObserverTarget: MutationObserverTarget | null
125
+ /** @internal */
126
+ _$eventTarget: EventTarget<{ [name: string]: unknown }>
127
+
128
+ constructor() {
129
+ throw new Error('Element cannot be constructed directly')
130
+ }
131
+
132
+ protected _$initialize(
133
+ virtual: boolean,
134
+ backendElement: GeneralBackendElement | null,
135
+ owner: ShadowRoot | null,
136
+ nodeTreeContext?: GeneralBackendContext,
137
+ ) {
138
+ this._$backendElement = backendElement
139
+ if (backendElement) {
140
+ backendElement.__wxElement = this
141
+ }
142
+ this._$destroyOnDetach = false
143
+ this._$nodeTreeContext = nodeTreeContext || owner!._$nodeTreeContext
144
+ this._$nodeId = ''
145
+ this._$nodeAttributes = null
146
+ this._$nodeSlot = ''
147
+ this._$slotName = null
148
+ this._$slotValues = null
149
+ this._$nodeSlotElement = null
150
+ this._$subtreeSlotCount = 0
151
+ this._$inheritSlots = false
152
+ this._$placeholderHandler = undefined
153
+ this._$virtual = virtual
154
+ this.dataset = null
155
+ this._$marks = null
156
+ this._$attached = false
157
+ this.classList = null
158
+ this._$styleSegments = [] as string[]
159
+ this.parentNode = null
160
+ this.childNodes = []
161
+ this.ownerShadowRoot = owner
162
+ this._$mutationObserverTarget = null
163
+ this._$eventTarget = new EventTarget()
164
+ }
165
+
166
+ get $$(): GeneralBackendElement | null {
167
+ return this._$backendElement
168
+ }
169
+
170
+ get id(): string {
171
+ return this._$nodeId
172
+ }
173
+
174
+ set id(x: unknown) {
175
+ const newId = String(x)
176
+ if (this._$nodeId === newId) return
177
+ this._$nodeId = newId
178
+ if (this.ownerShadowRoot) {
179
+ const host = this.ownerShadowRoot.getHostNode()
180
+ this.ownerShadowRoot._$markIdCacheDirty()
181
+ if (!this._$virtual && host.getComponentOptions().writeIdToDOM) {
182
+ const idPrefix = host._$idPrefix
183
+ const val = idPrefix ? `${idPrefix}--${newId}` : newId
184
+ if (BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike)) {
185
+ const e = this._$backendElement as domlikeBackend.Element | null
186
+ if (e) e.id = val
187
+ } else {
188
+ (this._$backendElement as backend.Element | composedBackend.Element | null)?.setId(val)
189
+ }
190
+ }
191
+ }
192
+ if (globalOptions.writeExtraInfoToAttr) {
193
+ this._$backendElement?.setAttribute('exparser:info-attr-id', newId)
194
+ }
195
+ if (this._$mutationObserverTarget) {
196
+ MutationObserverTarget.callAttrObservers(this, {
197
+ type: 'properties',
198
+ target: this,
199
+ attributeName: 'id',
200
+ })
201
+ }
202
+ }
203
+
204
+ get slot(): string {
205
+ return this._$nodeSlot
206
+ }
207
+
208
+ set slot(x) {
209
+ const newSlot = String(x)
210
+ const oldSlot = this._$nodeSlot
211
+ if (oldSlot === newSlot) return
212
+ if (this._$inheritSlots) {
213
+ triggerWarning('slots-inherited nodes do not support "slot" attribute.')
214
+ return
215
+ }
216
+ this._$nodeSlot = newSlot
217
+ if (BM.SHADOW || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Shadow)) {
218
+ (this._$backendElement as backend.Element).setSlot(newSlot, this._$inheritSlots)
219
+ } else {
220
+ const parent = this.parentNode
221
+ if (parent) {
222
+ let slotParent: Element | null = parent
223
+ while (slotParent?._$inheritSlots) {
224
+ slotParent = slotParent.parentNode
225
+ }
226
+ if (slotParent instanceof Component && !slotParent._$external) {
227
+ const slotParentShadowRoot = slotParent.shadowRoot as ShadowRoot
228
+ if (slotParentShadowRoot.isDynamicSlots()) {
229
+ triggerWarning('nodes inside dynamic slots should change binding slots through Element#setSlotElement.')
230
+ return
231
+ }
232
+ const oldSlotElement = slotParentShadowRoot
233
+ .getSlotElementFromName(oldSlot) as Element | null
234
+ const newSlotElement = slotParentShadowRoot
235
+ .getSlotElementFromName(newSlot) as Element | null
236
+ Element.insertChildReassignComposed(
237
+ parent,
238
+ this,
239
+ oldSlotElement,
240
+ newSlotElement,
241
+ parent.childNodes.indexOf(this) + 1,
242
+ )
243
+ }
244
+ }
245
+ }
246
+ if (this._$mutationObserverTarget) {
247
+ MutationObserverTarget.callAttrObservers(this, {
248
+ type: 'properties',
249
+ target: this,
250
+ attributeName: 'slot',
251
+ })
252
+ }
253
+ }
254
+
255
+ get attributes(): { name: string, value: unknown }[] {
256
+ const ret: { name: string, value: unknown }[] = []
257
+ if (this._$nodeAttributes) {
258
+ Object.entries(this._$nodeAttributes).forEach(([name, value]) => {
259
+ ret.push({
260
+ name,
261
+ value,
262
+ })
263
+ })
264
+ }
265
+ return ret
266
+ }
267
+
268
+ get class(): string {
269
+ if (this.classList) {
270
+ return this.classList.getClassNames()
271
+ }
272
+ return ''
273
+ }
274
+
275
+ set class(classNames) {
276
+ if (this.classList) {
277
+ this.classList.setClassNames(classNames)
278
+ }
279
+ }
280
+
281
+ get style(): string {
282
+ return this._$styleSegments[StyleSegmentIndex.MAIN] || ''
283
+ }
284
+
285
+ set style(styleText) {
286
+ this.setNodeStyle(styleText, StyleSegmentIndex.MAIN)
287
+ }
288
+
289
+ // eslint-disable-next-line class-methods-use-this
290
+ asTextNode(): null {
291
+ return null
292
+ }
293
+
294
+ asElement(): Element {
295
+ return this
296
+ }
297
+
298
+ asNativeNode(): NativeNode | null {
299
+ if (this instanceof NativeNode) {
300
+ return this as NativeNode
301
+ }
302
+ return null
303
+ }
304
+
305
+ asVirtualNode(): VirtualNode | null {
306
+ if (this instanceof VirtualNode) {
307
+ return this as VirtualNode
308
+ }
309
+ return null
310
+ }
311
+
312
+ asInstanceOf<
313
+ UData extends DataList,
314
+ UProperty extends PropertyList,
315
+ UMethod extends MethodList,
316
+ >(
317
+ componentDefinition: ComponentDefinition<UData, UProperty, UMethod>,
318
+ ): ComponentInstance<UData, UProperty, UMethod> | null {
319
+ if (this instanceof Component) {
320
+ return this.asInstanceOf(componentDefinition)
321
+ }
322
+ return null
323
+ }
324
+
325
+ /** Get the backend context */
326
+ getBackendContext(): GeneralBackendContext {
327
+ return this._$nodeTreeContext
328
+ }
329
+
330
+ /** Get the backend mode */
331
+ getBackendMode(): BackendMode {
332
+ return this._$nodeTreeContext.mode
333
+ }
334
+
335
+ /** Get the backend element */
336
+ getBackendElement(): GeneralBackendElement | null {
337
+ return this._$backendElement
338
+ }
339
+
340
+ /** Destroy the backend element */
341
+ destroyBackendElement() {
342
+ if (this._$backendElement) {
343
+ if (!(BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike))) {
344
+ (this._$backendElement as backend.Element | composedBackend.Element).release()
345
+ }
346
+ this._$backendElement = null
347
+ }
348
+ }
349
+
350
+ /** Destroy the backend element on next detach */
351
+ destroyBackendElementOnDetach() {
352
+ this._$destroyOnDetach = true
353
+ }
354
+
355
+ /** Get whether the node is virtual or not */
356
+ isVirtual(): boolean {
357
+ return this._$virtual
358
+ }
359
+
360
+ /** Set the node style */
361
+ setNodeStyle(styleSegment: string, index: StyleSegmentIndex = 0) {
362
+ if (this._$styleSegments[index] === styleSegment) return
363
+ this._$styleSegments[index] = styleSegment
364
+ const style = this._$styleSegments.join(';')
365
+ if (this._$backendElement) {
366
+ if (BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike)) {
367
+ (this._$backendElement as domlikeBackend.Element).setAttribute('style', style)
368
+ } else {
369
+ (this._$backendElement as backend.Element | composedBackend.Element).setStyle(style)
370
+ }
371
+ }
372
+ if (this._$mutationObserverTarget) {
373
+ MutationObserverTarget.callAttrObservers(this, {
374
+ type: 'properties',
375
+ target: this,
376
+ attributeName: 'style',
377
+ })
378
+ }
379
+ }
380
+
381
+ private static checkAndCallAttached(node: Node) {
382
+ const callFunc = function callFunc(node: Node) {
383
+ if (node instanceof Element && !node._$attached) {
384
+ node._$attached = true
385
+ if (node instanceof Component) {
386
+ node._$lifetimeFuncs.attached?.call(node.getMethodCaller(), [])
387
+ if (node._$relation) {
388
+ node._$relation.triggerLinkEvent(RelationType.ParentNonVirtualNode, false)
389
+ node._$relation.triggerLinkEvent(RelationType.ParentComponent, false)
390
+ node._$relation.triggerLinkEvent(RelationType.Ancestor, false)
391
+ }
392
+ if (node._$mutationObserverTarget) {
393
+ MutationObserverTarget.callAttachObservers(node, {
394
+ type: 'attachStatus',
395
+ target: node,
396
+ status: 'attached',
397
+ })
398
+ }
399
+ const shadowRoot = node.getShadowRoot()
400
+ if (shadowRoot) callFunc(shadowRoot)
401
+ }
402
+ node.childNodes.forEach(callFunc)
403
+ }
404
+ }
405
+ callFunc(node)
406
+ }
407
+
408
+ private static checkAndCallDetached(node: Node) {
409
+ const callFunc = function callFunc(node: Node) {
410
+ if (node instanceof Element && node._$attached) {
411
+ node.childNodes.forEach(callFunc)
412
+ if (node instanceof Component) {
413
+ const f = node._$placeholderHandler
414
+ if (f) f()
415
+ const shadowRoot = node.getShadowRoot()
416
+ if (shadowRoot) callFunc(shadowRoot)
417
+ node._$attached = false
418
+ node._$lifetimeFuncs.detached?.call(node.getMethodCaller(), [])
419
+ if (node._$relation) {
420
+ node._$relation.triggerLinkEvent(RelationType.ParentNonVirtualNode, true)
421
+ node._$relation.triggerLinkEvent(RelationType.ParentComponent, true)
422
+ node._$relation.triggerLinkEvent(RelationType.Ancestor, true)
423
+ }
424
+ if (node._$mutationObserverTarget) {
425
+ MutationObserverTarget.callAttachObservers(node, {
426
+ type: 'attachStatus',
427
+ target: node,
428
+ status: 'detached',
429
+ })
430
+ }
431
+ } else {
432
+ node._$attached = false
433
+ }
434
+ }
435
+ if (node._$destroyOnDetach) {
436
+ node.destroyBackendElement()
437
+ }
438
+ }
439
+ callFunc(node)
440
+ }
441
+
442
+ private static checkAndCallMoved(node: Node) {
443
+ const callFunc = function callFunc(node: Node) {
444
+ if (node instanceof Element && node._$attached) {
445
+ node.childNodes.forEach(callFunc)
446
+ if (node instanceof Component) {
447
+ const shadowRoot = node.getShadowRoot()
448
+ if (shadowRoot) callFunc(shadowRoot)
449
+ node._$lifetimeFuncs.moved?.call(node.getMethodCaller(), [])
450
+ if (node._$relation) {
451
+ node._$relation.triggerLinkEvent(RelationType.ParentNonVirtualNode, false)
452
+ node._$relation.triggerLinkEvent(RelationType.ParentComponent, false)
453
+ node._$relation.triggerLinkEvent(RelationType.Ancestor, false)
454
+ }
455
+ }
456
+ }
457
+ }
458
+ callFunc(node)
459
+ }
460
+
461
+ private static checkChildObservers(node: Element, type: 'add' | 'remove' | 'move', child: Node) {
462
+ const observer = node._$mutationObserverTarget
463
+ if (observer && (!observer.childObservers?.empty || observer.hasSubtreeListeners())) {
464
+ const childList = [child]
465
+ let childEventObj: MutationObserverChildEvent
466
+ if (type === 'add') {
467
+ childEventObj = {
468
+ type: 'childList',
469
+ target: node,
470
+ addedNodes: childList,
471
+ }
472
+ } else if (type === 'remove') {
473
+ childEventObj = {
474
+ type: 'childList',
475
+ target: node,
476
+ removedNodes: childList,
477
+ }
478
+ } else {
479
+ childEventObj = {
480
+ type: 'childList',
481
+ target: node,
482
+ addedNodes: childList,
483
+ removedNodes: childList,
484
+ }
485
+ }
486
+ MutationObserverTarget.callChildObservers(node, childEventObj)
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Get whether a node has any subtree `MutationObserver` attached to it
492
+ *
493
+ * If there is, then tree update may have more performance impact.
494
+ */
495
+ static hasSubtreeMutationObservers(node: Element): boolean {
496
+ return node._$mutationObserverTarget?.hasSubtreeListeners() || false
497
+ }
498
+
499
+ private static updateSlotCount(element: Element, diff: number) {
500
+ let cur = element
501
+ for (;;) {
502
+ cur._$subtreeSlotCount += diff
503
+ const next = cur.parentNode
504
+ if (next) {
505
+ cur = next
506
+ } else {
507
+ if (cur instanceof ShadowRoot) {
508
+ cur._$markSlotCacheDirty()
509
+ }
510
+ break
511
+ }
512
+ }
513
+ }
514
+
515
+ /** @internal */
516
+ static _$insertChildReassignSlot(
517
+ shadowRoot: ShadowRoot,
518
+ name: string,
519
+ oldSlot: Element | null,
520
+ newSlot: Element | null,
521
+ ) {
522
+ const host = shadowRoot.getHostNode()
523
+ const ideaPosIndex = host.childNodes.length
524
+ shadowRoot.forEachNodeInSpecifiedSlot(name, (node, _posIndex) => {
525
+ if (node instanceof Element && node._$inheritSlots) return
526
+ // a special use of composing process:
527
+ // the child node might not be the direct child of the host;
528
+ // there may be slot-inherit nodes between them.
529
+ Element.insertChildReassignComposed(
530
+ host,
531
+ node,
532
+ oldSlot,
533
+ newSlot,
534
+ ideaPosIndex,
535
+ )
536
+ })
537
+ }
538
+
539
+ private static insertChildReassignComposed(
540
+ shadowParent: Element,
541
+ child: Node,
542
+ oldSlot: Element | null,
543
+ newSlot: Element | null,
544
+ ideaPosIndex: number,
545
+ ) {
546
+ if (oldSlot) {
547
+ if (
548
+ newSlot
549
+ && (BM.DOMLIKE || (BM.DYNAMIC && shadowParent.getBackendMode() === BackendMode.Domlike))
550
+ ) {
551
+ // removal of in-tree elements are not needed for DOM backend
552
+ // do nothing
553
+ } else {
554
+ const backendParent = Element.findNearestNonVirtual(oldSlot)
555
+ if (backendParent) {
556
+ const d = Element.countNonVirtual(child)
557
+ if (d) {
558
+ const [before, removeCount] = d
559
+ if (
560
+ BM.DOMLIKE
561
+ || (BM.DYNAMIC && shadowParent.getBackendContext().mode === BackendMode.Domlike)
562
+ ) {
563
+ const rel = (before as Element)._$backendElement as domlikeBackend.Element
564
+ for (let i = 1; i < removeCount; i += 1) {
565
+ const next = rel.nextSibling
566
+ if (next) (backendParent as domlikeBackend.Element).removeChild(next)
567
+ }
568
+ if (removeCount > 0) (backendParent as domlikeBackend.Element).removeChild(rel)
569
+ } else {
570
+ (backendParent as composedBackend.Element).spliceRemove(
571
+ (before as Element)._$backendElement as composedBackend.Element,
572
+ removeCount,
573
+ )
574
+ }
575
+ }
576
+ }
577
+ }
578
+ }
579
+ if (newSlot) {
580
+ Element.insertChildComposed(
581
+ shadowParent,
582
+ child,
583
+ undefined,
584
+ false,
585
+ ideaPosIndex,
586
+ )
587
+ }
588
+ }
589
+
590
+ // a helper for searching the nearest non-virtual ancestor
591
+ private static findNearestNonVirtual(p: Element): composedElement | null {
592
+ let cur = p
593
+ for (;;) {
594
+ if (!cur._$virtual) return cur._$backendElement as composedElement | null
595
+ if (cur instanceof ShadowRoot) {
596
+ cur = cur.getHostNode()
597
+ continue
598
+ }
599
+ const next = cur.parentNode
600
+ if (!next) return null
601
+ if (next instanceof Component && !next._$external) {
602
+ const slot = (next.shadowRoot as ShadowRoot).getContainingSlot(cur)
603
+ if (!slot) return null
604
+ cur = slot
605
+ } else {
606
+ cur = next
607
+ }
608
+ }
609
+ }
610
+
611
+ private static countNonVirtual(
612
+ target: Node,
613
+ ): [Node, number] | null {
614
+ let firstNode = null
615
+ let removeCount = 0
616
+ const recRelVirtual = (c: Node) => {
617
+ if (c instanceof Element) {
618
+ if (c._$virtual) {
619
+ c.forEachComposedChild(recRelVirtual)
620
+ return
621
+ }
622
+ }
623
+ if (removeCount === 0) {
624
+ firstNode = c
625
+ }
626
+ removeCount += 1
627
+ }
628
+ recRelVirtual(target)
629
+ return firstNode ? [firstNode, removeCount] : null
630
+ }
631
+
632
+ private static insertChildComposed(
633
+ shadowParent: Element,
634
+ newChild: Node | null,
635
+ relChild: Node | undefined,
636
+ removal: boolean,
637
+ newPosIndex: number, // only valid when newChild is provided (for searching position)
638
+ ) {
639
+ let parentConverted = false
640
+ let cur: Element | null = shadowParent
641
+ while (cur?._$inheritSlots) {
642
+ parentConverted = true
643
+ cur = cur.parentNode
644
+ }
645
+ if (!cur) return
646
+ const slotParent = cur
647
+ const context = slotParent._$nodeTreeContext as composedContext
648
+
649
+ // detect whether it is in single-slot mode
650
+ let sharedNonVirtualParent: composedElement | null | undefined
651
+ let dynamicSlotting = false
652
+ if (slotParent instanceof Component) {
653
+ if (slotParent._$external) {
654
+ const singleSlot = (slotParent.shadowRoot as ExternalShadowRoot).slot
655
+ sharedNonVirtualParent = singleSlot
656
+ } else {
657
+ parentConverted = true
658
+ const singleSlot = (slotParent.shadowRoot as ShadowRoot).getSingleSlotElement()
659
+ if (singleSlot !== undefined) {
660
+ sharedNonVirtualParent = singleSlot
661
+ ? Element.findNearestNonVirtual(singleSlot)
662
+ : undefined
663
+ } else {
664
+ if ((slotParent.shadowRoot as ShadowRoot).isDynamicSlots()) dynamicSlotting = true
665
+ sharedNonVirtualParent = undefined
666
+ }
667
+ }
668
+ } else {
669
+ if (slotParent._$virtual) parentConverted = true
670
+ sharedNonVirtualParent = Element.findNearestNonVirtual(slotParent)
671
+ }
672
+
673
+ // a helper for listing children in specified slot
674
+ const forEachChildInSlot = (
675
+ node: Node,
676
+ slot: Element | null,
677
+ slotName: string,
678
+ f: (node: Node) => boolean,
679
+ ): void => {
680
+ const recInheritedSlot = (c: Node): boolean => {
681
+ if (c instanceof Element && c._$inheritSlots) {
682
+ return c.childNodes.every(recInheritedSlot)
683
+ }
684
+ return f(c)
685
+ }
686
+ const recv = (c: Node): boolean => {
687
+ if (sharedNonVirtualParent) {
688
+ // single slot
689
+ if (c instanceof Element && c._$inheritSlots) {
690
+ return c.childNodes.every(recInheritedSlot)
691
+ }
692
+ } else if (!dynamicSlotting) {
693
+ // multiple slots
694
+ if (c instanceof Element && c._$inheritSlots) {
695
+ return c.childNodes.every(recv)
696
+ }
697
+ if (slotName !== (c instanceof Element ? c._$nodeSlot : '')) return true
698
+ } else {
699
+ // dynamic slots
700
+ if (slot !== c._$nodeSlotElement) return true
701
+ if (c instanceof Element && c._$inheritSlots) {
702
+ return c.childNodes.every(recInheritedSlot)
703
+ }
704
+ }
705
+ return f(c)
706
+ }
707
+ recv(node)
708
+ }
709
+
710
+ // a helper for searching the first non-virtual node
711
+ const findFirstNonVirtual = (
712
+ shadowParent: Element,
713
+ index: number,
714
+ slot: Element | null,
715
+ slotName: string,
716
+ ): Node | null => {
717
+ const children = shadowParent.childNodes
718
+ if (index >= 0) {
719
+ for (let i = index; i < children.length; i += 1) {
720
+ let ret: Node | null = null
721
+ const recVirtual = (c: Node): boolean => {
722
+ if (c instanceof Element && c._$virtual) {
723
+ return c.forEachComposedChild(recVirtual)
724
+ }
725
+ ret = c
726
+ return ret === null
727
+ }
728
+ forEachChildInSlot(children[i]!, slot, slotName, recVirtual)
729
+ if (ret) return ret
730
+ }
731
+ }
732
+ let cur: Element
733
+ if (shadowParent instanceof ShadowRoot) {
734
+ const host = shadowParent.getHostNode()
735
+ cur = host
736
+ } else if (shadowParent instanceof Component && !shadowParent._$external) {
737
+ const slotElem = slot === null
738
+ ? (shadowParent.shadowRoot as ShadowRoot).getSlotElementFromName(slotName)
739
+ : slot
740
+ if (!slotElem) return null
741
+ cur = slotElem as Element
742
+ } else {
743
+ cur = shadowParent
744
+ }
745
+ if (!cur._$virtual) return null
746
+ const p = cur.parentNode
747
+ if (p) {
748
+ const i = p.childNodes.lastIndexOf(cur)
749
+ let nextSlot: Element | null
750
+ let nextSlotName: string
751
+ if (cur._$inheritSlots) {
752
+ nextSlot = slot
753
+ nextSlotName = slotName
754
+ } else if (p instanceof Component && !p._$external) {
755
+ const singleSlot = (p.shadowRoot as ShadowRoot).getSingleSlotElement()
756
+ if (singleSlot !== undefined) {
757
+ nextSlot = null
758
+ nextSlotName = ''
759
+ } else {
760
+ nextSlot = (p.shadowRoot as ShadowRoot).getContainingSlot(cur)
761
+ nextSlotName = cur._$slotName || ''
762
+ }
763
+ } else {
764
+ nextSlot = null
765
+ nextSlotName = ''
766
+ }
767
+ const ret = findFirstNonVirtual(p, i + 1, nextSlot, nextSlotName)
768
+ if (ret) return ret
769
+ }
770
+ return null
771
+ }
772
+
773
+ // a helper for grouping and slicing update
774
+ let sharedFrag: composedElement | null = null
775
+ const groupUpdate = (slot: Element | null, slotName?: string) => {
776
+ // find the proper element of the backend
777
+ let backendParent: composedElement | null
778
+ if (slotName === undefined) {
779
+ // `sharedNonVirtualParent` must be valid if `null`
780
+ backendParent = sharedNonVirtualParent as composedElement | null
781
+ } else {
782
+ // `slotParent` must be `Component` if slot not `null`
783
+ const p = slotParent as GeneralComponentInstance
784
+ if (p._$external) {
785
+ const s = (p.shadowRoot as ExternalShadowRoot).slot
786
+ backendParent = s as composedBackend.Element | domlikeBackend.Element
787
+ } else {
788
+ backendParent = slot ? Element.findNearestNonVirtual(slot) : null
789
+ }
790
+ }
791
+ if (!(BM.DOMLIKE || (BM.DYNAMIC && shadowParent.getBackendMode() === BackendMode.Domlike))) {
792
+ if (!backendParent) {
793
+ return
794
+ }
795
+ }
796
+ const slotNameLimit = slotName || ''
797
+ let removeCount = 0
798
+ let before: Node | null = null
799
+
800
+ // get the new child nodes of the backend
801
+ let frag: composedElement | null = null
802
+ if (newChild) {
803
+ const f = sharedFrag || (
804
+ BM.DOMLIKE || (BM.DYNAMIC && context.mode === BackendMode.Domlike)
805
+ ? (context as domlikeBackend.Context).document.createDocumentFragment()
806
+ : (context as backend.Context | composedBackend.Context).createFragment()
807
+ )
808
+ sharedFrag = frag
809
+ const recNewVirtual = (c: Node): boolean => {
810
+ if (c instanceof Element && c._$virtual) {
811
+ c.forEachComposedChild(recNewVirtual)
812
+ } else {
813
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
814
+ // domlike backend also
815
+ (f as composedBackend.Element)
816
+ .appendChild((c as Element)._$backendElement as composedBackend.Element)
817
+ frag = f
818
+ }
819
+ return true
820
+ }
821
+ forEachChildInSlot(newChild, slot, slotNameLimit, recNewVirtual)
822
+ }
823
+ if (BM.DOMLIKE || (BM.DYNAMIC && shadowParent.getBackendMode() === BackendMode.Domlike)) {
824
+ if (!backendParent) {
825
+ sharedFrag = null
826
+ return
827
+ }
828
+ }
829
+
830
+ // get the proper relative node of the backend
831
+ if (relChild || parentConverted) {
832
+ if (removal && relChild) {
833
+ forEachChildInSlot(relChild, slot, slotNameLimit, (c) => {
834
+ if (c instanceof Element) {
835
+ const d = Element.countNonVirtual(c)
836
+ if (d) {
837
+ if (!before) before = d[0]
838
+ removeCount += d[1]
839
+ }
840
+ } else {
841
+ if (!before) before = c
842
+ removeCount += 1
843
+ }
844
+ return true
845
+ })
846
+ if (removeCount === 0 && frag) {
847
+ before = findFirstNonVirtual(shadowParent, newPosIndex + 1, slot, slotNameLimit)
848
+ }
849
+ } else if (frag) {
850
+ before = findFirstNonVirtual(shadowParent, newPosIndex, slot, slotNameLimit)
851
+ }
852
+ }
853
+
854
+ // actually do the backend operation
855
+ if (frag) {
856
+ if (before) {
857
+ if (BM.DOMLIKE || (BM.DYNAMIC && context.mode === BackendMode.Domlike)) {
858
+ const rel = (before as Element)._$backendElement as domlikeBackend.Element
859
+ (backendParent as domlikeBackend.Element).insertBefore(
860
+ frag as domlikeBackend.Element,
861
+ rel,
862
+ )
863
+ for (let i = 1; i < removeCount; i += 1) {
864
+ const next = rel.nextSibling
865
+ if (next) (backendParent as domlikeBackend.Element).removeChild(next)
866
+ }
867
+ if (removeCount > 0) (backendParent as domlikeBackend.Element).removeChild(rel)
868
+ } else {
869
+ (backendParent as composedBackend.Element).spliceBefore(
870
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
871
+ (before as Element)._$backendElement as composedBackend.Element,
872
+ removeCount,
873
+ frag as composedBackend.Element,
874
+ )
875
+ }
876
+ } else {
877
+ if (BM.DOMLIKE || (BM.DYNAMIC && context.mode === BackendMode.Domlike)) {
878
+ (backendParent as domlikeBackend.Element).appendChild(frag as domlikeBackend.Element)
879
+ } else {
880
+ (backendParent as composedBackend.Element).spliceAppend(frag as composedBackend.Element)
881
+ }
882
+ }
883
+ } else if (removeCount > 0) {
884
+ if (BM.DOMLIKE || (BM.DYNAMIC && context.mode === BackendMode.Domlike)) {
885
+ const rel = (before as Element)._$backendElement as domlikeBackend.Element
886
+ for (let i = 1; i < removeCount; i += 1) {
887
+ const next = rel.nextSibling
888
+ if (next) (backendParent as domlikeBackend.Element).removeChild(next)
889
+ }
890
+ if (removeCount > 0) (backendParent as domlikeBackend.Element).removeChild(rel)
891
+ } else {
892
+ (backendParent as composedBackend.Element).spliceRemove(
893
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
894
+ (before as Element)._$backendElement as composedBackend.Element,
895
+ removeCount,
896
+ )
897
+ }
898
+ }
899
+ }
900
+
901
+ // in single-slot mode, use a simpler update logic; otherwise use a slower one
902
+ if (sharedNonVirtualParent !== undefined) {
903
+ if (sharedNonVirtualParent === null) {
904
+ // for nodes with no valid non-virtual parent, do nothing
905
+ } else if (
906
+ !parentConverted
907
+ && (!(newChild instanceof Element) || !newChild._$virtual)
908
+ && (!(relChild instanceof Element) || !relChild._$virtual)
909
+ ) {
910
+ // for non-virtual children, use single child operation
911
+ if (removal) {
912
+ if (newChild) {
913
+ (sharedNonVirtualParent as composedBackend.Element).replaceChild(
914
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
915
+ // domlike backend also
916
+ (newChild as Element)._$backendElement as composedBackend.Element,
917
+ (relChild as Element)._$backendElement as composedBackend.Element,
918
+ )
919
+ } else {
920
+ (sharedNonVirtualParent as composedBackend.Element).removeChild(
921
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
922
+ // domlike backend also
923
+ (relChild as Element)._$backendElement as composedBackend.Element,
924
+ )
925
+ }
926
+ } else if (relChild) {
927
+ (sharedNonVirtualParent as composedBackend.Element).insertBefore(
928
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
929
+ // domlike backend also
930
+ (newChild as Element)._$backendElement as composedBackend.Element,
931
+ (relChild as Element)._$backendElement as composedBackend.Element,
932
+ )
933
+ } else {
934
+ (sharedNonVirtualParent as composedBackend.Element).appendChild(
935
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
936
+ // domlike backend also
937
+ (newChild as Element)._$backendElement as composedBackend.Element,
938
+ )
939
+ }
940
+ } else {
941
+ // for virtual children in a single slot, group the children and use splice
942
+ groupUpdate(null)
943
+ }
944
+ } else {
945
+ // for multi-slots, find out each slot that needs update, and update them one by one
946
+ const slots: (Element | string)[] = []
947
+ const recInheritedSlots = (c: Node) => {
948
+ let slot: Element | string
949
+ if (c._$nodeSlotElement) {
950
+ slot = c._$nodeSlotElement
951
+ } else if (c instanceof Element) {
952
+ if (c._$inheritSlots) {
953
+ c.childNodes.forEach(recInheritedSlots)
954
+ return
955
+ }
956
+ slot = c._$nodeSlot
957
+ } else {
958
+ slot = ''
959
+ }
960
+ if (slots.indexOf(slot) < 0) slots.push(slot)
961
+ }
962
+ if (removal && relChild) recInheritedSlots(relChild)
963
+ if (newChild) recInheritedSlots(newChild)
964
+ for (let i = 0; i < slots.length; i += 1) {
965
+ const slot = slots[i]!
966
+ if (typeof slot === 'string') {
967
+ const s = ((slotParent as GeneralComponentInstance).shadowRoot as ShadowRoot)
968
+ .getSlotElementFromName(slot)
969
+ groupUpdate(s as Element | null, slot)
970
+ } else {
971
+ groupUpdate(slot, '')
972
+ }
973
+ }
974
+ }
975
+ if (!(BM.DOMLIKE || (BM.DYNAMIC && context.mode === BackendMode.Domlike))) {
976
+ if (sharedFrag) (sharedFrag as composedBackend.Element).release()
977
+ }
978
+
979
+ // write extra info if needed
980
+ if (globalOptions.writeExtraInfoToAttr) {
981
+ if (removal && relChild instanceof Element) {
982
+ relChild._$backendElement?.removeAttribute('exparser:info-in-slot-of')
983
+ }
984
+ if (newChild instanceof Element) {
985
+ if (shadowParent instanceof Component) {
986
+ newChild._$backendElement?.setAttribute('exparser:info-in-slot-of', shadowParent._$componentInstanceId)
987
+ } else {
988
+ newChild._$backendElement?.removeAttribute('exparser:info-in-slot-of')
989
+ }
990
+ }
991
+ }
992
+ }
993
+
994
+ private static insertChildSingleOperation(
995
+ parent: Element,
996
+ newChild: Node | null,
997
+ oriPosIndex: number,
998
+ replace: boolean,
999
+ ) {
1000
+ if (newChild && parent.ownerShadowRoot !== newChild.ownerShadowRoot) {
1001
+ throw new Error('Cannot move the node from one shadow tree to another shadow tree.')
1002
+ }
1003
+ let posIndex = oriPosIndex
1004
+ const relChild: Node | undefined = posIndex >= 0 ? parent.childNodes[posIndex] : undefined
1005
+ let removal: boolean
1006
+ if (replace) {
1007
+ if (!relChild) {
1008
+ removal = false
1009
+ } else if (newChild === relChild) {
1010
+ removal = false
1011
+ } else {
1012
+ removal = true
1013
+ }
1014
+ } else {
1015
+ removal = false
1016
+ }
1017
+ if (!removal && !newChild) return
1018
+ let needUpdateSlotCount = false
1019
+ let slotCountDiff = 0
1020
+
1021
+ // change the parent of newChild
1022
+ let oldParent: Element | null
1023
+ if (newChild) {
1024
+ oldParent = newChild.parentNode
1025
+ if (oldParent) {
1026
+ const oldPosIndex = oldParent.childNodes.indexOf(newChild)
1027
+ oldParent.childNodes.splice(oldPosIndex, 1)
1028
+ if (oldParent === parent && oldPosIndex < posIndex) posIndex -= 1
1029
+ if (BM.DOMLIKE || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Domlike)) {
1030
+ // removal of in-tree elements are not needed for DOM backend
1031
+ // do nothing
1032
+ } else if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1033
+ (oldParent._$backendElement as backend.Element | null)?.removeChild(
1034
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1035
+ (newChild as Element)._$backendElement as backend.Element,
1036
+ oldPosIndex,
1037
+ )
1038
+ } else {
1039
+ Element.insertChildComposed(
1040
+ parent,
1041
+ null,
1042
+ newChild,
1043
+ true,
1044
+ oldPosIndex,
1045
+ )
1046
+ }
1047
+ }
1048
+ newChild.parentNode = parent
1049
+ if (newChild instanceof Element) {
1050
+ if (oldParent !== parent) {
1051
+ if (oldParent) {
1052
+ oldParent._$mutationObserverTarget?.detachChild(newChild)
1053
+ if (newChild._$subtreeSlotCount > 0) {
1054
+ Element.updateSlotCount(oldParent, -newChild._$subtreeSlotCount)
1055
+ }
1056
+ }
1057
+ parent._$mutationObserverTarget?.attachChild(newChild)
1058
+ if (newChild._$subtreeSlotCount > 0) {
1059
+ needUpdateSlotCount = true
1060
+ slotCountDiff += newChild._$subtreeSlotCount
1061
+ }
1062
+ } else if (newChild._$subtreeSlotCount > 0) {
1063
+ needUpdateSlotCount = true
1064
+ }
1065
+ }
1066
+ } else {
1067
+ oldParent = null
1068
+ }
1069
+
1070
+ // spread in composed tree
1071
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1072
+ if (parent._$backendElement) {
1073
+ if (removal) {
1074
+ if (newChild) {
1075
+ (parent._$backendElement as backend.Element).replaceChild(
1076
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1077
+ (newChild as Element)._$backendElement as backend.Element,
1078
+ (relChild as Element)._$backendElement as backend.Element,
1079
+ posIndex,
1080
+ )
1081
+ } else {
1082
+ (parent._$backendElement as backend.Element).removeChild(
1083
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1084
+ (relChild as Element)._$backendElement as backend.Element,
1085
+ posIndex,
1086
+ )
1087
+ }
1088
+ } else if (relChild) {
1089
+ (parent._$backendElement as backend.Element).insertBefore(
1090
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1091
+ (newChild as Element)._$backendElement as backend.Element,
1092
+ (relChild as Element)._$backendElement as backend.Element,
1093
+ posIndex,
1094
+ )
1095
+ } else {
1096
+ (parent._$backendElement as backend.Element).appendChild(
1097
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1098
+ (newChild as Element)._$backendElement as backend.Element,
1099
+ )
1100
+ }
1101
+ }
1102
+ } else {
1103
+ Element.insertChildComposed(
1104
+ parent,
1105
+ newChild,
1106
+ relChild,
1107
+ removal,
1108
+ posIndex,
1109
+ )
1110
+ }
1111
+
1112
+ // remove parent of relChild if needed
1113
+ if (removal && relChild instanceof Element) {
1114
+ parent._$mutationObserverTarget?.detachChild(relChild)
1115
+ relChild.parentNode = null
1116
+ if (relChild._$subtreeSlotCount > 0) {
1117
+ needUpdateSlotCount = true
1118
+ slotCountDiff -= relChild._$subtreeSlotCount
1119
+ }
1120
+ }
1121
+
1122
+ // handling child nodes list
1123
+ if (newChild) {
1124
+ if (posIndex < 0) parent.childNodes.push(newChild)
1125
+ else if (removal) parent.childNodes[posIndex] = newChild
1126
+ else parent.childNodes.splice(posIndex, 0, newChild)
1127
+ } else if (removal) {
1128
+ parent.childNodes.splice(posIndex, 1)
1129
+ }
1130
+
1131
+ // update id and slot cache if needed
1132
+ parent.ownerShadowRoot?._$markIdCacheDirty()
1133
+ if (needUpdateSlotCount) {
1134
+ Element.updateSlotCount(parent, slotCountDiff)
1135
+ parent.ownerShadowRoot?._$checkSlotChanges()
1136
+ }
1137
+
1138
+ // call life-times
1139
+ if (removal) {
1140
+ Element.checkAndCallDetached(relChild as Node)
1141
+ Element.checkChildObservers(parent, 'remove', relChild as Node)
1142
+ }
1143
+ if (newChild) {
1144
+ if (oldParent?._$attached) {
1145
+ if (parent._$attached) {
1146
+ Element.checkAndCallMoved(newChild)
1147
+ } else {
1148
+ Element.checkAndCallDetached(newChild)
1149
+ }
1150
+ } else if (parent._$attached) {
1151
+ Element.checkAndCallAttached(newChild)
1152
+ }
1153
+ if (oldParent === parent) {
1154
+ Element.checkChildObservers(parent, 'move', newChild)
1155
+ } else {
1156
+ if (oldParent) {
1157
+ Element.checkChildObservers(oldParent, 'remove', newChild)
1158
+ }
1159
+ Element.checkChildObservers(parent, 'add', newChild)
1160
+ }
1161
+ }
1162
+ }
1163
+
1164
+ private static insertChildBatchRemoval(
1165
+ parent: Element,
1166
+ posIndex: number,
1167
+ count: number,
1168
+ ) {
1169
+ const relChild = parent.childNodes[posIndex]
1170
+ let needUpdateSlotCount = false
1171
+ let slotCountDiff = 0
1172
+
1173
+ // spread in composed tree
1174
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1175
+ if (parent._$backendElement) {
1176
+ (parent._$backendElement as backend.Element).spliceRemove(
1177
+ (relChild as Element)._$backendElement as backend.Element,
1178
+ count,
1179
+ )
1180
+ }
1181
+ } else {
1182
+ for (let i = 0; i < count; i += 1) {
1183
+ Element.insertChildComposed(
1184
+ parent,
1185
+ null,
1186
+ parent.childNodes[posIndex + i],
1187
+ true,
1188
+ posIndex + i,
1189
+ )
1190
+ }
1191
+ }
1192
+
1193
+ // remove parent
1194
+ for (let i = 0; i < count; i += 1) {
1195
+ const relChild = parent.childNodes[posIndex + i]!
1196
+ relChild.parentNode = null
1197
+ if (relChild instanceof Element) {
1198
+ parent._$mutationObserverTarget?.detachChild(relChild)
1199
+ if (relChild._$subtreeSlotCount > 0) {
1200
+ needUpdateSlotCount = true
1201
+ slotCountDiff -= relChild._$subtreeSlotCount
1202
+ }
1203
+ }
1204
+ }
1205
+
1206
+ // handling child nodes list
1207
+ const relChildren = parent.childNodes.splice(posIndex, count)
1208
+
1209
+ // update id and slot cache if needed
1210
+ parent.ownerShadowRoot?._$markIdCacheDirty()
1211
+ if (needUpdateSlotCount) {
1212
+ Element.updateSlotCount(parent, slotCountDiff)
1213
+ parent.ownerShadowRoot?._$checkSlotChanges()
1214
+ }
1215
+
1216
+ // call life-times
1217
+ for (let i = 0; i < count; i += 1) {
1218
+ const relChild = relChildren[i]!
1219
+ Element.checkAndCallDetached(relChild)
1220
+ Element.checkChildObservers(parent, 'remove', relChild)
1221
+ }
1222
+ }
1223
+
1224
+ private static insertChildBatchInsertion(
1225
+ parent: Element,
1226
+ newChildList: Node[],
1227
+ posIndex: number,
1228
+ ) {
1229
+ const relChild: Node | undefined = posIndex >= 0 ? parent.childNodes[posIndex] : undefined
1230
+ let needUpdateSlotCount = false
1231
+ let slotCountDiff = 0
1232
+
1233
+ // change the parent of newChild
1234
+ let frag: backend.Element | null
1235
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1236
+ const backendContext = parent.getBackendContext() as backend.Context
1237
+ frag = backendContext.createFragment()
1238
+ } else {
1239
+ frag = null
1240
+ }
1241
+ for (let i = 0; i < newChildList.length; i += 1) {
1242
+ const newChild = newChildList[i]!
1243
+ if (parent.ownerShadowRoot !== newChild.ownerShadowRoot) {
1244
+ throw new Error('Cannot move the node from one shadow tree to another shadow tree.')
1245
+ }
1246
+ const oldParent = newChild.parentNode
1247
+ if (oldParent) {
1248
+ throw new Error('Cannot batch-insert the node which already has a parent.')
1249
+ }
1250
+ newChild.parentNode = parent
1251
+ if (newChild instanceof Element) {
1252
+ parent._$mutationObserverTarget?.attachChild(newChild)
1253
+ if (newChild._$subtreeSlotCount > 0) {
1254
+ needUpdateSlotCount = true
1255
+ slotCountDiff += newChild._$subtreeSlotCount
1256
+ }
1257
+ }
1258
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1259
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1260
+ const be = (newChild as Element)._$backendElement as backend.Element
1261
+ (frag as backend.Element).appendChild(be)
1262
+ }
1263
+ }
1264
+
1265
+ // spread in composed tree
1266
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1267
+ if (parent._$backendElement) {
1268
+ if (relChild) {
1269
+ (parent._$backendElement as backend.Element).spliceBefore(
1270
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1271
+ (relChild as Element)._$backendElement as backend.Element,
1272
+ 0,
1273
+ frag as backend.Element,
1274
+ )
1275
+ } else {
1276
+ (parent._$backendElement as backend.Element).spliceAppend(frag as backend.Element)
1277
+ }
1278
+ }
1279
+ (frag as backend.Element).release()
1280
+ } else {
1281
+ for (let i = 0; i < newChildList.length; i += 1) {
1282
+ const newChild = newChildList[i]!
1283
+ Element.insertChildComposed(
1284
+ parent,
1285
+ newChild,
1286
+ relChild,
1287
+ false,
1288
+ posIndex >= 0 ? posIndex : parent.childNodes.length,
1289
+ )
1290
+ }
1291
+ }
1292
+
1293
+ // handling child nodes list
1294
+ if (relChild) {
1295
+ parent.childNodes.splice(posIndex, 0, ...newChildList)
1296
+ } else {
1297
+ parent.childNodes.push(...newChildList)
1298
+ }
1299
+
1300
+ // update id and slot cache if needed
1301
+ parent.ownerShadowRoot?._$markIdCacheDirty()
1302
+ if (needUpdateSlotCount) {
1303
+ Element.updateSlotCount(parent, slotCountDiff)
1304
+ parent.ownerShadowRoot?._$checkSlotChanges()
1305
+ }
1306
+
1307
+ // call life-times
1308
+ for (let i = 0; i < newChildList.length; i += 1) {
1309
+ const newChild = newChildList[i]!
1310
+ if (parent._$attached) {
1311
+ Element.checkAndCallAttached(newChild)
1312
+ }
1313
+ Element.checkChildObservers(parent, 'add', newChild)
1314
+ }
1315
+ }
1316
+
1317
+ private static insertChildPlaceholerReplace(
1318
+ parent: Element,
1319
+ posIndex: number,
1320
+ replacer: Element,
1321
+ ) {
1322
+ if (replacer && parent.ownerShadowRoot !== replacer.ownerShadowRoot) {
1323
+ throw new Error('Cannot move the node from one shadow tree to another shadow tree.')
1324
+ }
1325
+ if (replacer.parentNode) {
1326
+ throw new Error('Cannot replace with the node which already has a parent.')
1327
+ }
1328
+ const placeholder = parent.childNodes[posIndex]
1329
+ if (!(placeholder instanceof Element)) {
1330
+ throw new Error('Cannot replace on text nodes.')
1331
+ }
1332
+ if (placeholder === replacer) return
1333
+
1334
+ // change the parent of replacer's children
1335
+ let frag: backend.Element | null
1336
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1337
+ const backendContext = parent.getBackendContext() as backend.Context
1338
+ frag = backendContext.createFragment()
1339
+ } else {
1340
+ frag = null
1341
+ }
1342
+ const replacedChildren = placeholder.childNodes
1343
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1344
+ if (replacedChildren.length > 0) {
1345
+ (placeholder._$backendElement as backend.Element | null)?.spliceRemove(
1346
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1347
+ (replacedChildren[0] as Element)._$backendElement as backend.Element,
1348
+ replacedChildren.length,
1349
+ )
1350
+ }
1351
+ }
1352
+ for (let i = 0; i < replacedChildren.length; i += 1) {
1353
+ const child = replacedChildren[i]!
1354
+ if (BM.DOMLIKE || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Domlike)) {
1355
+ // removal of in-tree elements are not needed for DOM backend
1356
+ // do nothing
1357
+ } else if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1358
+ // since `TextNode` also has `backendElement` private field, just make it as `Element`
1359
+ const be = (child as Element)._$backendElement as backend.Element
1360
+ (frag as backend.Element).appendChild(be)
1361
+ } else {
1362
+ Element.insertChildComposed(
1363
+ placeholder,
1364
+ null,
1365
+ child,
1366
+ true,
1367
+ i,
1368
+ )
1369
+ }
1370
+ child.parentNode = replacer
1371
+ if (child instanceof Element) {
1372
+ placeholder._$mutationObserverTarget?.detachChild(child)
1373
+ parent._$mutationObserverTarget?.attachChild(child)
1374
+ }
1375
+ }
1376
+
1377
+ // spread in composed tree
1378
+ if (BM.SHADOW || (BM.DYNAMIC && parent.getBackendMode() === BackendMode.Shadow)) {
1379
+ if (parent._$backendElement) {
1380
+ (parent._$backendElement as backend.Element).replaceChild(
1381
+ replacer._$backendElement as backend.Element,
1382
+ placeholder._$backendElement as backend.Element,
1383
+ posIndex,
1384
+ );
1385
+ // eslint-disable-next-line arrow-body-style
1386
+ (replacer._$backendElement as backend.Element | null)?.spliceAppend(frag as backend.Element)
1387
+ }
1388
+ (frag as backend.Element).release()
1389
+ } else {
1390
+ Element.insertChildComposed(
1391
+ parent,
1392
+ replacer,
1393
+ placeholder,
1394
+ true,
1395
+ posIndex,
1396
+ )
1397
+ for (let i = 0; i < replacedChildren.length; i += 1) {
1398
+ const child = replacedChildren[i]!
1399
+ Element.insertChildComposed(
1400
+ replacer,
1401
+ child,
1402
+ undefined,
1403
+ false,
1404
+ i,
1405
+ )
1406
+ }
1407
+ }
1408
+
1409
+ // change the parent of placeholder
1410
+ parent._$mutationObserverTarget?.detachChild(placeholder)
1411
+ placeholder.parentNode = null
1412
+
1413
+ // change the parent of replacer
1414
+ replacer.parentNode = parent
1415
+ parent._$mutationObserverTarget?.attachChild(replacer)
1416
+
1417
+ // handling child nodes list for parent
1418
+ parent.childNodes[posIndex] = replacer
1419
+
1420
+ // handling child nodes list for replacer
1421
+ replacer.childNodes.push(...placeholder.childNodes)
1422
+
1423
+ // handling child nodes list for placeholder
1424
+ placeholder.childNodes = []
1425
+
1426
+ // update id and slot cache if needed
1427
+ parent.ownerShadowRoot?._$markIdCacheDirty()
1428
+
1429
+ // call life-times
1430
+ if (parent._$attached) {
1431
+ Element.checkAndCallDetached(placeholder as Node)
1432
+ Element.checkChildObservers(parent, 'remove', placeholder as Node)
1433
+ Element.checkAndCallAttached(replacer)
1434
+ Element.checkChildObservers(parent, 'add', replacer as Node)
1435
+ for (let i = 0; i < replacedChildren.length; i += 1) {
1436
+ const child = replacedChildren[i]!
1437
+ Element.checkAndCallMoved(child)
1438
+ Element.checkChildObservers(parent, 'move', child)
1439
+ }
1440
+ }
1441
+ }
1442
+
1443
+ appendChild(child: Node) {
1444
+ Element.insertChildSingleOperation(this, child, this.childNodes.length, false)
1445
+ }
1446
+
1447
+ insertChildAt(child: Node, index: number) {
1448
+ Element.insertChildSingleOperation(this, child, index, false)
1449
+ }
1450
+
1451
+ insertBefore(child: Node, before: Node) {
1452
+ const index = this.childNodes.indexOf(before)
1453
+ Element.insertChildSingleOperation(this, child, index, false)
1454
+ }
1455
+
1456
+ removeChildAt(index: number) {
1457
+ Element.insertChildSingleOperation(this, null, index, true)
1458
+ }
1459
+
1460
+ removeChild(child: Node) {
1461
+ const index = this.childNodes.indexOf(child)
1462
+ Element.insertChildSingleOperation(this, null, index, true)
1463
+ }
1464
+
1465
+ replaceChildAt(child: Node, index: number) {
1466
+ Element.insertChildSingleOperation(this, child, index, true)
1467
+ }
1468
+
1469
+ replaceChild(child: Node, relChild: Node) {
1470
+ const index = this.childNodes.indexOf(relChild)
1471
+ Element.insertChildSingleOperation(this, child, index, true)
1472
+ }
1473
+
1474
+ insertChildren(children: Node[], index: number) {
1475
+ Element.insertChildBatchInsertion(this, children, index)
1476
+ }
1477
+
1478
+ removeChildren(index: number, count: number) {
1479
+ Element.insertChildBatchRemoval(this, index, count)
1480
+ }
1481
+
1482
+ selfReplaceWith(replaceWith: Element) {
1483
+ const parent = this.parentNode
1484
+ if (parent) {
1485
+ Element.insertChildPlaceholerReplace(parent, parent.childNodes.indexOf(this), replaceWith)
1486
+ }
1487
+ }
1488
+
1489
+ /** @internal */
1490
+ protected static _$generateIdMap(node: ShadowRoot): {[id: string]: Element} {
1491
+ const idMap = Object.create(null) as {[id: string]: Element}
1492
+ const dfs = function dfs(node: Node) {
1493
+ if (node instanceof Element) {
1494
+ const nodeId = node._$nodeId
1495
+ if (nodeId) {
1496
+ if (!idMap[nodeId]) idMap[nodeId] = node
1497
+ }
1498
+ node.childNodes.forEach(dfs)
1499
+ }
1500
+ }
1501
+ dfs(node)
1502
+ return idMap
1503
+ }
1504
+
1505
+ /** Trigger an event on the element */
1506
+ triggerEvent(name: string, detail?: unknown, options?: EventOptions) {
1507
+ Event.triggerEvent(this, name, detail, options)
1508
+ }
1509
+
1510
+ /** Trigger an event with specified event object on the element */
1511
+ dispatchEvent(ev: Event<unknown>) {
1512
+ Event.dispatchEvent(this, ev)
1513
+ }
1514
+
1515
+ private _$updateEventDefaultPrevented(name: string, enabled: boolean) {
1516
+ if (!this._$backendElement) return
1517
+ if (BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike)) {
1518
+ (this._$nodeTreeContext as domlikeBackend.Context).setElementEventDefaultPrevented(
1519
+ this._$backendElement as domlikeBackend.Element,
1520
+ name,
1521
+ enabled,
1522
+ )
1523
+ } else {
1524
+ (this._$backendElement as backend.Element).setEventDefaultPrevented(name, enabled)
1525
+ }
1526
+ }
1527
+
1528
+ /** Add an event listener on the element */
1529
+ addListener(name: string, func: EventListener<unknown>, options?: EventListenerOptions) {
1530
+ const finalChanged = this._$eventTarget.addListener(name, func, options)
1531
+ if (finalChanged === FinalChanged.Init) this._$updateEventDefaultPrevented(name, false)
1532
+ else if (finalChanged === FinalChanged.Added) this._$updateEventDefaultPrevented(name, true)
1533
+ if (this instanceof Component && this._$definition._$options.listenerChangeLifetimes) {
1534
+ this.triggerLifetime('listenerChanged', [true, name, func, options])
1535
+ }
1536
+ }
1537
+
1538
+ /** Remove an event listener on the element */
1539
+ removeListener(name: string, func: EventListener<unknown>, options?: EventListenerOptions) {
1540
+ const finalChanged = this._$eventTarget.removeListener(name, func, options)
1541
+ if (finalChanged === FinalChanged.Failed) return
1542
+ if (finalChanged !== FinalChanged.NotChanged) this._$updateEventDefaultPrevented(name, false)
1543
+ if (this instanceof Component && this._$definition._$options.listenerChangeLifetimes) {
1544
+ this.triggerLifetime('listenerChanged', [false, name, func, options])
1545
+ }
1546
+ }
1547
+
1548
+ /** Get an attribute value ( `null` if not set or removed) */
1549
+ getAttribute(name: string): unknown {
1550
+ if (this._$nodeAttributes) return this._$nodeAttributes[name]
1551
+ return null
1552
+ }
1553
+
1554
+ /** Update an attribute value */
1555
+ updateAttribute(name: string, value: unknown) {
1556
+ if (BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike)) {
1557
+ if (typeof value === 'boolean') {
1558
+ if (value === true) {
1559
+ this.setAttribute(name, '')
1560
+ } else {
1561
+ this.removeAttribute(name)
1562
+ }
1563
+ } else {
1564
+ this.setAttribute(name, value === undefined || value === null ? '' : String(value))
1565
+ }
1566
+ } else {
1567
+ if (value === undefined) {
1568
+ this.removeAttribute(name)
1569
+ } else {
1570
+ this.setAttribute(name, value)
1571
+ }
1572
+ }
1573
+ }
1574
+
1575
+ /** Set an attribute value */
1576
+ setAttribute(name: string, value: unknown) {
1577
+ let attrs: { [name: string]: unknown }
1578
+ if (this._$nodeAttributes) {
1579
+ attrs = this._$nodeAttributes
1580
+ } else {
1581
+ attrs = Object.create(null) as { [name: string]: unknown }
1582
+ this._$nodeAttributes = attrs
1583
+ }
1584
+ attrs[name] = value
1585
+ if (this._$backendElement) {
1586
+ this._$backendElement.setAttribute(name, value)
1587
+ }
1588
+ }
1589
+
1590
+ /** Remove an attribute */
1591
+ removeAttribute(name: string) {
1592
+ if (this._$nodeAttributes) {
1593
+ delete this._$nodeAttributes[name]
1594
+ }
1595
+ if (this._$backendElement) this._$backendElement.removeAttribute(name)
1596
+ }
1597
+
1598
+ /** Set a mark on the element */
1599
+ setMark(name: string, value: unknown) {
1600
+ if (!this._$marks) {
1601
+ const marks: { [name: string]: unknown } = {}
1602
+ marks[name] = value
1603
+ this._$marks = marks
1604
+ } else {
1605
+ this._$marks[name] = value
1606
+ }
1607
+ }
1608
+
1609
+ /**
1610
+ * Collect the marks on the element
1611
+ *
1612
+ * The marks includes the marks on ancestors (in shadow tree) of the element.
1613
+ * If multiple marks on different elements shares the same name,
1614
+ * the mark value on the child-most element is accepted.
1615
+ */
1616
+ collectMarks(): { [name: string]: unknown } {
1617
+ const ret = {} as { [name: string]: unknown }
1618
+ let cur: Element | null
1619
+ for (cur = this; cur; cur = cur.parentNode) {
1620
+ const marks = cur._$marks
1621
+ if (marks) {
1622
+ Object.keys(marks).forEach((name) => {
1623
+ if (!Object.prototype.hasOwnProperty.call(ret, name)) {
1624
+ ret[name] = marks[name]
1625
+ }
1626
+ })
1627
+ }
1628
+ }
1629
+ return ret
1630
+ }
1631
+
1632
+ /**
1633
+ * Attach the element into the backend, swapping out a placeholder element in the backend.
1634
+ *
1635
+ * The `element` must not be a child node of another element,
1636
+ * must not be attached before,
1637
+ * and must not have a `ownerShadowRoot` .
1638
+ * The `element` `targetParent` and `targetNode` must be in the same backend context.
1639
+ * The `element` replaces the `targetNode` in the `targetParent` .
1640
+ */
1641
+ static replaceDocumentElement(
1642
+ element: Element,
1643
+ targetParent: GeneralBackendElement,
1644
+ targetNode: GeneralBackendElement,
1645
+ ) {
1646
+ if (element._$attached) {
1647
+ throw new Error('An attached element cannot be attached again')
1648
+ }
1649
+ if (element._$backendElement) {
1650
+ if (BM.DOMLIKE || (BM.DYNAMIC && element.getBackendMode() === BackendMode.Domlike)) {
1651
+ (targetParent as domlikeBackend.Element).replaceChild(
1652
+ element._$backendElement as domlikeBackend.Element,
1653
+ targetNode as domlikeBackend.Element,
1654
+ )
1655
+ } else {
1656
+ (targetParent as backend.Element | composedBackend.Element).replaceChild(
1657
+ element._$backendElement as backend.Element,
1658
+ targetNode as backend.Element,
1659
+ )
1660
+ }
1661
+ }
1662
+ Element.checkAndCallAttached(element)
1663
+ }
1664
+
1665
+ /**
1666
+ * Make the element looks like attached.
1667
+ *
1668
+ * If the element will never be attached to backend or it has no backend element at all,
1669
+ * this can be used to trigger `attached` life-time.
1670
+ */
1671
+ static pretendAttached(element: Element) {
1672
+ if (!element._$attached) {
1673
+ Element.checkAndCallAttached(element)
1674
+ }
1675
+ }
1676
+
1677
+ /**
1678
+ * Make the element looks like detached.
1679
+ *
1680
+ * This can be used to trigger `detached` life-time without remove the element in the backend.
1681
+ */
1682
+ static pretendDetached(element: Element) {
1683
+ if (element._$attached) {
1684
+ Element.checkAndCallDetached(element)
1685
+ }
1686
+ }
1687
+
1688
+ /** Check the element is attached or not */
1689
+ static isAttached(element: Element): boolean {
1690
+ return element._$attached
1691
+ }
1692
+
1693
+ /**
1694
+ * Set the slot name of the element
1695
+ *
1696
+ * Once this method is called for an `element` ,
1697
+ * it will be treated as a slot which can contain child nodes in composed tree.
1698
+ * This method should not be used in components,
1699
+ * otherwise the slot content will always be dangled.
1700
+ */
1701
+ static setSlotName(element: Element, name?: string) {
1702
+ if (element._$inheritSlots) {
1703
+ throw new Error('Slot-inherit mode is not usable in slot element')
1704
+ }
1705
+ if (element._$inheritSlots) {
1706
+ throw new Error('Component cannot be used as slot element')
1707
+ }
1708
+ const slotName = name ? String(name) : ''
1709
+ if (element._$slotName === slotName) return
1710
+ const needIncSlotCount = element._$slotName === null
1711
+ element._$slotName = slotName
1712
+ if (BM.SHADOW || (BM.DYNAMIC && element.getBackendMode() === BackendMode.Shadow)) {
1713
+ (element._$backendElement as backend.Element | null)?.setSlotName(slotName)
1714
+ }
1715
+ const owner = element.ownerShadowRoot
1716
+ if (owner) {
1717
+ if (needIncSlotCount && owner.isDynamicSlots()) {
1718
+ element._$slotValues = Object.create(null) as DataList
1719
+ }
1720
+ Element.updateSlotCount(element, needIncSlotCount ? 1 : 0)
1721
+ owner._$checkSlotChanges()
1722
+ }
1723
+ }
1724
+
1725
+ /**
1726
+ * Get the slot name of the element
1727
+ */
1728
+ static getSlotName(element: Element): string | undefined {
1729
+ const sn = element._$slotName
1730
+ return sn === null ? undefined : sn
1731
+ }
1732
+
1733
+ /**
1734
+ * Set the virtual node to slot-inherit mode
1735
+ *
1736
+ * In slot-inherit mode of an element,
1737
+ * the child nodes of the element will be treated as siblings and can have different target slot.
1738
+ */
1739
+ static setInheritSlots(element: VirtualNode) {
1740
+ if (element._$slotName !== null || element.childNodes.length !== 0) {
1741
+ throw new Error('Slot-inherit mode cannot be set when the element has any child node')
1742
+ }
1743
+ if (BM.SHADOW || (BM.DYNAMIC && element.getBackendMode() === BackendMode.Shadow)) {
1744
+ (element._$backendElement as backend.Element | null)?.setSlot(
1745
+ element._$nodeSlot,
1746
+ element._$inheritSlots,
1747
+ )
1748
+ }
1749
+ element._$inheritSlots = true
1750
+ }
1751
+
1752
+ /** Get whether the slot-inherit mode is set or not */
1753
+ static getInheritSlots(element: Element) {
1754
+ return element._$inheritSlots
1755
+ }
1756
+
1757
+ /**
1758
+ * Set the binding slot of specific node
1759
+ *
1760
+ * Necessary if node belongs to a dynamic slot, which cannot be identified by slot name.
1761
+ */
1762
+ static setSlotElement(node: Node, slot: Element) {
1763
+ const oldSlotElement = node._$nodeSlotElement
1764
+ if (oldSlotElement === slot) return
1765
+ node._$nodeSlotElement = slot
1766
+ if (node instanceof Element) {
1767
+ node._$nodeSlot = slot._$slotName || ''
1768
+ }
1769
+
1770
+ // TODO shadow mode
1771
+ const parent = node.parentNode
1772
+ let slotParent = parent
1773
+ while (slotParent?._$inheritSlots) {
1774
+ slotParent = slotParent.parentNode
1775
+ }
1776
+ if (slotParent instanceof Component && !slotParent._$external) {
1777
+ Element.insertChildReassignComposed(
1778
+ parent!,
1779
+ node,
1780
+ oldSlotElement,
1781
+ slot,
1782
+ parent!.childNodes.indexOf(node) + 1,
1783
+ )
1784
+ }
1785
+ }
1786
+
1787
+ static getSlotElement(node: Node) {
1788
+ if (node._$nodeSlotElement) return node._$nodeSlotElement
1789
+ let parent = node.parentNode
1790
+ while (parent?._$inheritSlots) {
1791
+ parent = parent.parentNode
1792
+ }
1793
+ if (parent instanceof Component && !parent._$external) {
1794
+ const slot = (parent.shadowRoot as ShadowRoot).getContainingSlot(node)
1795
+ return slot
1796
+ }
1797
+ return undefined
1798
+ }
1799
+
1800
+ /** Get composed parent (including virtual nodes) */
1801
+ getComposedParent(): Element | null {
1802
+ if (this instanceof ShadowRoot) {
1803
+ return this.getHostNode()
1804
+ }
1805
+ let parent = this.parentNode
1806
+ while (parent?._$inheritSlots) {
1807
+ parent = parent.parentNode
1808
+ }
1809
+ if (parent instanceof Component && !parent._$external) {
1810
+ return (parent.shadowRoot as ShadowRoot).getContainingSlot(this)
1811
+ }
1812
+ return parent
1813
+ }
1814
+
1815
+ /**
1816
+ * Get the composed children
1817
+ *
1818
+ * This method always returns a new array.
1819
+ * It is convinient but less performant.
1820
+ * For better performance, consider using `forEachComposedChild` .
1821
+ */
1822
+ getComposedChildren(): Node[] {
1823
+ const ret: Node[] = []
1824
+ this.forEachComposedChild((child) => {
1825
+ ret.push(child)
1826
+ })
1827
+ return ret
1828
+ }
1829
+
1830
+ /**
1831
+ * Iterate composed child nodes (including virtual nodes)
1832
+ *
1833
+ * if `f` returns `false` then the iteration is interrupted.
1834
+ * Returns `true` if that happens.
1835
+ */
1836
+ forEachComposedChild(f: (node: Node) => boolean | void): boolean {
1837
+ if (this._$inheritSlots) return true
1838
+ if (this instanceof Component && !this._$external) {
1839
+ return f(this.shadowRoot as ShadowRoot) !== false
1840
+ }
1841
+ if (this._$slotName !== null) {
1842
+ let notEnded = true
1843
+ if (this.ownerShadowRoot) {
1844
+ this.ownerShadowRoot.forEachNodeInSpecifiedSlot(this, (node) => {
1845
+ if (f(node) === false) notEnded = false
1846
+ return notEnded
1847
+ })
1848
+ }
1849
+ return notEnded
1850
+ }
1851
+ const recInheritSlots = (children: Node[]) => {
1852
+ for (let i = 0; i < children.length; i += 1) {
1853
+ const child = children[i]!
1854
+ if (f(child) === false) return false
1855
+ if (child instanceof Element && child._$inheritSlots) {
1856
+ if (!recInheritSlots(child.childNodes)) return false
1857
+ }
1858
+ }
1859
+ return true
1860
+ }
1861
+ if (!recInheritSlots(this.childNodes)) return false
1862
+ return true
1863
+ }
1864
+
1865
+ /** Parse a selector string so that it can be used multiple queries */
1866
+ static parseSelector(str: string): ParsedSelector {
1867
+ return new ParsedSelector(str)
1868
+ }
1869
+
1870
+ /** Select the first descendant which matches the selector */
1871
+ querySelector(selectorStr: string | ParsedSelector): Element | null {
1872
+ const parsedSelector = selectorStr instanceof ParsedSelector
1873
+ ? selectorStr
1874
+ : new ParsedSelector(selectorStr)
1875
+ const ret = parsedSelector.query(this, true) as Element | null
1876
+ return ret
1877
+ }
1878
+
1879
+ /** Select all descendants which matches the selector */
1880
+ querySelectorAll(selectorStr: string | ParsedSelector): Element[] {
1881
+ const parsedSelector = selectorStr instanceof ParsedSelector
1882
+ ? selectorStr
1883
+ : new ParsedSelector(selectorStr)
1884
+ const ret = parsedSelector.query(this, false) as Element[]
1885
+ return ret
1886
+ }
1887
+
1888
+ /** Test whether the target matches the selector */
1889
+ static matchSelector(selectorStr: string | ParsedSelector, target: Element): boolean {
1890
+ const parsedSelector = selectorStr instanceof ParsedSelector
1891
+ ? selectorStr
1892
+ : new ParsedSelector(selectorStr)
1893
+ return parsedSelector.testSelector(null, target)
1894
+ }
1895
+
1896
+ /** Test whether the target in this subtree matches the selector */
1897
+ matchSelector(selectorStr: string | ParsedSelector, target: Element): boolean {
1898
+ const parsedSelector = selectorStr instanceof ParsedSelector
1899
+ ? selectorStr
1900
+ : new ParsedSelector(selectorStr)
1901
+ return parsedSelector.testSelector(this, target)
1902
+ }
1903
+
1904
+ /**
1905
+ * Get the bounding client rect
1906
+ *
1907
+ * Return zero values when the backend element is invalid or it does not have layout information.
1908
+ */
1909
+ getBoundingClientRect(cb: (res: BoundingClientRect) => void): void {
1910
+ const backendElement = this._$backendElement
1911
+ if (backendElement) {
1912
+ if (BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike)) {
1913
+ const e = backendElement as domlikeBackend.Element
1914
+ const res = e.getBoundingClientRect()
1915
+ setTimeout(() => {
1916
+ cb(res)
1917
+ }, 0)
1918
+ } else {
1919
+ (backendElement as backend.Element | composedBackend.Element).getBoundingClientRect(cb)
1920
+ }
1921
+ } else {
1922
+ setTimeout(() => {
1923
+ cb({
1924
+ left: 0,
1925
+ top: 0,
1926
+ width: 0,
1927
+ height: 0,
1928
+ })
1929
+ }, 0)
1930
+ }
1931
+ }
1932
+
1933
+ /**
1934
+ * Get the bounding client rect
1935
+ *
1936
+ * Return zero values when the backend element is invalid or it does not have layout information.
1937
+ */
1938
+ getScrollOffset(cb: (res: ScrollOffset) => void): void {
1939
+ const backendElement = this._$backendElement
1940
+ if (backendElement) {
1941
+ if (BM.DOMLIKE || (BM.DYNAMIC && this.getBackendMode() === BackendMode.Domlike)) {
1942
+ const e = backendElement as domlikeBackend.Element
1943
+ const res = {
1944
+ scrollLeft: e.scrollLeft,
1945
+ scrollTop: e.scrollTop,
1946
+ scrollWidth: e.scrollWidth,
1947
+ scrollHeight: e.scrollHeight,
1948
+ }
1949
+ setTimeout(() => {
1950
+ cb(res)
1951
+ }, 0)
1952
+ } else {
1953
+ (backendElement as backend.Element | composedBackend.Element).getScrollOffset(cb)
1954
+ }
1955
+ } else {
1956
+ setTimeout(() => {
1957
+ cb({
1958
+ scrollLeft: 0,
1959
+ scrollTop: 0,
1960
+ scrollWidth: 0,
1961
+ scrollHeight: 0,
1962
+ })
1963
+ }, 0)
1964
+ }
1965
+ }
1966
+ }