PageSourceSearch

https://www.ekmd.de/assetm/@symfony/ux-autocomplete/controller-d817ce14a0bdb275bc009cc2b14b1a1c.js

js ekmd.de collected 2026-10-09 22:39:56 UTC 18,022 bytes, 410 lines download raw bytes

1import { Controller } from '@hotwired/stimulus';
2import TomSelect from 'tom-select';
3
4/******************************************************************************
5Copyright (c) Microsoft Corporation.
6
7Permission to use, copy, modify, and/or distribute this software for any
8purpose with or without fee is hereby granted.
9
10THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
15OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16PERFORMANCE OF THIS SOFTWARE.
17***************************************************************************** */
18/* global Reflect, Promise, SuppressedError, Symbol */
19
20
21function __classPrivateFieldGet(receiver, state, kind, f) {
22    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
23    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
24    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
25}
26
27typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
28    var e = new Error(message);
29    return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
30};
31
32var _default_1_instances, _default_1_getCommonConfig, _default_1_createAutocomplete, _default_1_createAutocompleteWithHtmlContents, _default_1_createAutocompleteWithRemoteData, _default_1_stripTags, _default_1_mergeConfigs, _default_1_normalizePluginsToHash, _default_1_createTomSelect;
33class default_1 extends Controller {
34    constructor() {
35        super(...arguments);
36        _default_1_instances.add(this);
37        this.isObserving = false;
38        this.hasLoadedChoicesPreviously = false;
39        this.originalOptions = [];
40        _default_1_normalizePluginsToHash.set(this, (plugins) => {
41            if (Array.isArray(plugins)) {
42                return plugins.reduce((acc, plugin) => {
43                    if (typeof plugin === 'string') {
44                        acc[plugin] = {};
45                    }
46                    if (typeof plugin === 'object' && plugin.name) {
47                        acc[plugin.name] = plugin.options || {};
48                    }
49                    return acc;
50                }, {});
51            }
52            return plugins;
53        });
54    }
55    initialize() {
56        if (!this.mutationObserver) {
57            this.mutationObserver = new MutationObserver((mutations) => {
58                this.onMutations(mutations);
59            });
60        }
61    }
62    connect() {
63        if (this.selectElement) {
64            this.originalOptions = this.createOptionsDataStructure(this.selectElement);
65        }
66        this.initializeTomSelect();
67    }
68    initializeTomSelect() {
69        if (this.selectElement) {
70            this.selectElement.setAttribute('data-skip-morph', '');
71        }
72        if (this.urlValue) {
73            this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithRemoteData).call(this, this.urlValue, this.hasMinCharactersValue ? this.minCharactersValue : null);
74            return;
75        }
76        if (this.optionsAsHtmlValue) {
77            this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocompleteWithHtmlContents).call(this);
78            return;
79        }
80        this.tomSelect = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createAutocomplete).call(this);
81        this.startMutationObserver();
82    }
83    disconnect() {
84        this.stopMutationObserver();
85        let currentSelectedValues = [];
86        if (this.selectElement) {
87            if (this.selectElement.multiple) {
88                currentSelectedValues = Array.from(this.selectElement.options)
89                    .filter((option) => option.selected)
90                    .map((option) => option.value);
91            }
92            else {
93                currentSelectedValues = [this.selectElement.value];
94            }
95        }
96        this.tomSelect.destroy();
97        if (this.selectElement) {
98            if (this.selectElement.multiple) {
99                Array.from(this.selectElement.options).forEach((option) => {
100                    option.selected = currentSelectedValues.includes(option.value);
101                });
102            }
103            else {
104                this.selectElement.value = currentSelectedValues[0];
105            }
106        }
107    }
108    urlValueChanged() {
109        this.resetTomSelect();
110    }
111    getMaxOptions() {
112        return this.selectElement ? this.selectElement.options.length : 50;
113    }
114    get selectElement() {
115        if (!(this.element instanceof HTMLSelectElement)) {
116            return null;
117        }
118        return this.element;
119    }
120    get formElement() {
121        if (!(this.element instanceof HTMLInputElement) && !(this.element instanceof HTMLSelectElement)) {
122            throw new Error('Autocomplete Stimulus controller can only be used on an <input> or <select>.');
123        }
124        return this.element;
125    }
126    dispatchEvent(name, payload) {
127        this.dispatch(name, { detail: payload, prefix: 'autocomplete' });
128    }
129    get preload() {
130        if (!this.hasPreloadValue) {
131            return 'focus';
132        }
133        if (this.preloadValue === 'false') {
134            return false;
135        }
136        if (this.preloadValue === 'true') {
137            return true;
138        }
139        return this.preloadValue;
140    }
141    resetTomSelect() {
142        if (this.tomSelect) {
143            this.dispatchEvent('before-reset', { tomSelect: this.tomSelect });
144            this.stopMutationObserver();
145            const currentHtml = this.element.innerHTML;
146            const currentValue = this.tomSelect.getValue();
147            this.tomSelect.destroy();
148            this.element.innerHTML = currentHtml;
149            this.initializeTomSelect();
150            this.tomSelect.setValue(currentValue);
151        }
152    }
153    changeTomSelectDisabledState(isDisabled) {
154        this.stopMutationObserver();
155        if (isDisabled) {
156            this.tomSelect.disable();
157        }
158        else {
159            this.tomSelect.enable();
160        }
161        this.startMutationObserver();
162    }
163    startMutationObserver() {
164        if (!this.isObserving && this.mutationObserver) {
165            this.mutationObserver.observe(this.element, {
166                childList: true,
167                subtree: true,
168                attributes: true,
169                characterData: true,
170                attributeOldValue: true,
171            });
172            this.isObserving = true;
173        }
174    }
175    stopMutationObserver() {
176        if (this.isObserving && this.mutationObserver) {
177            this.mutationObserver.disconnect();
178            this.isObserving = false;
179        }
180    }
181    onMutations(mutations) {
182        let changeDisabledState = false;
183        let requireReset = false;
184        mutations.forEach((mutation) => {
185            switch (mutation.type) {
186                case 'attributes':
187                    if (mutation.target === this.element && mutation.attributeName === 'disabled') {
188                        changeDisabledState = true;
189                        break;
190                    }
191                    if (mutation.target === this.element && mutation.attributeName === 'multiple') {
192                        const isNowMultiple = this.element.hasAttribute('multiple');
193                        const wasMultiple = mutation.oldValue === 'multiple';
194                        if (isNowMultiple !== wasMultiple) {
195                            requireReset = true;
196                        }
197                        break;
198                    }
199                    break;
200            }
201        });
202        const newOptions = this.selectElement ? this.createOptionsDataStructure(this.selectElement) : [];
203        const areOptionsEquivalent = this.areOptionsEquivalent(newOptions);
204        if (!areOptionsEquivalent || requireReset) {
205            this.originalOptions = newOptions;
206            this.resetTomSelect();
207        }
208        if (changeDisabledState) {
209            this.changeTomSelectDisabledState(this.formElement.disabled);
210        }
211    }
212    createOptionsDataStructure(selectElement) {
213        return Array.from(selectElement.options).map((option) => {
214            return {
215                value: option.value,
216                text: option.text,
217            };
218        });
219    }
220    areOptionsEquivalent(newOptions) {
221        const filteredOriginalOptions = this.originalOptions.filter((option) => option.value !== '');
222        const filteredNewOptions = newOptions.filter((option) => option.value !== '');
223        const originalPlaceholderOption = this.originalOptions.find((option) => option.value === '');
224        const newPlaceholderOption = newOptions.find((option) => option.value === '');
225        if (originalPlaceholderOption &&
226            newPlaceholderOption &&
227            originalPlaceholderOption.text !== newPlaceholderOption.text) {
228            return false;
229        }
230        if (filteredOriginalOptions.length !== filteredNewOptions.length) {
231            return false;
232        }
233        const normalizeOption = (option) => `${option.value}-${option.text}`;
234        const originalOptionsSet = new Set(filteredOriginalOptions.map(normalizeOption));
235        const newOptionsSet = new Set(filteredNewOptions.map(normalizeOption));
236        return (originalOptionsSet.size === newOptionsSet.size &&
237            [...originalOptionsSet].every((option) => newOptionsSet.has(option)));
238    }
239}
240_default_1_normalizePluginsToHash = new WeakMap(), _default_1_instances = new WeakSet(), _default_1_getCommonConfig = function _default_1_getCommonConfig() {
241    const plugins = {};
242    const isMultiple = !this.selectElement || this.selectElement.multiple;
243    if (!this.formElement.disabled && !isMultiple) {
244        plugins.clear_button = { title: '' };
245    }
246    if (isMultiple) {
247        plugins.remove_button = { title: '' };
248    }
249    if (this.urlValue) {
250        plugins.virtual_scroll = {};
251    }
252    const render = {
253        no_results: () => {
254            return `<div class="no-results">${this.noResultsFoundTextValue}</div>`;
255        },
256        option_create: (data, escapeData) => {
257            return `<div class="create">${this.createOptionTextValue.replace('%placeholder%', `<strong>${escapeData(data.input)}</strong>`)}</div>`;
258        },
259    };
260    const config = {
261        render,
262        plugins,
263        onItemAdd: () => {
264            this.tomSelect.setTextboxValue('');
265        },
266        closeAfterSelect: true,
267        onOptionAdd: (value, data) => {
268            let parentElement = this.tomSelect.input;
269            let optgroupData = null;
270            const optgroup = data[this.tomSelect.settings.optgroupField];
271            if (optgroup && this.tomSelect.optgroups) {
272                optgroupData = this.tomSelect.optgroups[optgroup];
273                if (optgroupData) {
274                    const optgroupElement = parentElement.querySelector(`optgroup[label="${optgroupData.label}"]`);
275                    if (optgroupElement) {
276                        parentElement = optgroupElement;
277                    }
278                }
279            }
280            const optionElement = document.createElement('option');
281            optionElement.value = value;
282            optionElement.text = data[this.tomSelect.settings.labelField];
283            const optionOrder = data.$order;
284            let orderedOption = null;
285            for (const [, tomSelectOption] of Object.entries(this.tomSelect.options)) {
286                if (tomSelectOption.$order === optionOrder) {
287                    orderedOption = parentElement.querySelector(`:scope > option[value="${CSS.escape(tomSelectOption[this.tomSelect.settings.valueField])}"]`);
288                    break;
289                }
290            }
291            if (orderedOption) {
292                orderedOption.insertAdjacentElement('afterend', optionElement);
293            }
294            else if (optionOrder >= 0) {
295                parentElement.append(optionElement);
296            }
297            else {
298                parentElement.prepend(optionElement);
299            }
300        },
301    };
302    if (!this.selectElement && !this.urlValue) {
303        config.shouldLoad = () => false;
304    }
305    return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, config, this.tomSelectOptionsValue);
306}, _default_1_createAutocomplete = function _default_1_createAutocomplete() {
307    const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this), {
308        maxOptions: this.getMaxOptions(),
309    });
310    return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config);
311}, _default_1_createAutocompleteWithHtmlContents = function _default_1_createAutocompleteWithHtmlContents() {
312    const commonConfig = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this);
313    const labelField = commonConfig.labelField ?? 'text';
314    const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, commonConfig, {
315        maxOptions: this.getMaxOptions(),
316        score: (search) => {
317            const scoringFunction = this.tomSelect.getScoreFunction(search);
318            return (item) => {
319                return scoringFunction({ ...item, text: __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_stripTags).call(this, item[labelField]) });
320            };
321        },
322        render: {
323            item: (item) => `<div>${item[labelField]}</div>`,
324            option: (item) => `<div>${item[labelField]}</div>`,
325        },
326    });
327    return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config);
328}, _default_1_createAutocompleteWithRemoteData = function _default_1_createAutocompleteWithRemoteData(autocompleteEndpointUrl, minCharacterLength) {
329    const commonConfig = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_getCommonConfig).call(this);
330    const labelField = commonConfig.labelField ?? 'text';
331    const config = __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_mergeConfigs).call(this, commonConfig, {
332        firstUrl: (query) => {
333            const separator = autocompleteEndpointUrl.includes('?') ? '&' : '?';
334            return `${autocompleteEndpointUrl}${separator}query=${encodeURIComponent(query)}`;
335        },
336        load: function (query, callback) {
337            const url = this.getUrl(query);
338            fetch(url)
339                .then((response) => response.json())
340                .then((json) => {
341                this.setNextUrl(query, json.next_page);
342                callback(json.results.options || json.results, json.results.optgroups || []);
343            })
344                .catch(() => callback([], []));
345        },
346        shouldLoad: (query) => {
347            if (null !== minCharacterLength) {
348                return query.length >= minCharacterLength;
349            }
350            if (this.hasLoadedChoicesPreviously) {
351                return true;
352            }
353            if (query.length > 0) {
354                this.hasLoadedChoicesPreviously = true;
355            }
356            return query.length >= 3;
357        },
358        optgroupField: 'group_by',
359        score: (search) => (item) => 1,
360        render: {
361            option: (item) => `<div>${item[labelField]}</div>`,
362            item: (item) => `<div>${item[labelField]}</div>`,
363            loading_more: () => {
364                return `<div class="loading-more-results">${this.loadingMoreTextValue}</div>`;
365            },
366            no_more_results: () => {
367                return `<div class="no-more-results">${this.noMoreResultsTextValue}</div>`;
368            },
369            no_results: () => {
370                return `<div class="no-results">${this.noResultsFoundTextValue}</div>`;
371            },
372            option_create: (data, escapeData) => {
373                return `<div class="create">${this.createOptionTextValue.replace('%placeholder%', `<strong>${escapeData(data.input)}</strong>`)}</div>`;
374            },
375        },
376        preload: this.preload,
377    });
378    return __classPrivateFieldGet(this, _default_1_instances, "m", _default_1_createTomSelect).call(this, config);
379}, _default_1_stripTags = function _default_1_stripTags(string) {
380    return string.replace(/(<([^>]+)>)/gi, '');
381}, _default_1_mergeConfigs = function _default_1_mergeConfigs(config1, config2) {
382    return {
383        ...config1,
384        ...config2,
385        plugins: {
386            ...__classPrivateFieldGet(this, _default_1_normalizePluginsToHash, "f").call(this, config1.plugins || {}),
387            ...__classPrivateFieldGet(this, _default_1_normalizePluginsToHash, "f").call(this, config2.plugins || {}),
388        },
389    };
390}, _default_1_createTomSelect = function _default_1_createTomSelect(options) {
391    const preConnectPayload = { options };
392    this.dispatchEvent('pre-connect', preConnectPayload);
393    const tomSelect = new TomSelect(this.formElement, options);
394    const connectPayload = { tomSelect, options };
395    this.dispatchEvent('connect', connectPayload);
396    return tomSelect;
397};
398default_1.values = {
399    url: String,
400    optionsAsHtml: Boolean,
401    loadingMoreText: String,
402    noResultsFoundText: String,
403    noMoreResultsText: String,
404    createOptionText: String,
405    minCharacters: Number,
406    tomSelectOptions: Object,
407    preload: String,
408};
409
410export { default_1 as default };

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.