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.
- package/README.md +40 -0
- package/dist/glass_easel.all.d.ts +1 -0
- package/dist/glass_easel.all.js +2 -0
- package/dist/glass_easel.all.js.map +1 -0
- package/dist/glass_easel.domlike.global.d.ts +1 -0
- package/dist/glass_easel.domlike.global.js +2 -0
- package/dist/glass_easel.domlike.global.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types/src/backend/backend_protocol.d.ts +119 -0
- package/dist/types/src/backend/backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/composed_backend_protocol.d.ts +90 -0
- package/dist/types/src/backend/composed_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/domlike_backend_protocol.d.ts +76 -0
- package/dist/types/src/backend/domlike_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/backend/mode.d.ts +46 -0
- package/dist/types/src/backend/mode.d.ts.map +1 -0
- package/dist/types/src/backend/suggested_backend_protocol.d.ts +30 -0
- package/dist/types/src/backend/suggested_backend_protocol.d.ts.map +1 -0
- package/dist/types/src/behavior.d.ts +428 -0
- package/dist/types/src/behavior.d.ts.map +1 -0
- package/dist/types/src/class_list.d.ts +79 -0
- package/dist/types/src/class_list.d.ts.map +1 -0
- package/dist/types/src/component.d.ts +291 -0
- package/dist/types/src/component.d.ts.map +1 -0
- package/dist/types/src/component_params.d.ts +239 -0
- package/dist/types/src/component_params.d.ts.map +1 -0
- package/dist/types/src/component_space.d.ts +164 -0
- package/dist/types/src/component_space.d.ts.map +1 -0
- package/dist/types/src/data_path.d.ts +5 -0
- package/dist/types/src/data_path.d.ts.map +1 -0
- package/dist/types/src/data_proxy.d.ts +107 -0
- package/dist/types/src/data_proxy.d.ts.map +1 -0
- package/dist/types/src/data_utils.d.ts +3 -0
- package/dist/types/src/data_utils.d.ts.map +1 -0
- package/dist/types/src/element.d.ts +275 -0
- package/dist/types/src/element.d.ts.map +1 -0
- package/dist/types/src/element_iterator.d.ts +43 -0
- package/dist/types/src/element_iterator.d.ts.map +1 -0
- package/dist/types/src/event.d.ts +104 -0
- package/dist/types/src/event.d.ts.map +1 -0
- package/dist/types/src/external_shadow_tree.d.ts +20 -0
- package/dist/types/src/external_shadow_tree.d.ts.map +1 -0
- package/dist/types/src/func_arr.d.ts +39 -0
- package/dist/types/src/func_arr.d.ts.map +1 -0
- package/dist/types/src/global_options.d.ts +111 -0
- package/dist/types/src/global_options.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +43 -0
- package/dist/types/src/index.d.ts.map +1 -0
- package/dist/types/src/mutation_observer.d.ts +79 -0
- package/dist/types/src/mutation_observer.d.ts.map +1 -0
- package/dist/types/src/native_node.d.ts +8 -0
- package/dist/types/src/native_node.d.ts.map +1 -0
- package/dist/types/src/node.d.ts +49 -0
- package/dist/types/src/node.d.ts.map +1 -0
- package/dist/types/src/relation.d.ts +47 -0
- package/dist/types/src/relation.d.ts.map +1 -0
- package/dist/types/src/render.d.ts +3 -0
- package/dist/types/src/render.d.ts.map +1 -0
- package/dist/types/src/selector.d.ts +32 -0
- package/dist/types/src/selector.d.ts.map +1 -0
- package/dist/types/src/shadow_root.d.ts +136 -0
- package/dist/types/src/shadow_root.d.ts.map +1 -0
- package/dist/types/src/template_engine.d.ts +18 -0
- package/dist/types/src/template_engine.d.ts.map +1 -0
- package/dist/types/src/text_node.d.ts +32 -0
- package/dist/types/src/text_node.d.ts.map +1 -0
- package/dist/types/src/tmpl/index.d.ts +18 -0
- package/dist/types/src/tmpl/index.d.ts.map +1 -0
- package/dist/types/src/tmpl/native_rendering.d.ts +45 -0
- package/dist/types/src/tmpl/native_rendering.d.ts.map +1 -0
- package/dist/types/src/tmpl/proc_gen_wrapper.d.ts +80 -0
- package/dist/types/src/tmpl/proc_gen_wrapper.d.ts.map +1 -0
- package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts +50 -0
- package/dist/types/src/tmpl/proc_gen_wrapper_dom.d.ts.map +1 -0
- package/dist/types/src/tmpl/range_list_diff.d.ts +19 -0
- package/dist/types/src/tmpl/range_list_diff.d.ts.map +1 -0
- package/dist/types/src/trait_behaviors.d.ts +38 -0
- package/dist/types/src/trait_behaviors.d.ts.map +1 -0
- package/dist/types/src/virtual_node.d.ts +10 -0
- package/dist/types/src/virtual_node.d.ts.map +1 -0
- package/dist/types/tests/backend/domlike.test.d.ts +2 -0
- package/dist/types/tests/backend/domlike.test.d.ts.map +1 -0
- package/dist/types/tests/base/env.d.ts +29 -0
- package/dist/types/tests/base/env.d.ts.map +1 -0
- package/dist/types/tests/base/match.d.ts +9 -0
- package/dist/types/tests/base/match.d.ts.map +1 -0
- package/dist/types/tests/core/backend.test.d.ts +2 -0
- package/dist/types/tests/core/backend.test.d.ts.map +1 -0
- package/dist/types/tests/core/behavior.test.d.ts +2 -0
- package/dist/types/tests/core/behavior.test.d.ts.map +1 -0
- package/dist/types/tests/core/component_space.test.d.ts +2 -0
- package/dist/types/tests/core/component_space.test.d.ts.map +1 -0
- package/dist/types/tests/core/data_update.test.d.ts +2 -0
- package/dist/types/tests/core/data_update.test.d.ts.map +1 -0
- package/dist/types/tests/core/misc.test.d.ts +2 -0
- package/dist/types/tests/core/misc.test.d.ts.map +1 -0
- package/dist/types/tests/core/placeholder.test.d.ts +2 -0
- package/dist/types/tests/core/placeholder.test.d.ts.map +1 -0
- package/dist/types/tests/core/slot.test.d.ts +2 -0
- package/dist/types/tests/core/slot.test.d.ts.map +1 -0
- package/dist/types/tests/core/trait_behaviors.test.d.ts +2 -0
- package/dist/types/tests/core/trait_behaviors.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/binding_map.test.d.ts +2 -0
- package/dist/types/tests/tmpl/binding_map.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/event.test.d.ts +2 -0
- package/dist/types/tests/tmpl/event.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/expression.test.d.ts +2 -0
- package/dist/types/tests/tmpl/expression.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/lvalue.test.d.ts +2 -0
- package/dist/types/tests/tmpl/lvalue.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/native_rendering.test.d.ts +2 -0
- package/dist/types/tests/tmpl/native_rendering.test.d.ts.map +1 -0
- package/dist/types/tests/tmpl/structure.test.d.ts +2 -0
- package/dist/types/tests/tmpl/structure.test.d.ts.map +1 -0
- package/dist/types/tests/types/chaining.test.d.ts +2 -0
- package/dist/types/tests/types/chaining.test.d.ts.map +1 -0
- package/dist/types/tests/types/createElement.test.d.ts +2 -0
- package/dist/types/tests/types/createElement.test.d.ts.map +1 -0
- package/dist/types/tests/types/definition.test.d.ts +2 -0
- package/dist/types/tests/types/definition.test.d.ts.map +1 -0
- package/guide/zh_CN/advanced/binding_map_update.md +32 -0
- package/guide/zh_CN/advanced/build_args.md +28 -0
- package/guide/zh_CN/advanced/component_filter.md +70 -0
- package/guide/zh_CN/advanced/component_space.md +124 -0
- package/guide/zh_CN/advanced/custom_backend.md +53 -0
- package/guide/zh_CN/advanced/error_listener.md +32 -0
- package/guide/zh_CN/advanced/external_component.md +73 -0
- package/guide/zh_CN/advanced/template_engine.md +61 -0
- package/guide/zh_CN/appendix/backend_protocol.md +501 -0
- package/guide/zh_CN/appendix/list_diff_algorithm.md +406 -0
- package/guide/zh_CN/basic/beginning.md +94 -0
- package/guide/zh_CN/basic/component.md +156 -0
- package/guide/zh_CN/basic/event.md +169 -0
- package/guide/zh_CN/basic/lifetime.md +66 -0
- package/guide/zh_CN/basic/method.md +62 -0
- package/guide/zh_CN/basic/template.md +135 -0
- package/guide/zh_CN/data_management/advanced_update.md +170 -0
- package/guide/zh_CN/data_management/data_deep_copy.md +157 -0
- package/guide/zh_CN/data_management/data_observer.md +154 -0
- package/guide/zh_CN/data_management/property_early_init.md +31 -0
- package/guide/zh_CN/data_management/pure_data_pattern.md +21 -0
- package/guide/zh_CN/index.md +93 -0
- package/guide/zh_CN/interaction/behavior.md +52 -0
- package/guide/zh_CN/interaction/component_path.md +37 -0
- package/guide/zh_CN/interaction/generic.md +73 -0
- package/guide/zh_CN/interaction/placeholder.md +40 -0
- package/guide/zh_CN/interaction/relation.md +151 -0
- package/guide/zh_CN/interaction/slot.md +137 -0
- package/guide/zh_CN/interaction/template_import.md +94 -0
- package/guide/zh_CN/interaction/trait_behavior.md +117 -0
- package/guide/zh_CN/styling/external_class.md +46 -0
- package/guide/zh_CN/styling/style_isolation.md +54 -0
- package/guide/zh_CN/styling/virtual_host.md +52 -0
- package/guide/zh_CN/tree/element_iterator.md +54 -0
- package/guide/zh_CN/tree/mutation_observer.md +52 -0
- package/guide/zh_CN/tree/node_tree.md +142 -0
- package/guide/zh_CN/tree/node_tree_modification.md +78 -0
- package/guide/zh_CN/tree/selector.md +66 -0
- package/jest.config.js +6 -0
- package/jest.dts.config.js +9 -0
- package/jest.unit.config.js +14 -0
- package/package.json +28 -0
- package/src/backend/backend_protocol.ts +313 -0
- package/src/backend/composed_backend_protocol.ts +252 -0
- package/src/backend/domlike_backend_protocol.ts +370 -0
- package/src/backend/mode.ts +51 -0
- package/src/backend/suggested_backend_protocol.ts +83 -0
- package/src/behavior.ts +1655 -0
- package/src/bootstrap_dom_dev.js +22 -0
- package/src/class_list.ts +376 -0
- package/src/component.ts +1309 -0
- package/src/component_params.ts +461 -0
- package/src/component_space.ts +547 -0
- package/src/data_path.ts +225 -0
- package/src/data_proxy.ts +670 -0
- package/src/data_utils.ts +50 -0
- package/src/element.ts +1966 -0
- package/src/element_iterator.ts +158 -0
- package/src/event.ts +401 -0
- package/src/external_shadow_tree.ts +27 -0
- package/src/func_arr.ts +198 -0
- package/src/global_options.ts +242 -0
- package/src/index.ts +187 -0
- package/src/mutation_observer.ts +252 -0
- package/src/native_node.ts +74 -0
- package/src/node.ts +174 -0
- package/src/relation.ts +380 -0
- package/src/render.ts +25 -0
- package/src/selector.ts +218 -0
- package/src/shadow_root.ts +766 -0
- package/src/template_engine.ts +45 -0
- package/src/text_node.ts +149 -0
- package/src/tmpl/index.ts +199 -0
- package/src/tmpl/native_rendering.ts +175 -0
- package/src/tmpl/proc_gen_wrapper.ts +954 -0
- package/src/tmpl/proc_gen_wrapper_dom.ts +230 -0
- package/src/tmpl/range_list_diff.ts +443 -0
- package/src/trait_behaviors.ts +51 -0
- package/src/virtual_node.ts +51 -0
- package/tests/backend/domlike.test.ts +254 -0
- package/tests/base/env.ts +78 -0
- package/tests/base/match.ts +185 -0
- package/tests/core/backend.test.ts +144 -0
- package/tests/core/behavior.test.ts +546 -0
- package/tests/core/component_space.test.ts +212 -0
- package/tests/core/data_update.test.ts +461 -0
- package/tests/core/misc.test.ts +339 -0
- package/tests/core/placeholder.test.ts +180 -0
- package/tests/core/slot.test.ts +1495 -0
- package/tests/core/trait_behaviors.test.ts +153 -0
- package/tests/legacy/README.md +3 -0
- package/tests/legacy/behavior.test.js +293 -0
- package/tests/legacy/component.test.js +1247 -0
- package/tests/legacy/data_path.test.js +149 -0
- package/tests/legacy/data_proxy.test.js +759 -0
- package/tests/legacy/element_iterator.test.js +148 -0
- package/tests/legacy/event.test.js +849 -0
- package/tests/legacy/external.test.js +510 -0
- package/tests/legacy/extra_info.test.js +109 -0
- package/tests/legacy/generics.test.js +176 -0
- package/tests/legacy/mutation_observer.test.js +210 -0
- package/tests/legacy/relation.test.js +517 -0
- package/tests/legacy/selector.test.js +263 -0
- package/tests/legacy/slot.test.js +915 -0
- package/tests/legacy/virtual.test.js +394 -0
- package/tests/tmpl/binding_map.test.ts +208 -0
- package/tests/tmpl/event.test.ts +206 -0
- package/tests/tmpl/expression.test.ts +429 -0
- package/tests/tmpl/lvalue.test.ts +160 -0
- package/tests/tmpl/native_rendering.test.ts +155 -0
- package/tests/tmpl/structure.test.ts +998 -0
- package/tests/types/chaining.test.ts +614 -0
- package/tests/types/createElement.test.ts +82 -0
- package/tests/types/definition.test.ts +442 -0
- package/tsconfig.json +11 -0
- 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
|
+
}
|