@mk-drag-and-drop/dom 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/LICENSE +21 -0
- package/README.md +460 -0
- package/dist/controller/controller-internals.d.ts +5 -0
- package/dist/controller/controller-internals.js +11 -0
- package/dist/controller/create-drag-controller.d.ts +36 -0
- package/dist/controller/create-drag-controller.js +232 -0
- package/dist/draggable/create-draggable-binding.d.ts +8 -0
- package/dist/draggable/create-draggable-binding.js +52 -0
- package/dist/draggable/create-draggable.d.ts +46 -0
- package/dist/draggable/create-draggable.js +57 -0
- package/dist/droppable/create-drop-container-binding.d.ts +8 -0
- package/dist/droppable/create-drop-container-binding.js +28 -0
- package/dist/droppable/create-drop-container.d.ts +18 -0
- package/dist/droppable/create-drop-container.js +36 -0
- package/dist/droppable/create-droppable-binding.d.ts +9 -0
- package/dist/droppable/create-droppable-binding.js +29 -0
- package/dist/droppable/create-droppable.d.ts +17 -0
- package/dist/droppable/create-droppable.js +31 -0
- package/dist/geometry/measurement.d.ts +4 -0
- package/dist/geometry/measurement.js +33 -0
- package/dist/geometry/overlay.d.ts +19 -0
- package/dist/geometry/overlay.js +32 -0
- package/dist/geometry/rects.d.ts +17 -0
- package/dist/geometry/rects.js +34 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +9 -0
- package/dist/input/config.d.ts +35 -0
- package/dist/input/config.js +56 -0
- package/dist/input/create-drag-handle.d.ts +4 -0
- package/dist/input/create-drag-handle.js +4 -0
- package/dist/input/drag-handle.d.ts +10 -0
- package/dist/input/drag-handle.js +74 -0
- package/dist/input/keyboard-drag.d.ts +28 -0
- package/dist/input/keyboard-drag.js +77 -0
- package/dist/input/pointer-activation.d.ts +31 -0
- package/dist/input/pointer-activation.js +97 -0
- package/dist/integration/index.d.ts +10 -0
- package/dist/integration/index.js +6 -0
- package/dist/modifiers/built-ins.d.ts +6 -0
- package/dist/modifiers/built-ins.js +62 -0
- package/dist/modifiers/pipeline.d.ts +18 -0
- package/dist/modifiers/pipeline.js +34 -0
- package/dist/modifiers/types.d.ts +32 -0
- package/dist/modifiers/types.js +1 -0
- package/dist/runtime/drag-runtime-handle.d.ts +22 -0
- package/dist/runtime/drag-runtime-handle.js +33 -0
- package/dist/runtime/drag-runtime.d.ts +96 -0
- package/dist/runtime/drag-runtime.js +798 -0
- package/dist/runtime/drop-target-registry.d.ts +101 -0
- package/dist/runtime/drop-target-registry.js +855 -0
- package/dist/runtime/lifecycle.d.ts +44 -0
- package/dist/runtime/lifecycle.js +1 -0
- package/dist/runtime/types.d.ts +65 -0
- package/dist/runtime/types.js +1 -0
- package/dist/sortable/create-sortable-binding.d.ts +10 -0
- package/dist/sortable/create-sortable-binding.js +58 -0
- package/dist/sortable/create-sortable.d.ts +19 -0
- package/dist/sortable/create-sortable.js +55 -0
- package/dist/sortable/sortable-options.d.ts +17 -0
- package/dist/sortable/sortable-options.js +20 -0
- package/dist/sortable/sortable-preview.d.ts +79 -0
- package/dist/sortable/sortable-preview.js +357 -0
- package/dist/sortable/sortable-registry.d.ts +113 -0
- package/dist/sortable/sortable-registry.js +145 -0
- package/dist/targeting/algorithms.d.ts +6 -0
- package/dist/targeting/algorithms.js +56 -0
- package/dist/targeting/constraints.d.ts +15 -0
- package/dist/targeting/constraints.js +58 -0
- package/dist/targeting/types.d.ts +23 -0
- package/dist/targeting/types.js +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,855 @@
|
|
|
1
|
+
import { documentRectToViewportRect, measureDocumentRect, } from "../geometry/measurement.js";
|
|
2
|
+
import { emptyDragRect } from "../geometry/rects.js";
|
|
3
|
+
const sortableCandidateWindowRadius = 3;
|
|
4
|
+
export class DropTargetRegistry {
|
|
5
|
+
targetsByGroup = new Map();
|
|
6
|
+
targetElements = new WeakMap();
|
|
7
|
+
sortableAxisIndexes = new Map();
|
|
8
|
+
register(id, element, group, options = {}) {
|
|
9
|
+
const groupTargets = this.getOrCreateGroupTargets(group);
|
|
10
|
+
const previousElementTarget = this.targetElements.get(element);
|
|
11
|
+
const removedTargets = [];
|
|
12
|
+
if (previousElementTarget) {
|
|
13
|
+
const previousEntry = this.targetsByGroup
|
|
14
|
+
.get(previousElementTarget.group)
|
|
15
|
+
?.get(previousElementTarget.id);
|
|
16
|
+
const previousElement = previousEntry
|
|
17
|
+
? this.resolveEntry(previousEntry)
|
|
18
|
+
: null;
|
|
19
|
+
if (previousElement === element) {
|
|
20
|
+
if (previousElementTarget.group !== group ||
|
|
21
|
+
previousElementTarget.id !== id) {
|
|
22
|
+
removedTargets.push(...this.removeTarget(previousElementTarget.group, previousElementTarget.id));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.targetElements.delete(element);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const displacedEntry = groupTargets.get(id);
|
|
30
|
+
const displacedElement = displacedEntry
|
|
31
|
+
? this.resolveEntry(displacedEntry)
|
|
32
|
+
: null;
|
|
33
|
+
if (displacedElement && displacedElement !== element) {
|
|
34
|
+
this.targetElements.delete(displacedElement);
|
|
35
|
+
}
|
|
36
|
+
const entry = {
|
|
37
|
+
id,
|
|
38
|
+
elementRef: new WeakRef(element),
|
|
39
|
+
group,
|
|
40
|
+
containerId: options.containerId ?? null,
|
|
41
|
+
capabilities: {
|
|
42
|
+
container: options.container ?? false,
|
|
43
|
+
sortable: options.sortable ?? false,
|
|
44
|
+
},
|
|
45
|
+
sortableAxis: options.sortable ? options.sortableAxis ?? null : null,
|
|
46
|
+
documentRect: emptyDragRect,
|
|
47
|
+
};
|
|
48
|
+
groupTargets.set(id, entry);
|
|
49
|
+
this.targetElements.set(element, { group, id });
|
|
50
|
+
this.remeasureEntry(entry);
|
|
51
|
+
this.syncSortableAxisIndexAfterRegister(group, entry, element);
|
|
52
|
+
return removedTargets;
|
|
53
|
+
}
|
|
54
|
+
unregister(id, element) {
|
|
55
|
+
if (element) {
|
|
56
|
+
const target = this.targetElements.get(element);
|
|
57
|
+
if (!target || target.id !== id) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const entry = this.targetsByGroup.get(target.group)?.get(target.id);
|
|
61
|
+
if (!entry || entry.elementRef.deref() !== element) {
|
|
62
|
+
this.targetElements.delete(element);
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
this.targetElements.delete(element);
|
|
66
|
+
return this.removeTarget(target.group, target.id);
|
|
67
|
+
}
|
|
68
|
+
const removedTargets = [];
|
|
69
|
+
for (const group of Array.from(this.targetsByGroup.keys())) {
|
|
70
|
+
removedTargets.push(...this.removeTarget(group, id));
|
|
71
|
+
}
|
|
72
|
+
return removedTargets;
|
|
73
|
+
}
|
|
74
|
+
pruneDisconnected(group) {
|
|
75
|
+
const removedTargets = [];
|
|
76
|
+
if (group !== undefined) {
|
|
77
|
+
const groupTargets = this.targetsByGroup.get(group);
|
|
78
|
+
return groupTargets
|
|
79
|
+
? this.pruneDisconnectedGroup(group, groupTargets)
|
|
80
|
+
: removedTargets;
|
|
81
|
+
}
|
|
82
|
+
for (const [targetGroup, groupTargets] of this.targetsByGroup) {
|
|
83
|
+
removedTargets.push(...this.pruneDisconnectedGroup(targetGroup, groupTargets));
|
|
84
|
+
}
|
|
85
|
+
return removedTargets;
|
|
86
|
+
}
|
|
87
|
+
clear() {
|
|
88
|
+
this.targetsByGroup.clear();
|
|
89
|
+
this.targetElements = new WeakMap();
|
|
90
|
+
this.sortableAxisIndexes.clear();
|
|
91
|
+
}
|
|
92
|
+
remeasure(input) {
|
|
93
|
+
if (input === undefined) {
|
|
94
|
+
for (const groupTargets of this.targetsByGroup.values()) {
|
|
95
|
+
for (const target of groupTargets.values()) {
|
|
96
|
+
this.remeasureEntry(target);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
this.sortableAxisIndexes.clear();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (typeof input === "string") {
|
|
103
|
+
for (const group of this.remeasureTarget(input)) {
|
|
104
|
+
this.sortableAxisIndexes.delete(group);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(input)) {
|
|
109
|
+
const remeasuredGroups = new Set();
|
|
110
|
+
for (const dropTargetId of input) {
|
|
111
|
+
for (const group of this.remeasureTarget(dropTargetId)) {
|
|
112
|
+
remeasuredGroups.add(group);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
for (const group of remeasuredGroups) {
|
|
116
|
+
this.sortableAxisIndexes.delete(group);
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const groupTargets = this.targetsByGroup.get(input.group);
|
|
121
|
+
if (!groupTargets) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
for (const dropTarget of groupTargets.values()) {
|
|
125
|
+
this.remeasureEntry(dropTarget);
|
|
126
|
+
}
|
|
127
|
+
this.sortableAxisIndexes.delete(input.group);
|
|
128
|
+
}
|
|
129
|
+
getViewportRect(id, group) {
|
|
130
|
+
const registration = this.findRegistration(id, { group });
|
|
131
|
+
return registration
|
|
132
|
+
? documentRectToViewportRect(registration.documentRect)
|
|
133
|
+
: null;
|
|
134
|
+
}
|
|
135
|
+
getDropTargetRegistration(id, group) {
|
|
136
|
+
const registration = this.findRegistration(id, { group });
|
|
137
|
+
return registration ? toPublicRegistration(registration) : null;
|
|
138
|
+
}
|
|
139
|
+
getAvailableDropTargets(input) {
|
|
140
|
+
if (input.group === null) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
const groupTargets = this.targetsByGroup.get(input.group);
|
|
144
|
+
if (!groupTargets) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
const sortableCandidates = this.getNarrowedSortableDropTargetEntries(input, groupTargets);
|
|
148
|
+
if (sortableCandidates) {
|
|
149
|
+
const dropTargets = this.getAvailableDropTargetsFromEntries(sortableCandidates, input);
|
|
150
|
+
if (!input.targetingConstraint || dropTargets.length > 0) {
|
|
151
|
+
return dropTargets;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return this.getAvailableDropTargetsFromEntries(groupTargets.values(), input);
|
|
155
|
+
}
|
|
156
|
+
getAvailableDropTargetsFromEntries(entries, input) {
|
|
157
|
+
const candidates = [];
|
|
158
|
+
let hasContainerCandidate = false;
|
|
159
|
+
for (const target of entries) {
|
|
160
|
+
const element = this.resolveEntry(target);
|
|
161
|
+
if (!element) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (target.capabilities.container) {
|
|
165
|
+
hasContainerCandidate = true;
|
|
166
|
+
}
|
|
167
|
+
candidates.push({
|
|
168
|
+
entry: target,
|
|
169
|
+
element,
|
|
170
|
+
viewportRect: documentRectToViewportRect(target.documentRect),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const candidateParentElements = hasContainerCandidate
|
|
174
|
+
? getCandidateParentElements(candidates)
|
|
175
|
+
: null;
|
|
176
|
+
const dropTargets = [];
|
|
177
|
+
for (const candidate of candidates) {
|
|
178
|
+
if (candidate.entry.capabilities.container &&
|
|
179
|
+
candidateParentElements &&
|
|
180
|
+
!shouldIncludeContainerCandidate({
|
|
181
|
+
candidateParentElements,
|
|
182
|
+
containerCandidate: candidate,
|
|
183
|
+
pointerPosition: input.pointerPosition,
|
|
184
|
+
})) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const candidateDropTarget = {
|
|
188
|
+
dropTargetId: candidate.entry.id,
|
|
189
|
+
dropTargetRect: candidate.viewportRect,
|
|
190
|
+
};
|
|
191
|
+
if (input.targetingConstraint &&
|
|
192
|
+
!input.targetingConstraint({
|
|
193
|
+
pointerPosition: input.pointerPosition,
|
|
194
|
+
overlayRect: input.overlayRect,
|
|
195
|
+
dropTarget: candidateDropTarget,
|
|
196
|
+
})) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
dropTargets.push(candidateDropTarget);
|
|
200
|
+
}
|
|
201
|
+
return dropTargets;
|
|
202
|
+
}
|
|
203
|
+
getSortableDropPlacement(input) {
|
|
204
|
+
if (input.dropTargetId === null || input.group === null) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
const dropTarget = this.findRegistration(input.dropTargetId, {
|
|
208
|
+
group: input.group,
|
|
209
|
+
});
|
|
210
|
+
if (!dropTarget) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const itemRegistration = this.findRegistration(input.draggableId, {
|
|
214
|
+
group: input.group,
|
|
215
|
+
});
|
|
216
|
+
if (!itemRegistration?.capabilities.sortable) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
if (!dropTarget.capabilities.sortable &&
|
|
220
|
+
!dropTarget.capabilities.container) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const previewContainer = dropTarget.id === input.draggableId
|
|
224
|
+
? this.findContainerRegistrationForElement({
|
|
225
|
+
element: itemRegistration?.element.parentElement ?? null,
|
|
226
|
+
group: input.group,
|
|
227
|
+
})
|
|
228
|
+
: null;
|
|
229
|
+
const containerId = previewContainer?.containerId ?? dropTarget.containerId;
|
|
230
|
+
const containerElement = previewContainer?.element ?? getDropTargetContainerElement(dropTarget);
|
|
231
|
+
const siblingPlacement = this.getSiblingPlacement({
|
|
232
|
+
draggableId: input.draggableId,
|
|
233
|
+
group: input.group,
|
|
234
|
+
itemElement: itemRegistration.element,
|
|
235
|
+
dropTarget,
|
|
236
|
+
containerElement,
|
|
237
|
+
});
|
|
238
|
+
const targetPlacement = this.getTargetPlacement({
|
|
239
|
+
draggableId: input.draggableId,
|
|
240
|
+
group: input.group,
|
|
241
|
+
itemElement: itemRegistration.element,
|
|
242
|
+
dropTarget,
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
sourceContainerId: input.sourceContainerId,
|
|
246
|
+
containerId,
|
|
247
|
+
previousDraggableId: siblingPlacement.previousDraggableId,
|
|
248
|
+
nextDraggableId: siblingPlacement.nextDraggableId,
|
|
249
|
+
targetDraggableId: targetPlacement.targetDraggableId,
|
|
250
|
+
side: targetPlacement.side,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
getSortableItemPlacement(draggableId, group) {
|
|
254
|
+
const registration = this.findRegistration(draggableId, { group });
|
|
255
|
+
if (!registration?.capabilities.sortable ||
|
|
256
|
+
!registration.element.parentElement) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
const { element } = registration;
|
|
260
|
+
const itemGroup = registration.group;
|
|
261
|
+
const previousDraggableId = this.getNearestSortableSiblingDraggableId(element.previousElementSibling, itemGroup, "previous");
|
|
262
|
+
const nextDraggableId = this.getNearestSortableSiblingDraggableId(element.nextElementSibling, itemGroup, "next");
|
|
263
|
+
return {
|
|
264
|
+
containerId: registration.containerId,
|
|
265
|
+
previousDraggableId,
|
|
266
|
+
nextDraggableId,
|
|
267
|
+
exactAnchors: this.getExactSortableItemAnchors(element, itemGroup),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
getOrCreateGroupTargets(group) {
|
|
271
|
+
let groupTargets = this.targetsByGroup.get(group);
|
|
272
|
+
if (!groupTargets) {
|
|
273
|
+
groupTargets = new Map();
|
|
274
|
+
this.targetsByGroup.set(group, groupTargets);
|
|
275
|
+
}
|
|
276
|
+
return groupTargets;
|
|
277
|
+
}
|
|
278
|
+
removeTarget(group, id) {
|
|
279
|
+
const groupTargets = this.targetsByGroup.get(group);
|
|
280
|
+
if (!groupTargets?.delete(id)) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
this.sortableAxisIndexes.delete(group);
|
|
284
|
+
if (groupTargets.size === 0) {
|
|
285
|
+
this.targetsByGroup.delete(group);
|
|
286
|
+
}
|
|
287
|
+
return [{ id, group }];
|
|
288
|
+
}
|
|
289
|
+
pruneDisconnectedGroup(group, groupTargets) {
|
|
290
|
+
const removedTargets = [];
|
|
291
|
+
for (const [id, entry] of groupTargets) {
|
|
292
|
+
if (this.resolveEntry(entry)) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
groupTargets.delete(id);
|
|
296
|
+
removedTargets.push({ id, group });
|
|
297
|
+
}
|
|
298
|
+
if (removedTargets.length > 0) {
|
|
299
|
+
this.sortableAxisIndexes.delete(group);
|
|
300
|
+
}
|
|
301
|
+
if (groupTargets.size === 0) {
|
|
302
|
+
this.targetsByGroup.delete(group);
|
|
303
|
+
}
|
|
304
|
+
return removedTargets;
|
|
305
|
+
}
|
|
306
|
+
remeasureTarget(dropTargetId) {
|
|
307
|
+
const remeasuredGroups = [];
|
|
308
|
+
for (const groupTargets of this.targetsByGroup.values()) {
|
|
309
|
+
const dropTarget = groupTargets.get(dropTargetId);
|
|
310
|
+
if (dropTarget) {
|
|
311
|
+
this.remeasureEntry(dropTarget);
|
|
312
|
+
remeasuredGroups.push(dropTarget.group);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return remeasuredGroups;
|
|
316
|
+
}
|
|
317
|
+
remeasureEntry(registration) {
|
|
318
|
+
const element = this.resolveEntry(registration);
|
|
319
|
+
if (!element) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
registration.documentRect = measureDocumentRect(element);
|
|
323
|
+
}
|
|
324
|
+
findRegistration(id, input = {}) {
|
|
325
|
+
const entry = this.findEntry(id, input);
|
|
326
|
+
return entry ? this.resolveRegistration(entry) : null;
|
|
327
|
+
}
|
|
328
|
+
findEntry(id, input = {}) {
|
|
329
|
+
if (input.group !== undefined) {
|
|
330
|
+
return (input.group === null
|
|
331
|
+
? null
|
|
332
|
+
: this.targetsByGroup.get(input.group)?.get(id) ?? null);
|
|
333
|
+
}
|
|
334
|
+
for (const groupTargets of this.targetsByGroup.values()) {
|
|
335
|
+
const entry = groupTargets.get(id);
|
|
336
|
+
if (entry && this.resolveEntry(entry)) {
|
|
337
|
+
return entry;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
resolveRegistration(entry) {
|
|
343
|
+
const element = this.resolveEntry(entry);
|
|
344
|
+
return element
|
|
345
|
+
? {
|
|
346
|
+
...entry,
|
|
347
|
+
element,
|
|
348
|
+
}
|
|
349
|
+
: null;
|
|
350
|
+
}
|
|
351
|
+
resolveEntry(entry) {
|
|
352
|
+
const element = entry.elementRef.deref();
|
|
353
|
+
return element?.isConnected ? element : null;
|
|
354
|
+
}
|
|
355
|
+
getNarrowedSortableDropTargetEntries(input, groupTargets) {
|
|
356
|
+
if (input.group === null ||
|
|
357
|
+
!input.targetingAlgorithmKind ||
|
|
358
|
+
!input.draggingDraggableId) {
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
const index = this.getSortableAxisIndex(input.group, groupTargets);
|
|
362
|
+
if (!index || index.unsupported) {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
if (!index.candidatesById.has(input.draggingDraggableId)) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
const axisCoordinate = getSortableQueryAxisCoordinate({
|
|
369
|
+
axis: index.axis,
|
|
370
|
+
overlayRect: input.overlayRect,
|
|
371
|
+
pointerPosition: input.pointerPosition,
|
|
372
|
+
targetingAlgorithmKind: input.targetingAlgorithmKind,
|
|
373
|
+
});
|
|
374
|
+
if (axisCoordinate === null) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const candidates = new Map();
|
|
378
|
+
const addCandidate = (candidate) => {
|
|
379
|
+
if (candidate) {
|
|
380
|
+
candidates.set(candidate.id, candidate);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
const nearestIndex = getNearestSortableAxisIndex(index.candidatesByCenter, axisCoordinate);
|
|
384
|
+
if (nearestIndex !== null) {
|
|
385
|
+
for (let candidateIndex = Math.max(0, nearestIndex - sortableCandidateWindowRadius); candidateIndex <=
|
|
386
|
+
Math.min(index.candidatesByCenter.length - 1, nearestIndex + sortableCandidateWindowRadius); candidateIndex += 1) {
|
|
387
|
+
addCandidate(index.candidatesByCenter[candidateIndex]);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
addCandidate(findSortableAxisCandidateContainingCoordinate(index.candidatesByStart, axisCoordinate));
|
|
391
|
+
addCandidate(index.candidatesById.get(input.draggingDraggableId));
|
|
392
|
+
addCandidate(input.activeDropTargetId
|
|
393
|
+
? index.candidatesById.get(input.activeDropTargetId)
|
|
394
|
+
: undefined);
|
|
395
|
+
addCandidate(input.sourceContainerId
|
|
396
|
+
? index.candidatesById.get(input.sourceContainerId)
|
|
397
|
+
: undefined);
|
|
398
|
+
this.addSortableDomNeighborCandidates({
|
|
399
|
+
candidates,
|
|
400
|
+
group: input.group,
|
|
401
|
+
index,
|
|
402
|
+
targetId: input.draggingDraggableId,
|
|
403
|
+
});
|
|
404
|
+
if (input.activeDropTargetId) {
|
|
405
|
+
this.addSortableDomNeighborCandidates({
|
|
406
|
+
candidates,
|
|
407
|
+
group: input.group,
|
|
408
|
+
index,
|
|
409
|
+
targetId: input.activeDropTargetId,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return Array.from(candidates.values(), (candidate) => candidate.entry);
|
|
413
|
+
}
|
|
414
|
+
getSortableAxisIndex(group, groupTargets) {
|
|
415
|
+
const existingIndex = this.sortableAxisIndexes.get(group);
|
|
416
|
+
if (existingIndex) {
|
|
417
|
+
return existingIndex;
|
|
418
|
+
}
|
|
419
|
+
const index = this.createSortableAxisIndex(groupTargets);
|
|
420
|
+
if (index) {
|
|
421
|
+
this.sortableAxisIndexes.set(group, index);
|
|
422
|
+
}
|
|
423
|
+
return index;
|
|
424
|
+
}
|
|
425
|
+
createSortableAxisIndex(groupTargets) {
|
|
426
|
+
let axis = null;
|
|
427
|
+
let containerId;
|
|
428
|
+
const candidates = [];
|
|
429
|
+
for (const entry of groupTargets.values()) {
|
|
430
|
+
const element = this.resolveEntry(entry);
|
|
431
|
+
if (!element) {
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (entry.capabilities.container ||
|
|
435
|
+
!entry.capabilities.sortable ||
|
|
436
|
+
entry.sortableAxis === null) {
|
|
437
|
+
return createUnsupportedSortableAxisIndex();
|
|
438
|
+
}
|
|
439
|
+
if (axis === null) {
|
|
440
|
+
axis = entry.sortableAxis;
|
|
441
|
+
}
|
|
442
|
+
else if (axis !== entry.sortableAxis) {
|
|
443
|
+
return createUnsupportedSortableAxisIndex();
|
|
444
|
+
}
|
|
445
|
+
if (containerId === undefined) {
|
|
446
|
+
containerId = entry.containerId;
|
|
447
|
+
}
|
|
448
|
+
else if (containerId !== entry.containerId) {
|
|
449
|
+
return createUnsupportedSortableAxisIndex();
|
|
450
|
+
}
|
|
451
|
+
candidates.push(createSortableAxisCandidate(entry, element, entry.sortableAxis));
|
|
452
|
+
}
|
|
453
|
+
if (axis === null || candidates.length === 0) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
const candidatesByCenter = [...candidates].sort(compareAxisCenter);
|
|
457
|
+
const candidatesByStart = [...candidates].sort(compareAxisStart);
|
|
458
|
+
const index = {
|
|
459
|
+
axis,
|
|
460
|
+
containerId: containerId ?? null,
|
|
461
|
+
candidatesByCenter,
|
|
462
|
+
candidatesByStart,
|
|
463
|
+
candidatesById: new Map(candidates.map((candidate) => [candidate.id, candidate])),
|
|
464
|
+
unsupported: hasOverlappingSortableAxisRanges(candidatesByStart),
|
|
465
|
+
};
|
|
466
|
+
return index;
|
|
467
|
+
}
|
|
468
|
+
syncSortableAxisIndexAfterRegister(group, entry, element) {
|
|
469
|
+
const index = this.sortableAxisIndexes.get(group);
|
|
470
|
+
if (!index) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
if (index.unsupported ||
|
|
474
|
+
entry.capabilities.container ||
|
|
475
|
+
!entry.capabilities.sortable ||
|
|
476
|
+
entry.sortableAxis !== index.axis ||
|
|
477
|
+
entry.containerId !== index.containerId) {
|
|
478
|
+
this.sortableAxisIndexes.delete(group);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const candidate = index.candidatesById.get(entry.id);
|
|
482
|
+
if (!candidate) {
|
|
483
|
+
this.sortableAxisIndexes.delete(group);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const updatedCandidate = createSortableAxisCandidate(entry, element, index.axis);
|
|
487
|
+
candidate.entry = updatedCandidate.entry;
|
|
488
|
+
candidate.element = updatedCandidate.element;
|
|
489
|
+
candidate.containerId = updatedCandidate.containerId;
|
|
490
|
+
candidate.axisStart = updatedCandidate.axisStart;
|
|
491
|
+
candidate.axisEnd = updatedCandidate.axisEnd;
|
|
492
|
+
candidate.axisCenter = updatedCandidate.axisCenter;
|
|
493
|
+
}
|
|
494
|
+
addSortableDomNeighborCandidates(input) {
|
|
495
|
+
const registration = this.findRegistration(input.targetId, {
|
|
496
|
+
group: input.group,
|
|
497
|
+
});
|
|
498
|
+
if (!registration) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const previousDraggableId = this.getNearestSortableSiblingDraggableId(registration.element.previousElementSibling, input.group, "previous");
|
|
502
|
+
const nextDraggableId = this.getNearestSortableSiblingDraggableId(registration.element.nextElementSibling, input.group, "next");
|
|
503
|
+
if (previousDraggableId) {
|
|
504
|
+
const previousCandidate = input.index.candidatesById.get(previousDraggableId);
|
|
505
|
+
if (previousCandidate) {
|
|
506
|
+
input.candidates.set(previousDraggableId, previousCandidate);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
if (nextDraggableId) {
|
|
510
|
+
const nextCandidate = input.index.candidatesById.get(nextDraggableId);
|
|
511
|
+
if (nextCandidate) {
|
|
512
|
+
input.candidates.set(nextDraggableId, nextCandidate);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
findContainerRegistrationForElement(input) {
|
|
517
|
+
if (!input.element) {
|
|
518
|
+
return null;
|
|
519
|
+
}
|
|
520
|
+
const groupTargets = this.targetsByGroup.get(input.group);
|
|
521
|
+
if (!groupTargets) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
for (const entry of groupTargets.values()) {
|
|
525
|
+
const element = this.resolveEntry(entry);
|
|
526
|
+
if (entry.capabilities.container &&
|
|
527
|
+
element &&
|
|
528
|
+
element === input.element) {
|
|
529
|
+
return {
|
|
530
|
+
...entry,
|
|
531
|
+
element,
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return null;
|
|
536
|
+
}
|
|
537
|
+
getSiblingPlacement(input) {
|
|
538
|
+
if (input.itemElement.parentElement &&
|
|
539
|
+
input.itemElement.parentElement === input.containerElement) {
|
|
540
|
+
return {
|
|
541
|
+
previousDraggableId: this.getNearestSortableSiblingDraggableId(input.itemElement.previousElementSibling, input.group, "previous", input.draggableId),
|
|
542
|
+
nextDraggableId: this.getNearestSortableSiblingDraggableId(input.itemElement.nextElementSibling, input.group, "next", input.draggableId),
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
if (input.dropTarget.capabilities.container) {
|
|
546
|
+
return {
|
|
547
|
+
previousDraggableId: this.getNearestSortableSiblingDraggableId(input.dropTarget.element.lastElementChild, input.group, "previous", input.draggableId),
|
|
548
|
+
nextDraggableId: null,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
previousDraggableId: this.getNearestSortableSiblingDraggableId(input.dropTarget.element.previousElementSibling, input.group, "previous", input.draggableId),
|
|
553
|
+
nextDraggableId: input.dropTarget.id,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
getTargetPlacement(input) {
|
|
557
|
+
if (!input.dropTarget.capabilities.sortable) {
|
|
558
|
+
return {
|
|
559
|
+
targetDraggableId: null,
|
|
560
|
+
side: null,
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
if (input.dropTarget.id === input.draggableId) {
|
|
564
|
+
return this.getPreviewDomTargetPlacement({
|
|
565
|
+
draggableId: input.draggableId,
|
|
566
|
+
group: input.group,
|
|
567
|
+
itemElement: input.itemElement,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
targetDraggableId: input.dropTarget.id,
|
|
572
|
+
side: getSortableTargetSide({
|
|
573
|
+
itemElement: input.itemElement,
|
|
574
|
+
targetElement: input.dropTarget.element,
|
|
575
|
+
}),
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
getExactSortableItemAnchors(element, group) {
|
|
579
|
+
const anchors = [];
|
|
580
|
+
const previousDraggableId = this.getSortableDraggableId(element.previousElementSibling, group);
|
|
581
|
+
const nextDraggableId = this.getSortableDraggableId(element.nextElementSibling, group);
|
|
582
|
+
if (previousDraggableId) {
|
|
583
|
+
anchors.push({
|
|
584
|
+
targetDraggableId: previousDraggableId,
|
|
585
|
+
side: "after",
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
if (nextDraggableId) {
|
|
589
|
+
anchors.push({
|
|
590
|
+
targetDraggableId: nextDraggableId,
|
|
591
|
+
side: "before",
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return anchors;
|
|
595
|
+
}
|
|
596
|
+
getPreviewDomTargetPlacement(input) {
|
|
597
|
+
const previousElement = input.itemElement.previousElementSibling;
|
|
598
|
+
const nextElement = input.itemElement.nextElementSibling;
|
|
599
|
+
const previousDraggableId = this.getSortableDraggableId(previousElement, input.group);
|
|
600
|
+
const nextDraggableId = this.getSortableDraggableId(nextElement, input.group);
|
|
601
|
+
if (this.isSkippedSortableSibling(previousElement, input.group) &&
|
|
602
|
+
nextDraggableId &&
|
|
603
|
+
nextDraggableId !== input.draggableId) {
|
|
604
|
+
return {
|
|
605
|
+
targetDraggableId: nextDraggableId,
|
|
606
|
+
side: "before",
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
if (this.isSkippedSortableSibling(nextElement, input.group) &&
|
|
610
|
+
previousDraggableId &&
|
|
611
|
+
previousDraggableId !== input.draggableId) {
|
|
612
|
+
return {
|
|
613
|
+
targetDraggableId: previousDraggableId,
|
|
614
|
+
side: "after",
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
if (previousDraggableId && previousDraggableId !== input.draggableId) {
|
|
618
|
+
return {
|
|
619
|
+
targetDraggableId: previousDraggableId,
|
|
620
|
+
side: "after",
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
if (nextDraggableId && nextDraggableId !== input.draggableId) {
|
|
624
|
+
return {
|
|
625
|
+
targetDraggableId: nextDraggableId,
|
|
626
|
+
side: "before",
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
const previousNearestDraggableId = this.getNearestSortableSiblingDraggableId(previousElement, input.group, "previous", input.draggableId);
|
|
630
|
+
if (previousNearestDraggableId) {
|
|
631
|
+
return {
|
|
632
|
+
targetDraggableId: previousNearestDraggableId,
|
|
633
|
+
side: "after",
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const nextNearestDraggableId = this.getNearestSortableSiblingDraggableId(nextElement, input.group, "next", input.draggableId);
|
|
637
|
+
if (nextNearestDraggableId) {
|
|
638
|
+
return {
|
|
639
|
+
targetDraggableId: nextNearestDraggableId,
|
|
640
|
+
side: "before",
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
targetDraggableId: null,
|
|
645
|
+
side: null,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
isSkippedSortableSibling(element, group) {
|
|
649
|
+
return (element instanceof HTMLElement &&
|
|
650
|
+
element.dataset.dndSortableDraggable !== undefined &&
|
|
651
|
+
this.getSortableDraggableId(element, group) === null);
|
|
652
|
+
}
|
|
653
|
+
getNearestSortableSiblingDraggableId(element, group, direction, excludeDraggableId) {
|
|
654
|
+
let currentElement = element;
|
|
655
|
+
while (currentElement) {
|
|
656
|
+
const draggableId = this.getSortableDraggableId(currentElement, group);
|
|
657
|
+
if (draggableId && draggableId !== excludeDraggableId) {
|
|
658
|
+
return draggableId;
|
|
659
|
+
}
|
|
660
|
+
currentElement =
|
|
661
|
+
direction === "previous"
|
|
662
|
+
? currentElement.previousElementSibling
|
|
663
|
+
: currentElement.nextElementSibling;
|
|
664
|
+
}
|
|
665
|
+
return null;
|
|
666
|
+
}
|
|
667
|
+
getSortableDraggableId(element, group) {
|
|
668
|
+
if (!(element instanceof HTMLElement)) {
|
|
669
|
+
return null;
|
|
670
|
+
}
|
|
671
|
+
const target = this.targetElements.get(element);
|
|
672
|
+
const entry = target
|
|
673
|
+
? this.targetsByGroup.get(group)?.get(target.id) ?? null
|
|
674
|
+
: null;
|
|
675
|
+
if (!entry?.capabilities.sortable || entry.group !== group) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
return this.resolveEntry(entry) === element ? entry.id : null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function toPublicRegistration(registration) {
|
|
682
|
+
return {
|
|
683
|
+
id: registration.id,
|
|
684
|
+
element: registration.element,
|
|
685
|
+
group: registration.group,
|
|
686
|
+
containerId: registration.containerId,
|
|
687
|
+
capabilities: registration.capabilities,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
function getDropTargetContainerElement(dropTarget) {
|
|
691
|
+
return dropTarget.capabilities.container
|
|
692
|
+
? dropTarget.element
|
|
693
|
+
: dropTarget.element.parentElement;
|
|
694
|
+
}
|
|
695
|
+
function getSortableTargetSide(input) {
|
|
696
|
+
if (input.itemElement === input.targetElement ||
|
|
697
|
+
input.itemElement.parentElement !== input.targetElement.parentElement) {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
const position = input.targetElement.compareDocumentPosition(input.itemElement);
|
|
701
|
+
if ((position & Node.DOCUMENT_POSITION_FOLLOWING) !== 0) {
|
|
702
|
+
return "after";
|
|
703
|
+
}
|
|
704
|
+
if ((position & Node.DOCUMENT_POSITION_PRECEDING) !== 0) {
|
|
705
|
+
return "before";
|
|
706
|
+
}
|
|
707
|
+
return null;
|
|
708
|
+
}
|
|
709
|
+
function shouldIncludeContainerCandidate(input) {
|
|
710
|
+
if (!isPointInsideRect(input.pointerPosition, input.containerCandidate.viewportRect)) {
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
return !input.candidateParentElements.has(input.containerCandidate.element);
|
|
714
|
+
}
|
|
715
|
+
function getCandidateParentElements(candidates) {
|
|
716
|
+
const parentElements = new Set();
|
|
717
|
+
for (const candidate of candidates) {
|
|
718
|
+
const parentElement = candidate.element.parentElement;
|
|
719
|
+
if (parentElement) {
|
|
720
|
+
parentElements.add(parentElement);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return parentElements;
|
|
724
|
+
}
|
|
725
|
+
function createUnsupportedSortableAxisIndex() {
|
|
726
|
+
return {
|
|
727
|
+
axis: "vertical",
|
|
728
|
+
containerId: null,
|
|
729
|
+
candidatesByCenter: [],
|
|
730
|
+
candidatesByStart: [],
|
|
731
|
+
candidatesById: new Map(),
|
|
732
|
+
unsupported: true,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function createSortableAxisCandidate(entry, element, axis) {
|
|
736
|
+
const coordinates = getSortableAxisCoordinates(entry.documentRect, axis);
|
|
737
|
+
return {
|
|
738
|
+
id: entry.id,
|
|
739
|
+
entry,
|
|
740
|
+
element,
|
|
741
|
+
containerId: entry.containerId,
|
|
742
|
+
axisStart: coordinates.start,
|
|
743
|
+
axisEnd: coordinates.end,
|
|
744
|
+
axisCenter: coordinates.center,
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
function getSortableAxisCoordinates(rect, axis) {
|
|
748
|
+
if (axis === "horizontal") {
|
|
749
|
+
return {
|
|
750
|
+
start: rect.left,
|
|
751
|
+
end: rect.right,
|
|
752
|
+
center: rect.left + rect.width / 2,
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
return {
|
|
756
|
+
start: rect.top,
|
|
757
|
+
end: rect.bottom,
|
|
758
|
+
center: rect.top + rect.height / 2,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
function getSortableQueryAxisCoordinate(input) {
|
|
762
|
+
if (input.targetingAlgorithmKind === "center-to-center") {
|
|
763
|
+
if (!input.overlayRect) {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
const coordinates = getSortableAxisCoordinates(input.overlayRect, input.axis);
|
|
767
|
+
return coordinates.center + getDocumentScrollOffset(input.axis);
|
|
768
|
+
}
|
|
769
|
+
return ((input.axis === "horizontal"
|
|
770
|
+
? input.pointerPosition.x
|
|
771
|
+
: input.pointerPosition.y) + getDocumentScrollOffset(input.axis));
|
|
772
|
+
}
|
|
773
|
+
function getDocumentScrollOffset(axis) {
|
|
774
|
+
return axis === "horizontal" ? window.scrollX : window.scrollY;
|
|
775
|
+
}
|
|
776
|
+
function getNearestSortableAxisIndex(candidates, axisCoordinate) {
|
|
777
|
+
if (candidates.length === 0) {
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
let low = 0;
|
|
781
|
+
let high = candidates.length - 1;
|
|
782
|
+
while (low <= high) {
|
|
783
|
+
const mid = Math.floor((low + high) / 2);
|
|
784
|
+
const candidate = candidates[mid];
|
|
785
|
+
if (!candidate) {
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
if (candidate.axisCenter < axisCoordinate) {
|
|
789
|
+
low = mid + 1;
|
|
790
|
+
}
|
|
791
|
+
else {
|
|
792
|
+
high = mid - 1;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
if (low <= 0) {
|
|
796
|
+
return 0;
|
|
797
|
+
}
|
|
798
|
+
if (low >= candidates.length) {
|
|
799
|
+
return candidates.length - 1;
|
|
800
|
+
}
|
|
801
|
+
const previous = candidates[low - 1];
|
|
802
|
+
const next = candidates[low];
|
|
803
|
+
if (!previous || !next) {
|
|
804
|
+
return low;
|
|
805
|
+
}
|
|
806
|
+
return axisCoordinate - previous.axisCenter <= next.axisCenter - axisCoordinate
|
|
807
|
+
? low - 1
|
|
808
|
+
: low;
|
|
809
|
+
}
|
|
810
|
+
function findSortableAxisCandidateContainingCoordinate(candidatesByStart, axisCoordinate) {
|
|
811
|
+
let low = 0;
|
|
812
|
+
let high = candidatesByStart.length - 1;
|
|
813
|
+
let lastStartBeforeCoordinate = -1;
|
|
814
|
+
while (low <= high) {
|
|
815
|
+
const mid = Math.floor((low + high) / 2);
|
|
816
|
+
const candidate = candidatesByStart[mid];
|
|
817
|
+
if (!candidate) {
|
|
818
|
+
return undefined;
|
|
819
|
+
}
|
|
820
|
+
if (candidate.axisStart <= axisCoordinate) {
|
|
821
|
+
lastStartBeforeCoordinate = mid;
|
|
822
|
+
low = mid + 1;
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
high = mid - 1;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (lastStartBeforeCoordinate === -1) {
|
|
829
|
+
return undefined;
|
|
830
|
+
}
|
|
831
|
+
const candidate = candidatesByStart[lastStartBeforeCoordinate];
|
|
832
|
+
return candidate && candidate.axisEnd >= axisCoordinate ? candidate : undefined;
|
|
833
|
+
}
|
|
834
|
+
function hasOverlappingSortableAxisRanges(candidatesByStart) {
|
|
835
|
+
let previousAxisEnd = Number.NEGATIVE_INFINITY;
|
|
836
|
+
for (const candidate of candidatesByStart) {
|
|
837
|
+
if (candidate.axisStart < previousAxisEnd) {
|
|
838
|
+
return true;
|
|
839
|
+
}
|
|
840
|
+
previousAxisEnd = Math.max(previousAxisEnd, candidate.axisEnd);
|
|
841
|
+
}
|
|
842
|
+
return false;
|
|
843
|
+
}
|
|
844
|
+
function compareAxisCenter(a, b) {
|
|
845
|
+
return a.axisCenter - b.axisCenter;
|
|
846
|
+
}
|
|
847
|
+
function compareAxisStart(a, b) {
|
|
848
|
+
return a.axisStart - b.axisStart;
|
|
849
|
+
}
|
|
850
|
+
function isPointInsideRect(point, rect) {
|
|
851
|
+
return (point.x >= rect.left &&
|
|
852
|
+
point.x <= rect.right &&
|
|
853
|
+
point.y >= rect.top &&
|
|
854
|
+
point.y <= rect.bottom);
|
|
855
|
+
}
|