PageSourceSearch

https://en.monos.lu/assets/controllers/category_drag_controller-fe…cef0fed3f36457ac2f7d525bf9c9d1606ed9ff60.js

js monos.lu collected 2026-10-08 06:21:46 UTC 59,112 bytes, 1,356 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"// app/javascript/controllers/category_sortable_controller.js
2
3
4
5/*
6 * HTML5Sortable package
7 * https://github.com/lukasoppermann/html5sortable
8 *
9 * Maintained by Lukas Oppermann <[email protected]>
10 *
11 * Released under the MIT license.
12 */
13var sortable = (function () {
14    'use strict';
15    console.log("load sortablejs")
16    /**
17     * Get or set data on element
18     * @param {HTMLElement} element
19     * @param {string} key
20     * @param {any} value
21     * @return {*}
22     */
23    function addData(element, key, value) {
24        if (value === undefined) {
25            return element && element.h5s && element.h5s.data && element.h5s.data[key];
26        }
27        else {
28            element.h5s = element.h5s || {};
29            element.h5s.data = element.h5s.data || {};
30            element.h5s.data[key] = value;
31        }
32    }
33    /**
34     * Remove data from element
35     * @param {HTMLElement} element
36     */
37    function removeData(element) {
38        if (element.h5s) {
39            delete element.h5s.data;
40        }
41    }
42
43    /* eslint-env browser */
44    /**
45     * Filter only wanted nodes
46     * @param {NodeList|HTMLCollection|Array} nodes
47     * @param {String} selector
48     * @returns {Array}
49     */
50    var filter = (function (nodes, selector) {
51        if (!(nodes instanceof NodeList || nodes instanceof HTMLCollection || nodes instanceof Array)) {
52            throw new Error('You must provide a nodeList/HTMLCollection/Array of elements to be filtered.');
53        }
54        if (typeof selector !== 'string') {
55            return Array.from(nodes);
56        }
57        return Array.from(nodes).filter(function (item) { return item.nodeType === 1 && item.matches(selector); });
58    });
59
60    /* eslint-env browser */
61    /* eslint-disable no-use-before-define */
62    var stores = new Map();
63    /* eslint-enable no-use-before-define */
64    /**
65     * Stores data & configurations per Sortable
66     * @param {Object} config
67     */
68    var Store = /** @class */ (function () {
69        function Store() {
70            this._config = new Map(); // eslint-disable-line no-undef
71            this._placeholder = undefined; // eslint-disable-line no-undef
72            this._data = new Map(); // eslint-disable-line no-undef
73        }
74        Object.defineProperty(Store.prototype, "config", {
75            /**
76             * get the configuration map of a class instance
77             * @method config
78             * @return {object}
79             */
80            get: function () {
81                // transform Map to object
82                var config = {};
83                this._config.forEach(function (value, key) {
84                    config[key] = value;
85                });
86                // return object
87                return config;
88            },
89            /**
90             * set the configuration of a class instance
91             * @method config
92             * @param {object} config object of configurations
93             */
94            set: function (config) {
95                if (typeof config !== 'object') {
96                    throw new Error('You must provide a valid configuration object to the config setter.');
97                }
98                // combine config with default
99                var mergedConfig = Object.assign({}, config);
100                // add config to map
101                this._config = new Map(Object.entries(mergedConfig));
102            },
103            enumerable: false,
104            configurable: true
105        });
106        /**
107         * set individual configuration of a class instance
108         * @method setConfig
109         * @param  key valid configuration key
110         * @param  value any value
111         * @return void
112         */
113        Store.prototype.setConfig = function (key, value) {
114            if (!this._config.has(key)) {
115                throw new Error("Trying to set invalid configuration item: " + key);
116            }
117            // set config
118            this._config.set(key, value);
119        };
120        /**
121         * get an individual configuration of a class instance
122         * @method getConfig
123         * @param  key valid configuration key
124         * @return any configuration value
125         */
126        Store.prototype.getConfig = function (key) {
127            if (!this._config.has(key)) {
128                throw new Error("Invalid configuration item requested: " + key);
129            }
130            return this._config.get(key);
131        };
132        Object.defineProperty(Store.prototype, "placeholder", {
133            /**
134             * get the placeholder for a class instance
135             * @method placeholder
136             * @return {HTMLElement|null}
137             */
138            get: function () {
139                return this._placeholder;
140            },
141            /**
142             * set the placeholder for a class instance
143             * @method placeholder
144             * @param {HTMLElement} placeholder
145             * @return {void}
146             */
147            set: function (placeholder) {
148                if (!(placeholder instanceof HTMLElement) && placeholder !== null) {
149                    throw new Error('A placeholder must be an html element or null.');
150                }
151                this._placeholder = placeholder;
152            },
153            enumerable: false,
154            configurable: true
155        });
156        /**
157         * set an data entry
158         * @method setData
159         * @param {string} key
160         * @param {any} value
161         * @return {void}
162         */
163        Store.prototype.setData = function (key, value) {
164            if (typeof key !== 'string') {
165                throw new Error('The key must be a string.');
166            }
167            this._data.set(key, value);
168        };
169        /**
170         * get an data entry
171         * @method getData
172         * @param {string} key an existing key
173         * @return {any}
174         */
175        Store.prototype.getData = function (key) {
176            if (typeof key !== 'string') {
177                throw new Error('The key must be a string.');
178            }
179            return this._data.get(key);
180        };
181        /**
182         * delete an data entry
183         * @method deleteData
184         * @param {string} key an existing key
185         * @return {boolean}
186         */
187        Store.prototype.deleteData = function (key) {
188            if (typeof key !== 'string') {
189                throw new Error('The key must be a string.');
190            }
191            return this._data.delete(key);
192        };
193        return Store;
194    }());
195    /**
196     * @param {HTMLElement} sortableElement
197     * @returns {Class: Store}
198     */
199    var store = (function (sortableElement) {
200        // if sortableElement is wrong type
201        if (!(sortableElement instanceof HTMLElement)) {
202            throw new Error('Please provide a sortable to the store function.');
203        }
204        // create new instance if not avilable
205        if (!stores.has(sortableElement)) {
206            stores.set(sortableElement, new Store());
207        }
208        // return instance
209        return stores.get(sortableElement);
210    });
211
212    /**
213     * @param {Array|HTMLElement} element
214     * @param {Function} callback
215     * @param {string} event
216     */
217    function addEventListener(element, eventName, callback) {
218        if (element instanceof Array) {
219            for (var i = 0; i < element.length; ++i) {
220                addEventListener(element[i], eventName, callback);
221            }
222            return;
223        }
224        element.addEventListener(eventName, callback);
225        store(element).setData("event" + eventName, callback);
226    }
227    /**
228     * @param {Array<HTMLElement>|HTMLElement} element
229     * @param {string} eventName
230     */
231    function removeEventListener(element, eventName) {
232        if (element instanceof Array) {
233            for (var i = 0; i < element.length; ++i) {
234                removeEventListener(element[i], eventName);
235            }
236            return;
237        }
238        element.removeEventListener(eventName, store(element).getData("event" + eventName));
239        store(element).deleteData("event" + eventName);
240    }
241
242    /**
243     * @param {Array<HTMLElement>|HTMLElement} element
244     * @param {string} attribute
245     * @param {string} value
246     */
247    function addAttribute(element, attribute, value) {
248        if (element instanceof Array) {
249            for (var i = 0; i < element.length; ++i) {
250                addAttribute(element[i], attribute, value);
251            }
252            return;
253        }
254        element.setAttribute(attribute, value);
255    }
256    /**
257     * @param {Array|HTMLElement} element
258     * @param {string} attribute
259     */
260    function removeAttribute(element, attribute) {
261        if (element instanceof Array) {
262            for (var i = 0; i < element.length; ++i) {
263                removeAttribute(element[i], attribute);
264            }
265            return;
266        }
267        element.removeAttribute(attribute);
268    }
269
270    /**
271     * @param {HTMLElement} element
272     * @returns {Object}
273     */
274    var offset = (function (element) {
275        if (!element.parentElement || element.getClientRects().length === 0) {
276            throw new Error('target element must be part of the dom');
277        }
278        var rect = element.getClientRects()[0];
279        return {
280            left: rect.left + window.pageXOffset,
281            right: rect.right + window.pageXOffset,
282            top: rect.top + window.pageYOffset,
283            bottom: rect.bottom + window.pageYOffset
284        };
285    });
286
287    /**
288     * Creates and returns a new debounced version of the passed function which will postpone its execution until after wait milliseconds have elapsed
289     * @param {Function} func to debounce
290     * @param {number} time to wait before calling function with latest arguments, 0 - no debounce
291     * @returns {function} - debounced function
292     */
293    var debounce = (function (func, wait) {
294        if (wait === void 0) { wait = 0; }
295        var timeout;
296        return function () {
297            var args = [];
298            for (var _i = 0; _i < arguments.length; _i++) {
299                args[_i] = arguments[_i];
300            }
301            clearTimeout(timeout);
302            timeout = setTimeout(function () {
303                func.apply(void 0, args);
304            }, wait);
305        };
306    });
307
308    /* eslint-env browser */
309    /**
310     * Get position of the element relatively to its sibling elements
311     * @param {HTMLElement} element
312     * @returns {number}
313     */
314    var getIndex = (function (element, elementList) {
315        if (!(element instanceof HTMLElement) || !(elementList instanceof NodeList || elementList instanceof HTMLCollection || elementList instanceof Array)) {
316            throw new Error('You must provide an element and a list of elements.');
317        }
318        return Array.from(elementList).indexOf(element);
319    });
320
321    /* eslint-env browser */
322    /**
323     * Test whether element is in DOM
324     * @param {HTMLElement} element
325     * @returns {boolean}
326     */
327    var isInDom = (function (element) {
328        if (!(element instanceof HTMLElement)) {
329            throw new Error('Element is not a node element.');
330        }
331        return element.parentNode !== null;
332    });
333
334    /* eslint-env browser */
335    /**
336     * Insert node before or after target
337     * @param {HTMLElement} referenceNode - reference element
338     * @param {HTMLElement} newElement - element to be inserted
339     * @param {String} position - insert before or after reference element
340     */
341    var insertNode = function (referenceNode, newElement, position) {
342        if (!(referenceNode instanceof HTMLElement) || !(referenceNode.parentElement instanceof HTMLElement)) {
343            throw new Error('target and element must be a node');
344        }
345        referenceNode.parentElement.insertBefore(newElement, (position === 'before' ? referenceNode : referenceNode.nextElementSibling));
346    };
347    /**
348     * Insert before target
349     * @param {HTMLElement} target
350     * @param {HTMLElement} element
351     */
352    var insertBefore = function (target, element) { return insertN
352ode(target, element, 'before'); };
353    /**
354     * Insert after target
355     * @param {HTMLElement} target
356     * @param {HTMLElement} element
357     */
358    var insertAfter = function (target, element) { return insertNode(target, element, 'after'); };
359
360    /* eslint-env browser */
361    /**
362     * Filter only wanted nodes
363     * @param {HTMLElement} sortableContainer
364     * @param {Function} customSerializer
365     * @returns {Array}
366     */
367    var serialize = (function (sortableContainer, customItemSerializer, customContainerSerializer) {
368        if (customItemSerializer === void 0) { customItemSerializer = function (serializedItem, sortableContainer) { return serializedItem; }; }
369        if (customContainerSerializer === void 0) { customContainerSerializer = function (serializedContainer) { return serializedContainer; }; }
370        // check for valid sortableContainer
371        if (!(sortableContainer instanceof HTMLElement) || !sortableContainer.isSortable === true) {
372            throw new Error('You need to provide a sortableContainer to be serialized.');
373        }
374        // check for valid serializers
375        if (typeof customItemSerializer !== 'function' || typeof customContainerSerializer !== 'function') {
376            throw new Error('You need to provide a valid serializer for items and the container.');
377        }
378        // get options
379        var options = addData(sortableContainer, 'opts');
380        var item = options.items;
381        // serialize container
382        var items = filter(sortableContainer.children, item);
383        var serializedItems = items.map(function (item) {
384            return {
385                parent: sortableContainer,
386                node: item,
387                html: item.outerHTML,
388                index: getIndex(item, items)
389            };
390        });
391        // serialize container
392        var container = {
393            node: sortableContainer,
394            itemCount: serializedItems.length
395        };
396        return {
397            container: customContainerSerializer(container),
398            items: serializedItems.map(function (item) { return customItemSerializer(item, sortableContainer); })
399        };
400    });
401
402    /* eslint-env browser */
403    /**
404     * create a placeholder element
405     * @param {HTMLElement} sortableElement a single sortable
406     * @param {string|undefined} placeholder a string representing an html element
407     * @param {string} placeholderClasses a string representing the classes that should be added to the placeholder
408     */
409    var makePlaceholder = (function (sortableElement, placeholder, placeholderClass) {
410        var _a;
411        if (placeholderClass === void 0) { placeholderClass = 'sortable-placeholder'; }
412        if (!(sortableElement instanceof HTMLElement)) {
413            throw new Error('You must provide a valid element as a sortable.');
414        }
415        // if placeholder is not an element
416        if (!(placeholder instanceof HTMLElement) && placeholder !== undefined) {
417            throw new Error('You must provide a valid element as a placeholder or set ot to undefined.');
418        }
419        // if no placeholder element is given
420        if (placeholder === undefined) {
421            if (['UL', 'OL'].includes(sortableElement.tagName)) {
422                placeholder = document.createElement('li');
423            }
424            else if (['TABLE', 'TBODY'].includes(sortableElement.tagName)) {
425                placeholder = document.createElement('tr');
426                // set colspan to always all rows, otherwise the item can only be dropped in first column
427                placeholder.innerHTML = '<td colspan="100"></td>';
428            }
429            else {
430                placeholder = document.createElement('div');
431            }
432        }
433        // add classes to placeholder
434        if (typeof placeholderClass === 'string') {
435            (_a = placeholder.classList).add.apply(_a, placeholderClass.split(' '));
436        }
437        return placeholder;
438    });
439
440    /* eslint-env browser */
441    /**
442     * Get height of an element including padding
443     * @param {HTMLElement} element an dom element
444     */
445    var getElementHeight = (function (element) {
446        if (!(element instanceof HTMLElement)) {
447            throw new Error('You must provide a valid dom element');
448        }
449        // get calculated style of element
450        var style = window.getComputedStyle(element);
451        // get only height if element has box-sizing: border-box specified
452        if (style.getPropertyValue('box-sizing') === 'border-box') {
453            return parseInt(style.getPropertyValue('height'), 10);
454        }
455        // pick applicable properties, convert to int and reduce by adding
456        return ['height', 'padding-top', 'padding-bottom']
457            .map(function (key) {
458                var int = parseInt(style.getPropertyValue(key), 10);
459                return isNaN(int) ? 0 : int;
460            })
461            .reduce(function (sum, value) { return sum + value; });
462    });
463
464    /* eslint-env browser */
465    /**
466     * Get width of an element including padding
467     * @param {HTMLElement} element an dom element
468     */
469    var getElementWidth = (function (element) {
470        if (!(element instanceof HTMLElement)) {
471            throw new Error('You must provide a valid dom element');
472        }
473        // get calculated style of element
474        var style = window.getComputedStyle(element);
475        // pick applicable properties, convert to int and reduce by adding
476        return ['width', 'padding-left', 'padding-right']
477            .map(function (key) {
478                var int = parseInt(style.getPropertyValue(key), 10);
479                return isNaN(int) ? 0 : int;
480            })
481            .reduce(function (sum, value) { return sum + value; });
482    });
483
484    /* eslint-env browser */
485    /**
486     * get handle or return item
487     * @param {Array<HTMLElement>} items
488     * @param {string} selector
489     */
490    var getHandles = (function (items, selector) {
491        if (!(items instanceof Array)) {
492            throw new Error('You must provide a Array of HTMLElements to be filtered.');
493        }
494        if (typeof selector !== 'string') {
495            return items;
496        }
497        return items
498            // remove items without handle from array
499            .filter(function (item) {
500                return item.querySelector(selector) instanceof HTMLElement ||
501                    (item.shadowRoot && item.shadowRoot.querySelector(selector) instanceof HTMLElement);
502            })
503            // replace item with handle in array
504            .map(function (item) {
505                return item.querySelector(selector) || (item.shadowRoot && item.shadowRoot.querySelector(selector));
506            });
507    });
508
509    /**
510     * @param {Event} event
511     * @returns {HTMLElement}
512     */
513    var getEventTarget = (function (event) {
514        return (event.composedPath && event.composedPath()[0]) || event.target;
515    });
516
517    /* eslint-env browser */
518    /**
519     * defaultDragImage returns the current item as dragged image
520     * @param {HTMLElement} draggedElement - the item that the user drags
521     * @param {object} elementOffset - an object with the offsets top, left, right & bottom
522     * @param {Event} event - the original drag event object
523     * @return {object} with element, posX and posY properties
524     */
525    var defaultDragImage = function (draggedElement, elementOffset, event) {
526        return {
527            element: draggedElement,
528            posX: event.pageX - elementOffset.left,
529            posY: event.pageY - elementOffset.top
530        };
531    };
532    /**
533     * attaches an element as the drag image to an event
534     * @param {Event} event - the original drag event object
535     * @param {HTMLElement} draggedElement - the item that the user drags
536     * @param {Function} customDragImage - function to create a custom dragImage
537     * @return void
538     */
539    var setDragImage = (function (event, draggedElement, customDragImage) {
540        // check if event is provided
541        if (!(event instanceof Event)) {
542            throw new Error('setDragImage requires a DragEvent as the first argument.');
543        }
544        // check if draggedElement is provided
545        if (!(draggedElement instanceof HTMLElement)) {
546            throw new Error('setDragImage requires the dragged element as the second argument.');
547        }
548        // set default function of none provided
549        if (!customDragImage) {
550            customDragImage = defaultDragImage;
551        }
552        // check if setDragImage method is available
553        if (event.dataTransfer && event.dataTransfer.setDragImage) {
554            // get the elements offset
555            var elementOffset = offset(draggedElement);
556            // get the dragImage
557            var dragImage = customDragImage(draggedElement, elementOffset, event);
558            // check if custom function returns correct values
559            if (!(dragImage.element instanceof HTMLElement) || typeof dragImage.posX !== 'number' || typeof dragImage.posY !== 'number') {
560                throw new Error('The customDragImage function you provided must return and object with the properties element[string], posX[integer], posY[integer].');
561            }
562            // needs to be set for HTML5 drag & drop to work
563            event.dataTransfer.effectAllowed = 'copyMove';
564            // Firefox requires it to use the event target's id for the data
565            event.dataTransfer.setData('text/plain', getEventTarget(event).id);
566            // set the drag image on the event
567            event.dataTransfer.setDragImage(dragImage.element, dragImage.posX, dragImage.posY);
568        }
569    });
570
571    /**
572     * Check if curList accepts items from destList
573     * @param {sortable} destination the container an item is move to
574     * @param {sortable} origin the container an item comes from
575     */
576    var listsConnected = (function (destination, origin) {
577        // check if valid sortable
578        if (destination.isSortable === true) {
579            var acceptFrom = store(destination).getConfig('acceptFrom');
580            // check if acceptFrom is valid
581            if (acceptFrom !== null && acceptFrom !== false && typeof acceptFrom !== 'string') {
582                throw new Error('HTML5Sortable: Wrong argument, "acceptFrom" must be "null", "false", or a valid selector string.');
583            }
584            if (acceptFrom !== null) {
585                return acceptFrom !== false && acceptFrom.split(',').filter(function (sel) {
586                    return sel.length > 0 && origin.matches(sel);
587                }).length > 0;
588            }
589            // drop in same list
590            if (destination === origin) {
591                return true;
592            }
593            // check if lists are connected with connectWith
594            if (store(destination).getConfig('connectWith') !== undefined && store(destination).getConfig('connectWith') !== null) {
595                return store(destination).getConfig('connectWith') === store(origin).getConfig('connectWith');
596            }
597        }
598        return false;
599    });
600
601    /**
602     * default configurations
603     */
604    var defaultConfiguration = {
605        items: null,
606        // deprecated
607        connectWith: null,
608        // deprecated
609        disableIEFix: null,
610        acceptFrom: null,
611        copy: false,
612        placeholder: null,
613        placeholderClass: 'sortable-placeholder',
614        draggingClass: 'sortable-dragging',
615        hoverClass: false,
616        dropTargetContainerClass: false,
617        debounce: 0,
618        throttleTime: 100,
619        maxItems: 0,
620        itemSerializer: undefined,
621        containerSerializer: undefined,
622        customDragImage: null,
623        orientation: 'vertical'
624    };
625
626    /**
627     * make sure a function is only called once within the given amount of time
628     * @param {Function} fn the function to throttle
629     * @param {number} threshold time limit for throttling
630     */
631    // must use function to keep this context
632    function throttle(fn, threshold) {
633        var _this = this;
634        if (threshold === void 0) { threshold = 250; }
635        // check function
636        if (typeof fn !== 'function') {
637            throw new Error('You must provide a function as the first argument for throttle.');
638        }
639        // check threshold
640        if (typeof threshold !== 'number') {
641            throw new Error('You must provide a number as the second argument for throttle.');
642        }
643        var lastEventTimestamp = null;
644        return function () {
645            var args = [];
646            for (var _i = 0; _i < arguments.length; _i++) {
647                args[_i] = arguments[_i];
648            }
649            var now = Date.now();
650            if (lastEventTimestamp === null || now - lastEventTimestamp >= threshold) {
651                lastEventTimestamp = now;
652                fn.apply(_this, args);
653            }
654        };
655    }
656
657    /* eslint-env browser */
658    /**
659     * enable or disable hoverClass on mouseenter/leave if container Items
660     * @param {sortable} sortableContainer a valid sortableContainer
661     * @param {boolean} enable enable or disable event
662     */
663    var enableHoverClass = (function (sortableContainer, enable) {
664        if (typeof store(sortableContainer).getConfig('hoverClass') === 'string') {
665            var hoverClasses_1 = store(sortableContainer).getConfig('hoverClass').split(' ');
666            // add class on hover
667            if (enable === true) {
668                addEventListener(sortableContainer, 'mousemove', throttle(function (event) {
669                    // check of no mouse button was pressed when mousemove started == no drag
670                    if (event.buttons === 0) {
671                        filter(sortableContainer.children, store(sortableContainer).getConfig('items')).forEach(function (item) {
672                            var _a, _b;
673                            if (item === event.target || item.contains(event.target)) {
674                                (_a = item.classList).add.apply(_a, hoverClasses_1);
675                            }
676                            else {
677                                (_b = item.classList).remove.apply(_b, hoverClasses_1);
678                            }
679                        });
680                    }
681                }, store(sortableContainer).getConfig('throttleTime')));
682                // remove class on leave
683                addEventListener(sortableContainer, 'mouseleave', function () {
684                    filter(sortableContainer.children, store(sortableContainer).getConfig('items')).forEach(function (item) {
685                        var _a;
686                        (_a = item.classList).remove.apply(_a, hoverClasses_1);
687                    });
688                });
689                // remove events
690            }
691            else {
692                removeEventListener(sortableContainer, 'mousemove');
693                removeEventListener(sortableContainer, 'mouseleave');
694            }
695        }
696    });
697
698    /* eslint-env browser */
699    /*
700     * variables global to the plugin
701     */
702    var dragging;
703    var draggingHeight;
704    var draggingWidth;
705    /*
706     * Keeps track of the initialy selected list, where 'dragstart' event was triggered
707     * It allows us to move the data in between individual Sortable List instances
708     */
709    // Origin List - data from before any item was changed
710    var originContainer;
711    var originIndex;
712    var originElementIndex;
713    var originItemsBeforeUpdate;
714    // Previous Sortable Container - we dispatch as sortenter event when a
715    // dragged item enters a sortableContainer for the first time
716    var previousContainer;
717    // Destination List - data from before any item was changed
718    var destinationItemsBeforeUpdate;
719    /**
720     * remove event handlers from items
721     * @param {Array|NodeList} items
722     */
723    var removeItemEvents = function (items) {
724        removeEventListener(items, 'dragstart');
725        removeEventListener(items, 'dragend');
726        removeEventListener(items, 'dragover');
727        removeEventListener(items, 'dragenter');
728        removeEventListener(items, 'drop');
729        removeEventListener(items, 'mouseenter');
730        removeEventListener(items, 'mouseleave');
731    };
732    // Remove container events
733    var removeContainerEvents = function (originContainer, previousContainer) {
734        if (originContainer) {
735            removeEventListener(originContainer, 'dragleave');
736        }
737        if (previousContainer && (previousContainer !== originContainer)) {
738            removeEventListener(previousContainer, 'dragleave');
739        }
740    };
741    /**
742     * getDragging returns the current element to drag or
743     * a copy of the element.
744     * Is Copy Active for sortable
745     * @param {HTMLElement} draggedItem - the item that the user drags
746     * @param {HTMLElement} sortable a single sortable
747     */
748    var getDragging = function (draggedItem, sortable) {
749        var ditem = draggedItem;
750        if (store(sortable).getConfig('copy') === true) {
751            ditem = draggedItem.cloneNode(true);
752            addAttribute(ditem, 'aria-copied', 'true');
753            draggedItem.parentElement.appendChild(ditem);
754            ditem.style.display = 'none';
755            ditem.oldDisplay = draggedItem.style.display;
756        }
757        return ditem;
758    };
759    /**
760     * Remove data from sortable
761     * @param {HTMLElement} sortable a single sortable
762     */
763    var removeSortableData = function (sortable) {
764        removeData(sortable);
765        removeAttribute(sortable, 'aria-dropeffect');
766    };
767    /**
768     * Remove data from items
769     * @param {Array<HTMLElement>|HTMLElement} items
770     */
771    var removeItemData = function (items) {
772        removeAttribute(items, 'aria-grabbed');
773        removeAttribute(items, 'aria-copied');
774        removeAttribute(items, 'draggable');
775        removeAttribute(items, 'role');
776    };
777    /**
778     * find sortable from element. travels up parent element until found or null.
779     * @param {HTMLElement} element a single sortable
780     * @param {Event} event - the current event. We need to pass it to be able to
781     * find Sortable whith shadowRoot (document fragment has no parent)
782     */
783    function findSortable(element, event) {
784        if (event.composedPath) {
785            return event.composedPath().find(function (el) { return el.isSortable; });
786        }
787        while (element.isSortable !== true) {
788            element = element.parentElement;
789        }
790        return element;
791    }
792    /**
793     * Dragging event is on the sortable element. finds the top child that
794     * contains the element.
795     * @param {HTMLElement} sortableElement a single sortable
796     * @param {HTMLElement} element is that being dragged
797     */
798    function findDragElement(sortableElement, element) {
799        var options = addData(sortableElement, 'opts');
800        var items = filter(sortableElement.children, options.items);
801        var itemlist = items.filter(function (ele) {
802            return ele.contains(element) || (ele.shadowRoot && ele.shadowRoot.contains(element));
803        });
804        return itemlist.length > 0 ? itemlist[0] : element;
805    }
806    /**
807     * Destroy the sortable
808     * @param {HTMLElement} sortableElement a single sortable
809     */
810    var destroySortable = function (sortableElement) {
811        var opts = addData(sortableElement, 'opts') || {};
812        var items = filter(sortableElement.children, opts.items);
813        var handles = getHandles(items, opts.handle);
814        // disable adding hover class
815        enableHoverClass(sortableElement, false);
816        // remove event handlers & data from sortable
817        removeEventListener(sortableElement, 'dragover');
818        removeEventListener(sortableElement, 'dragenter');
819        removeEventListener(sortableElement, 'dragstart');
820        removeEventListener(sortableElement, 'dragend');
821        removeEventListener(sortableElement, 'drop');
822        // remove event data from sortable
823        removeSortableData(sortableElement);
824        // remove event handlers & data from items
825        removeEventListener(handles, 'mousedown');
826        removeItemEvents(items);
827        removeItemData(items);
828        removeContainerEvents(originContainer, previousContainer);
829        // clear sortable flag
830        sortableElement.isSortable = false;
831    };
832    /**
833     * Enable the sortable
834     * @param {HTMLElement} sortableElement a single sortable
835     */
836    var enableSortable = function (sortableElement) {
837        var opts = addData(sortableElement, 'opts');
838        var items = filter(sortableElement.children, opts.items);
839        var handles = getHandles(items, opts.handle);
840        addAttribute(sortableElement, 'aria-dropeffect', 'move');
841        addData(sortableElement, '_disabled', 'false');
842        addAttribute(handles, 'draggable', 'true');
843        // enable hover class
844        enableHoverClass(sortableElement, true);
845        // @todo: remove this fix
846        // IE FIX for ghost
847        // can be disabled as it has the side effect that other events
848        // (e.g. click) will be ignored
849        if (opts.disableIEFix === false) {
850            var spanEl = (document || window.document).createElement('span');
851            if (typeof spanEl.dragDrop === 'function') {
852                addEventListener(handles, 'mousedown', function () {
853                    if (items.indexOf(this) !== -1) {
854                        this.dragDrop();
855                    }
856                    else {
857                        var parent = this.parentElement;
858                        while (items.indexOf(parent) === -1) {
859                            parent = parent.parentElement;
860                        }
861                        parent.dragDrop();
862                    }
863                });
864            }
865        }
866    };
867    /**
868     * Disable the sortable
869     * @param {HTMLElement} sortableElement a single sortable
870     */
871    var disableSortable = function (sortableElement) {
872        var opts = addData(sortableElement, 'opts');
873        var items = filter(sortableElement.children, opts.items);
874        var handles = getHandles(items, opts.handle);
875        addAttribute(sortableElement, 'aria-dropeffect', 'none');
876        addData(sortableElement, '_disabled', 'true');
877        addAttribute(handles, 'draggable', 'false');
878        removeEventListener(handles, 'mousedown');
879        enableHoverClass(sortableElement, false);
880    };
881    /**
882     * Reload the sortable
883     * @param {HTMLElement} sortableElement a single sortable
884     * @description events need to be removed to not be double bound
885     */
886    var reloadSortable = function (sortableElement) {
887        var opts = addData(sortableElement, 'opts');
888        var items = filter(sortableElement.children, opts.items);
889        var handles = getHandles(items, opts.handle);
890        addData(sortableElement, '_disabled', 'false');
891        // remove event handlers from items
892        removeItemEvents(items);
893        removeContainerEvents(originContainer, previousContainer);
894        removeEventListener(handles, 'mousedown');
895        // remove event handlers from sortable
896        removeEventListener(sortableElement, 'dragover');
897        removeEventListener(sortableElement, 'dragenter');
898        removeEventListener(sortableElement, 'drop');
899    };
900    /**
901     * Public sortable object
902     * @param {Array|NodeList} sortableElements
903     * @param {object|string} options|method
904     */
905    function sortable(sortableElements, options) {
906        // get method string to see if a method is called
907        var method = String(options);
908        options = options || {};
909        // check if the user provided a selector instead of an element
910        if (typeof sortableElements === 'string') {
911            sortableElements = document.querySelectorAll(sortableElements);
912        }
913        // if the user provided an element, return it in an array to keep the return value consistant
914        if (sortableElements instanceof HTMLElement) {
915            sortableElements = [sortableElements];
916        }
917        sortableElements = Array.prototype.slice.call(sortableElements);
918        if (/serialize/.test(method)) {
919            return sortableElements.map(function (sortableContainer) {
920                var opts = addData(sortableContainer, 'opts');
921                return serialize(sortableContainer, opts.itemSerializer, opts.containerSerializer);
922            });
923        }
924        sortableElements.forEach(function (sortableElement) {
925            if (/enable|disable|destroy/.test(method)) {
926                return sortable[method](sortableElement);
927            }
928            // log deprecation
929            ['connectWith', 'disableIEFix'].forEach(function (configKey) {
930                if (Object.prototype.hasOwnProperty.call(options, configKey) && options[configKey] !== null) {
931                    console.warn("HTML5Sortable: You are using the deprecated configuration \"" + configKey + "\". This will be removed in an upcoming version, make sure to migrate to the new options when updating.");
932                }
933            });
934            // merge options with default options
935            options = Object.assign({}, defaultConfiguration, store(sortableElement).config, options);
936            // init data store for sortable
937            store(sortableElement).config = options;
938            // set options on sortable
939            addData(sortableElement, 'opts', options);
940            // property to define as sortable
941            sortableElement.isSortable = true;
942            // reset sortable
943            reloadSortable(sortableElement);
944            // initialize
945            var listItems = filter(sortableElement.children, options.items);
946            // create element if user defined a placeholder element as a string
947            var customPlaceholder;
948            if (options.placeholder !== null && options.placeholder !== undefined) {
949                var tempContainer = document.createElement(sortableElement.tagName);
950                if (options.placeholder instanceof HTMLElement) {
951                    tempContainer.appendChild(options.placeholder);
952                }
953                else {
954                    tempContainer.innerHTML = options.placeholder;
955                }
956                customPlaceholder = tempContainer.children[0];
957            }
958            // add placeholder
959            store(sortableElement).placeholder = makePlaceholder(sortableElement, customPlaceholder, options.placeholderClass);
960            addData(sortableElement, 'items', options.items);
961            if (options.acceptFrom) {
962                addData(sortableElement, 'acceptFrom', options.acceptFrom);
963            }
964            else if (options.connectWith) {
965                addData(sortableElement, 'connectWith', options.connectWith);
966            }
967            enableSortable(sortableElement);
968            addAttribute(listItems, 'role', 'option');
969            addAttribute(listItems, 'aria-grabbed', 'false');
970            /*
971             Handle drag events on draggable items
972             Handle is set at the sortableElement level as it will bubble up
973             from the item
974             */
975            addEventListener(sortableElement, 'dragstart', function (e) {
976                // ignore dragstart events
977                var target = getEventTarget(e);
978                if (target.isSortable === true) {
979                    return;
980                }
981                e.stopImmediatePropagation();
982                if ((options.handle && !target.matches(options.handle)) || target.getAttribute('draggable') === 'false') {
983                    return;
984                }
985                var sortableContainer = findSortable(target, e);
986                var dragItem = findDragElement(sortableContainer, target);
987                // grab values
988                originItemsBeforeUpdate = filter(sortableContainer.children, options.items);
989                originIndex = originItemsBeforeUpdate.indexOf(dragItem);
990                originElementIndex = getIndex(dragItem, sortableContainer.children);
991                originContainer = sortableContainer;
992                // add transparent clone or other ghost to cursor
993                setDragImage(e, dragItem, options.customDragImage);
994                // cache selsection & add attr for dragging
995                draggingHeight = getElementHeight(dragItem);
996                draggingWidth = getElementWidth(dragItem);
997                dragItem.classList.add(options.draggingClass);
998                dragging = getDragging(dragItem, sortableContainer);
999                addAttribute(dragging, 'aria-grabbed', 'true');
1000                // dispatch sortstart event on each element in group
1001                sortableContainer.dispatchEvent(new CustomEvent('sortstart', {
1002                    detail: {
1003                        origin: {
1004                            elementIndex: originElementIndex,
1005                            index: originIndex,
1006                            container: originContainer
1007                        },
1008                        item: dragging,
1009                        originalTarget: target
1010                    }
1011                }));
1012            });
1013            /*
1014             We are capturing targetSortable before modifications with 'dragenter' event
1015            */
1016            addEventListener(sortableElement, 'dragenter', function (e) {
1017                var target = getEventTarget(e);
1018                var sortableContainer = findSortable(target, e);
1019                if (sortableContainer && sortableContainer !== previousContainer) {
1020                    destinationItemsBeforeUpdate = filter(sortableContainer.children, addData(sortableContainer, 'items'))
1021                        .filter(function (item) { return item !== store(sortableElement).placeholder; });
1022                    if (options.dropTargetContainerClass) {
1023                        sortableContainer.classList.add(options.dropTargetContainerClass);
1024                    }
1025                    sortableContainer.dispatchEvent(new CustomEvent('sortenter', {
1026                        detail: {
1027                            origin: {
1028                                elementIndex: originElementIndex,
1029                                index: originIndex,
1030                                container: originContainer
1031                            },
1032                            destination: {
1033                                container: sortableContainer,
1034                                itemsBeforeUpdate: destinationItemsBeforeUpdate
1035                            },
1036                            item: dragging,
1037                            originalTarget: target
1038                        }
1039                    }));
1040                    addEventListener(sortableContainer, 'dragleave', function (e) {
1041                        // TODO: rename outTarget to be more self-explanatory
1042                        // e.fromElement for very old browsers, similar to relatedTarget
1043                        var outTarget = e.relatedTarget || e.fromElement;
1044                        if (!e.currentTarget.contains(outTarget)) {
1045                            if (options.dropTargetContainerClass) {
1046                                sortableContainer.classList.remove(options.dropTargetContainerClass);
1047                            }
1048                            sortableContainer.dispatchEvent(new CustomEvent('sortleave', {
1049                                detail: {
1050                                    origin: {
1051                                        elementIndex: originElementIndex,
1052                                        index: originIndex,
1053                                        container: sortableContainer
1054                                    },
1055                                    item: dragging,
1056                                    originalTarget: target
1057                                }
1058                            }));
1059                        }
1060                    });
1061                }
1062                previousContainer = sortableContainer;
1063            });
1064            /*
1065             * Dragend Event - https://developer.mozilla.org/en-US/docs/Web/Events/dragend
1066             * Fires each time dragEvent end, or ESC pressed
1067             * We are using it to clean up any draggable elements and placeholders
1068             */
1069            addEventListener(sortableElement, 'dragend', function (e) {
1070                if (!dragging) {
1071                    return;
1072                }
1073                dragging.classList.remove(options.draggingClass);
1074                addAttribute(dragging, 'aria-grabbed', 'false');
1075                if (dragging.getAttribute('aria-copied') === 'true' && addData(dragging, 'dropped') !== 'true') {
1076                    dragging.remove();
1077                }
1078                if (dragging.oldDisplay !== undefined) {
1079                    dragging.style.display = dragging.oldDisplay;
1080                    delete dragging.oldDisplay;
1081                }
1082                var visiblePlaceholder = Array.from(stores.values()).map(function (data) { return data.placeholder; })
1083                    .filter(function (placeholder) { return placeholder instanceof HTMLElement; })
1084                    .filter(isInDom)[0];
1085                if (visiblePlaceholder) {
1086                    visiblePlaceholder.remove();
1087                }
1088                // dispatch sortstart event on each element in group
1089                sortableElement.dispatchEvent(new CustomEvent('sortstop', {
1090                    detail: {
1091                        origin: {
1092                            elementIndex: originElementIndex,
1093                            index: originIndex,
1094                            container: originContainer
1095                        },
1096                        item: dragging
1097                    }
1098                }));
1099                previousContainer = null;
1100                dragging = null;
1101                draggingHeight = null;
1102                draggingWidth = null;
1103            });
1104            /*
1105             * Drop Event - https://developer.mozilla.org/en-US/docs/Web/Events/drop
1106             * Fires when valid drop target area is hit
1107             */
1108            addEventListener(sortableElement, 'drop', function (e) {
1109                if (!listsConnected(sortableElement, dragging.parentElement)) {
1110                    return;
1111                }
1112                e.preventDefault();
1113                e.stopPropagation();
1114                addData(dragging, 'dropped', 'true');
1115                // get the one placeholder that is currently visible
1116                var visiblePlaceholder = Array.from(stores.values()).map(function (data) {
1117                    return data.placeholder;
1118                })
1119                    // filter only HTMLElements
1120                    .filter(function (placeholder) { return placeholder instanceof HTMLElement; })
1121                    // only elements in DOM
1122                    .filter(isInDom)[0];
1123                if (visiblePlaceholder) {
1124                    visiblePlaceholder.replaceWith(dragging);
1125                    // to avoid flickering restoring element display immediately after replacing placeholder
1126                    if (dragging.oldDisplay !== undefined) {
1127                        dragging.style.display = dragging.oldDisplay;
1128                        delete dragging.oldDisplay;
1129                    }
1130                }
1131                else {
1132                    // set the dropped value to 'false' to delete copied dragging at the time of 'dragend'
1133                    addData(dragging, 'dropped', 'false');
1134                    return;
1135                }
1136                /*
1137                 * Fires Custom Event - 'sortstop'
1138                 */
1139                sortableElement.dispatchEvent(new CustomEvent('sortstop', {
1140                    detail: {
1141                        origin: {
1142                            elementIndex: originElementIndex,
1143                            index: originIndex,
1144                            container: originContainer
1145                        },
1146                        item: dragging
1147                    }
1148                }));
1149                var placeholder = store(sortableElement).placeholder;
1150                var originItems = filter(originContainer.children, options.items)
1151                    .filter(function (item) { return item !== placeholder; });
1152                var destinationContainer = this.isSortable === true ? this : this.parentElement;
1153                var destinationItems = filter(destinationContainer.children, addData(destinationContainer, 'items'))
1154                    .filter(function (item) { return item !== placeholder; });
1155                var destinationElementIndex = getIndex(dragging, Array.from(dragging.parentElement.children)
1156                    .filter(function (item) { return item !== placeholder; }));
1157                var destinationIndex = getIndex(dragging, destinationItems);
1158                if (options.dropTargetContainerClass) {
1159                    destinationContainer.classList.remove(options.dropTargetContainerClass);
1160                }
1161                /*
1162                 * When a list item changed container lists or index within a list
1163                 * Fires Custom Event - 'sortupdate'
1164                 */
1165                if (originElementIndex !== destinationElementIndex || originContainer !== destinationContainer) {
1166                    sortableElement.dispatchEvent(new CustomEvent('sortupdate', {
1167                        detail: {
1168                            origin: {
1169                                elementIndex: originElementIndex,
1170                                index: originIndex,
1171                                container: originContainer,
1172                                itemsBeforeUpdate: originItemsBeforeUpdate,
1173                                items: originItems
1174                            },
1175                            destination: {
1176                                index: destinationIndex,
1177                                elementIndex: destinationElementIndex,
1178                                container: destinationContainer,
1179                                itemsBeforeUpdate: destinationItemsBeforeUpdate,
1180                                items: destinationItems
1181                            },
1182                            item: dragging
1183                        }
1184                    }));
1185                }
1186            });
1187            var debouncedDragOverEnter = debounce(function (sortableElement, element, pageX, pageY) {
1188                if (!dragging) {
1189                    return;
1190                }
1191                // set placeholder height if forcePlaceholderSize option is set
1192                if (options.forcePlaceholderSize) {
1193                    store(sortableElement).placeholder.style.height = draggingHeight + 'px';
1194                    store(sortableElement).placeholder.style.width = draggingWidth + 'px';
1195                }
1196                // if element the draggedItem is dragged onto is within the array of all elements in list
1197                // (not only items, but also disabled, etc.)
1198                if (Array.from(sortableElement.children).indexOf(element) > -1) {
1199                    var thisHeight = getElementHeight(element);
1200                    var thisWidth = getElementWidth(element);
1201                    var placeholderIndex = getIndex(store(sortableElement).placeholder, element.parentElement.children);
1202                    var thisIndex = getIndex(element, element.parentElement.children);
1203                    // Check if `element` is bigger than the draggable. If it is, we have to define a dead zone to prevent flickering
1204                    if (thisHeight > draggingHeight || thisWidth > draggingWidth) {
1205                        // Dead zone?
1206                        var deadZoneVertical = thisHeight - draggingHeight;
1207                        var deadZoneHorizontal = thisWidth - draggingWidth;
1208                        var offsetTop = offset(element).top;
1209                        var offsetLeft = offset(element).left;
1210                        if (placeholderIndex < thisIndex &&
1211                            ((options.orientation === 'vertical' && pageY < offsetTop) ||
1212                                (options.orientation === 'horizontal' && pageX < offsetLeft))) {
1213                            return;
1214                        }
1215                        if (placeholderIndex > thisIndex &&
1216                            ((options.orientation === 'vertical' && pageY > offsetTop + thisHeight - deadZoneVertical) ||
1217                                (options.orientation === 'horizontal' && pageX > offsetLeft + thisWidth - deadZoneHorizontal))) {
1218                            return;
1219                        }
1220                    }
1221                    if (dragging.oldDisplay === undefined) {
1222                        dragging.oldDisplay = dragging.style.display;
1223                    }
1224                    if (dragging.style.display !== 'none') {
1225                        dragging.style.display = 'none';
1226                    }
1227                    // To avoid flicker, determine where to position the placeholder
1228                    // based on where the mouse pointer is relative to the elements
1229                    // vertical center.
1230                    var placeAfter = false;
1231                    try {
1232                        var elementMiddleVertical = offset(element).top + element.offsetHeight / 2;
1233                        var elementMiddleHorizontal = offset(element).left + element.offsetWidth / 2;
1234                        placeAfter = (options.orientation === 'vertical' && (pageY >= elementMiddleVertical)) ||
1235                            (options.orientation === 'horizontal' && (pageX >= elementMiddleHorizontal));
1236                    }
1237                    catch (e) {
1238                        placeAfter = placeholderIndex < thisIndex;
1239                    }
1240                    if (placeAfter) {
1241                        insertAfter(element, store(sortableElement).placeholder);
1242                    }
1243                    else {
1244                        insertBefore(element, store(sortableElement).placeholder);
1245                    }
1246                    // get placeholders from all stores & remove all but current one
1247                    Array.from(stores.values())
1248                        // remove empty values
1249                        .filter(function (data) { return data.placeholder !== undefined; })
1250                        // foreach placeholder in array if outside of current sorableContainer -> remove from DOM
1251                        .forEach(function (data) {
1252                            if (data.placeholder !== store(sortableElement).placeholder) {
1253                                data.placeholder.remove();
1254                            }
1255                        });
1256                }
1257                else {
1258                    // get all placeholders from store
1259                    var placeholders = Array.from(stores.values())
1260                        .filter(function (data) { return data.placeholder !== undefined; })
1261                        .map(function (data) {
1262                            return data.placeholder;
1263                        });
1264                    // check if element is not in placeholders
1265                    if (placeholders.indexOf(element) === -1 && sortableElement === element && !filter(element.children, options.items).length) {
1266                        placeholders.forEach(function (element) { return element.remove(); });
1267                        element.appendChild(store(sortableElement).placeholder);
1268                    }
1269                }
1270            }, options.debounce);
1271            // Handle dragover and dragenter events on draggable items
1272            var onDragOverEnter = function (e) {
1273                var element = e.target;
1274                var sortableElement = element.isSortable === true ? element : findSortable(element, e);
1275                element = findDragElement(sortableElement, element);
1276                if (!dragging || !listsConnected(sortableElement, dragging.parentElement) || addData(sortableElement, '_disabled') === 'true') {
1277                    return;
1278                }
1279                var options = addData(sortableElement, 'opts');
1280                if (parseInt(options.maxItems) && filter(sortableElement.children, addData(sortableElement, 'items')).length > parseInt(options.maxItems) && dragging.parentElement !== sortableElement) {
1281                    return;
1282                }
1283                e.preventDefault();
1284                e.stopPropagation();
1285                e.dataTransfer.dropEffect = store(sortableElement).getConfig('copy') === true ? 'copy' : 'move';
1286                debouncedDragOverEnter(sortableElement, element, e.pageX, e.pageY);
1287            };
1288            addEventListener(listItems.concat(sortableElement), 'dragover', onDragOverEnter);
1289            addEventListener(listItems.concat(sortableElement), 'dragenter', onDragOverEnter);
1290        });
1291        return sortableElements;
1292    }
1293    sortable.destroy = function (sortableElement) {
1294        destroySortable(sortableElement);
1295    };
1296    sortable.enable = function (sortableElement) {
1297        enableSortable(sortableElement);
1298    };
1299    sortable.disable = function (sortableElement) {
1300        disableSortable(sortableElement);
1301    };
1302    /* START.TESTS_ONLY */
1303    sortable.__testing = {
1304        // add internal methods here for testing purposes
1305        data: addData,
1306        removeItemEvents: removeItemEvents,
1307        removeItemData: removeItemData,
1308        removeSortableData: removeSortableData,
1309        removeContainerEvents: removeContainerEvents
1310    };
1311
1312    return sortable;
1313
1314}());
1315
1316export default class extends Controller {
1317    static targets = ["category"]
1318
1319    connect() {
1320        console.log("category sortable controller connected")
1321
1322        sortable('#sortable-category-list', {
1323        })[0].addEventListener('sortupdate', function (e) {
1324
1325            const { items } = sortable('#sortable-category-list', "serialize")[0]
1326            const category_ids = items.map(item => item.node.dataset.categoryId)
1327            const category_positions = items.map(item => item.index)
1328            console.log(category_positions)
1329            
1330            // Get the project ID from the current URL
1331            const projectId = window.location.pathname.split('/')[2] // /projects/:id/categories
1332            const url = `/projects/${projectId}/categories/update_position`
1333            
1334            fetch(url, {
1335                method: 'PATCH',
1336                headers: {
1337                    'Content-Type': 'application/json',
1338                    'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
1339                },
1340                body: JSON.stringify({ category_ids: category_ids, category_positions: category_positions })
1341            }).then(response => {
1342                console.log(response)
1343            }).catch(error => {
1344                alert(error)
1345            })
1346        })
1347    }
1348
1349    disconnect() {
1350        // Remove event listeners from each category element
1351
1352
1353    }
1354
1355
1356};

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.