PageSourceSearch

https://gomontag.com/_content/BlazorSortable/Sortable.a9fgxq99xm.razor.js

js gomontag.com collected 2026-09-25 14:51:56 UTC 9,378 bytes, 349 lines download raw bytes

1const sortableJsPath = "_content/BlazorSortable/Sortable.min.js";
2const sortableJsVersion = "1.15.7";
3let sortableJsLoadPromise = null;
4
5const stylesheetPath = "_content/BlazorSortable/blazor-sortable.css";
6const highlightStylesheetPath = "_content/BlazorSortable/blazor-sortable-highlights.css";
7const injectedStylesheets = new Set();
8
9export async function initSortable(id, options, component, assetOptions) {
10    const el = document.getElementById(id);
11    if (!el) {
12        return;
13    }
14
15    if (assetOptions.autoLoadSortableJs) {
16        await ensureSortableJs();
17    } else {
18        ensureSortableJsAvailable();
19    }
20
21    if (assetOptions.autoLoadStylesheet) {
22        ensureStylesheet(stylesheetPath, assetOptions.version);
23
24        if (assetOptions.autoLoadHighlightStylesheet) {
25            ensureStylesheet(highlightStylesheetPath, assetOptions.version);
26        }
27    }
28
29    configureTransferCallbacks(options.group, component);
30
31    const sortable = new Sortable(el, buildSortableOptions(options, component));
32    el.blazorSortableCleanup = configureTouchAwareMultiDragKey(el, sortable, options);
33}
34
35export function destroySortable(id) {
36    const el = document.getElementById(id);
37    const sortable = el ? Sortable.get(el) : null;
38
39    if (!sortable) {
40        return;
41    }
42
43    el.blazorSortableCleanup?.();
44    delete el.blazorSortableCleanup;
45
46    sortable.destroy();
47}
48
49function configureTransferCallbacks(group, component) {
50    if (group.pull === "function") {
51        group.pull = (to) => component.invokeMethod("OnPullJs", to.el.id);
52    }
53
54    if (group.put === "function") {
55        group.put = (_to, from) => component.invokeMethod("OnPutJs", from.el.id);
56    }
57}
58
59function configureTouchAwareMultiDragKey(el, sortable, options) {
60    if (!options.multiDragKey) {
61        return null;
62    }
63
64    const updateMultiDragKey = evt => {
65        const isTouchInput = evt.pointerType === "touch" || evt.type === "touchstart";
66        sortable.option("multiDragKey", isTouchInput ? "" : options.multiDragKey);
67    };
68
69    if (sortable.options.supportPointer) {
70        el.addEventListener("pointerdown", updateMultiDragKey, true);
71        return () => el.removeEventListener("pointerdown", updateMultiDragKey, true);
72    }
73
74    el.addEventListener("mousedown", updateMultiDragKey, true);
75    el.addEventListener("touchstart", updateMultiDragKey, true);
76
77    return () => {
78        el.removeEventListener("mousedown", updateMultiDragKey, true);
79        el.removeEventListener("touchstart", updateMultiDragKey, true);
80    };
81}
82
83function buildSortableOptions(options, component) {
84    return {
85        ...options,
86        onStart: (evt) => {
87            component.invokeMethodAsync(
88                "OnStartJs",
89                evt.oldIndex,
90                getIndexes(evt.oldIndicies));
91        },
92        onEnd: (evt) => {
93            component.invokeMethodAsync("OnEndJs");
94
95            resetCrossSortableMultiDragSelection(evt);
96        },
97        onUpdate: (evt) => {
98            const isSwap = Boolean(evt.swapItem);
99
100            revertDomMove(evt, isSwap);
101
102            const newIndex =
103                evt.to.children.length === 1 && evt.newIndex === 1
104                    ? 0
105                    : evt.newIndex;
106
107            component.invokeMethodAsync(
108                "OnUpdateJs",
109                evt.oldIndex,
110                getIndexes(evt.oldIndicies),
111                newIndex,
112                getIndexes(evt.newIndicies),
113                isSwap);
114        },
115        onAdd: (evt) => {
116            const isSwap = Boolean(evt.swapItem) && options.swap === true;
117
118            revertDomMove(evt, isSwap);
119
120            const isClone = isCloneMode(evt);
121
122            if (isClone) {
123                removeClone(evt);
124            }
125
126            component.invokeMethodAsync(
127                "OnAddJs",
128                evt.from.id,
129                evt.oldIndex,
130                getIndexes(evt.oldIndicies),
131                evt.newIndex,
132                getIndexes(evt.newIndicies),
133                isClone,
134                isSwap);
135        },
136        onRemove: (evt) => {
137            revertDomMove(evt, false);
138
139            const isClone = isCloneMode(evt);
140
141            if (isClone) {
142                removeClone(evt);
143            }
144
145            component.invokeMethodAsync(
146                "OnRemoveJs",
147                evt.oldIndex,
148                getIndexes(evt.oldIndicies),
149                evt.to.id,
150                evt.newIndex,
151                getIndexes(evt.newIndicies),
152                isClone);
153        },
154        onSelect: (evt) => {
155            keepSelectionWithinCurrentSortable(evt);
156
157            component.invokeMethodAsync(
158                "OnSelectJs",
159                getElementIndex(evt.item),
160                getElementIndexes(evt.items));
161        },
162        onDeselect: (evt) => {
163            component.invokeMethodAsync(
164                "OnDeselectJs",
165                getElementIndex(evt.item),
166                getElementIndexes(evt.items));
167        },
168        onSpill: (evt) => {
169            revertDomMove(evt, false);
170
171            const isClone = isCloneMode(evt);
172
173            if (isClone) {
174                removeClone(evt);
175            }
176
177            component.invokeMethodAsync(
178                "OnSpillJs",
179                getElementIndex(evt.item),
180                getElementIndexes(evt.items),
181                isClone);
182        }
183    };
184}
185
186function revertDomMove(evt, isSwap) {
187    const pairs = getMovedElementPairs(evt);
188
189    for (const pair of pairs) {
190        pair.element.remove();
191    }
192
193    if (isSwap) {
194        evt.swapItem.remove();
195    }
196
197    for (const pair of pairs) {
198        const referenceItem = evt.from.children[pair.oldIndex] ?? null;
199        evt.from.insertBefore(pair.element, referenceItem);
200    }
201
202    if (isSwap) {
203        const swapReferenceItem = evt.to.children[evt.newIndex] ?? null;
204        evt.to.insertBefore(evt.swapItem, swapReferenceItem);
205    }
206}
207
208function getMovedElementPairs(evt) {
209    if (evt.oldIndicies?.length) {
210        return evt.oldIndicies.map(x => ({
211            element: x.multiDragElement,
212            oldIndex: x.index
213        }));
214    }
215
216    return [{
217        element: evt.item,
218        oldIndex: evt.oldIndex
219    }];
220}
221
222function isCloneMode(evt) {
223    return evt.pullMode === "clone";
224}
225
226function removeClone(evt) {
227    if (evt.clones?.length) {
228        for (const clone of evt.clones) {
229            clone.remove();
230        }
231
232        return;
233    }
234
235    evt.clone?.remove();
236}
237
238function getIndexes(indices) {
239    return indices?.map(x => x.index) ?? [];
240}
241
242function keepSelectionWithinCurrentSortable(evt) {
243    const currentItems = evt.items.filter(item => item.parentElement === evt.from);
244
245    if (currentItems.length === evt.items.length) {
246        return;
247    }
248
249    for (const item of evt.items) {
250        if (item.parentElement !== evt.from) {
251            Sortable.utils.deselect(item);
252        }
253    }
254
255    evt.items = currentItems;
256}
257
258function resetCrossSortableMultiDragSelection(evt) {
259    if (evt.from === evt.to || !evt.items?.length) {
260        return;
261    }
262
263    setTimeout(() => {
264        Sortable.get(evt.to)?.multiDrag?._deselectMultiDrag?.();
265    });
266}
267
268function getElementIndex(element) {
269    return Sortable.utils.index(element);
270}
271
272function getElementIndexes(elements) {
273    return elements?.map(getElementIndex) ?? [];
274}
275
276async function ensureSortableJs() {
277    if (isSortableJsAvailable()) {
278        return;
279    }
280
281    sortableJsLoadPromise ??= loadSortableJs(`${sortableJsPath}?v=${sortableJsVersion}`);
282    await sortableJsLoadPromise;
283
284    ensureSortableJsAvailable();
285}
286
287function loadSortableJs(src) {
288    return new Promise((resolve, reject) => {
289        const sortableJs = document.createElement("script");
290        sortableJs.src = src;
291
292        sortableJs.addEventListener("load", () => resolve(), { once: true });
293        sortableJs.addEventListener("error", () => {
294            sortableJs.remove();
295            sortableJsLoadPromise = null;
296            reject(new Error(`Failed to load SortableJS from '${src}'.`));
297        }, { once: true });
298
299        document.body.append(sortableJs);
300    });
301}
302
303function ensureSortableJsAvailable() {
304    if (isSortableJsAvailable()) {
305        return;
306    }
307
308    throw new Error(
309        "SortableJS is not loaded. Add SortableJS before BlazorSortable initializes, or enable SortableOptions.AutoLoadSortableJs."
310    );
311}
312
313function isSortableJsAvailable() {
314    return typeof Sortable === "function";
315}
316
317function ensureStylesheet(path, version) {
318    if (injectedStylesheets.has(path)) {
319        return;
320    }
321
322    const headStylesheetLinks = getHeadStylesheetLinks();
323
324    if (headStylesheetLinks.some(link => link.href.includes(path))) {
325        injectedStylesheets.add(path);
326        return;
327    }
328
329    const link = document.createElement("link");
330    link.rel = "stylesheet";
331    link.href = `${path}?v=${version}`;
332
333    insertHeadStylesheetLink(headStylesheetLinks, link);
334    injectedStylesheets.add(path);
335}
336
337function getHeadStylesheetLinks() {
338    return Array.from(document.head.querySelectorAll('link[rel="stylesheet"]'));
339}
340
341function insertHeadStylesheetLink(headStylesheetLinks, link) {
342    const firstHeadStylesheetLink = headStylesheetLinks[0];
343
344    if (firstHeadStylesheetLink) {
345        firstHeadStylesheetLink.before(link);
346    } else {
347        document.head.append(link);
348    }
349}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.