PageSourceSearch

https://www.baja-mex.com/scripts/vanillaSelectBox.js

js baja-mex.com collected 2026-10-08 08:43:56 UTC 55,890 bytes, 1,359 lines download raw bytes

1/*
2Copyright (C) Philippe Meyer 2019-2021
3Distributed under the MIT License 
4
5vanillaSelectBox : v0.76 : New changeTree function : to rebuild the original tree with new data + correcting empty() function
6vanillaSelectBox : v0.75 : Remote search ready + local search modification : when a check on optgroup checks children only 
7                           if they not excluded from search.
8vanillaSelectBox : v0.72 : Remote search (WIP) bugfix [x] Select all duplicated
9vanillaSelectBox : v0.71 : Remote search (WIP) better code
10vanillaSelectBox : v0.70 : Remote search (WIP) for users to test
11vanillaSelectBox : v0.65 : Two levels: bug fix : groups are checked/unchecked when check all/uncheck all is clicked
12vanillaSelectBox : v0.64 : Two levels: groups are now checkable to check/uncheck the children options 
13vanillaSelectBox : v0.63 : Two levels: one click on the group selects / unselects children
14vanillaSelectBox : v0.62 : New option: maxOptionWidth set a maximum width for each option for narrow menus
15vanillaSelectBox : v0.61 : New option: maxSelect, set a maximum to the selectable options in a multiple choice menu
16vanillaSelectBox : v0.60 : Two levels: Optgroups are now used to show two level dropdowns 
17vanillaSelectBox : v0.59 : Bug fix : search box was overlapping first item in single selects
18vanillaSelectBox : v0.58 : Bug fixes
19vanillaSelectBox : v0.57 : Bug fix (minWidth option not honored)
20vanillaSelectBox : v0.56 : The multiselect checkboxes are a little smaller, maxWidth option is now working + added minWidth option as well
21                           The button has now a style attribute to protect its appearance 
22vanillaSelectBox : v0.55 : All attributes from the original select options are copied to the selectBox element
23vanillaSelectBox : v0.54 : if all the options of the select are selected by the user then the check all checkbox is checked
24vanillaSelectBox : v0.53 : if all the options of the select are selected then the check all checkbox is checked
25vanillaSelectBox : v0.52 : Better support of select('all') => command is consistent with checkbox and selecting / deselecting while searching select / uncheck only the found items
26vanillaSelectBox : v0.51 : Translations for select all/clear all + minor css corrections + don't select disabled items
27vanillaSelectBox : v0.50 : PR by jaguerra2017 adding a select all/clear all check button + optgroup support !
28vanillaSelectBox : v0.41 : Bug corrected, the menu content was misplaced if a css transform was applied on a parent
29vanillaSelectBox : v0.40 : A click on one selectBox close the other opened boxes
30vanillaSelectBox : v0.35 : You can enable and disable items
31vanillaSelectBox : v0.30 : The menu stops moving around on window resize and scroll + z-index in order of creation for multiple instances
32vanillaSelectBox : v0.26 : Corrected bug in stayOpen mode with disable() function
33vanillaSelectBox : v0.25 : New option stayOpen, and the dropbox is no longer a dropbox but a nice multi-select
34previous version : v0.24 : corrected bug affecting options with more than one class
35https://github.com/PhilippeMarcMeyer/vanillaSelectBox
36*/
37
38let VSBoxCounter = function () {
vendor: 46,061 bytes, lines 39-1189
39    let count = 0;
40    let instances = [];
41    return {
42        set: function (instancePtr) {
43            instances.push({ offset: ++count, ptr: instancePtr });
44            return instances[instances.length - 1].offset;
45        },
46        remove: function (instanceNr) {
47            let temp = instances.filter(function (x) {
48                return x.offset != instanceNr;
49            })
50            instances = temp.splice(0);
51        },
52        closeAllButMe: function (instanceNr) {
53            instances.forEach(function (x) {
54                if (x.offset != instanceNr) {
55                    x.ptr.closeOrder();
56                }
57            });
58        }
59    };
60}();
61
62function vanillaSelectBox(domSelector, options) {
63    let self = this;
64    this.instanceOffset = VSBoxCounter.set(self);
65    this.domSelector = domSelector;
66    this.root = document.querySelector(domSelector);
67    this.rootToken = null;
68    this.main;
69    this.button;
70    this.title;
71    this.isMultiple = this.root.hasAttribute("multiple");
72    this.multipleSize = this.isMultiple && this.root.hasAttribute("size") ? parseInt(this.root.getAttribute("size")) : -1;
73    this.isOptgroups = false;
74    this.currentOptgroup = 0;
75    this.drop;
76    this.top;
77    this.left;
78    this.options;
79    this.listElements;
80    this.isDisabled = false;
81    this.search = false;
82    this.searchZone = null;
83    this.inputBox = null;
84    this.disabledItems = [];
85    this.ulminWidth = 140;
86    this.ulmaxWidth = 280;
87    this.ulminHeight = 25;
88    this.maxOptionWidth = Infinity;
89    this.maxSelect = Infinity;
90    this.isInitRemote = false;
91    this.isSearchRemote = false;
92    this.onInit = null;
93    this.onSearch = null; // if isRemote is true : a user defined function that loads more options from the back
94    this.onInitSize = null;
95    this.forbidenAttributes = ["class", "selected", "disabled", "data-text", "data-value", "style"];
96    this.forbidenClasses = ["active", "disabled"];
97    this.userOptions = {
98        maxWidth: 500,
99        minWidth: -1,
100        maxHeight: 400,
101        translations: { "all": "All", "items": "items", "selectAll": "Select All", "clearAll": "Clear All" },
102        search: false,
103        placeHolder: "",
104        stayOpen: false,
105        disableSelectAll: false
106    }
107    if (options) {
108        if (options.maxWidth != undefined) {
109            this.userOptions.maxWidth = options.maxWidth;
110        }
111        if (options.minWidth != undefined) {
112            this.userOptions.minWidth = options.minWidth;
113        }
114        if (options.maxHeight != undefined) {
115            this.userOptions.maxHeight = options.maxHeight;
116        }
117        if (options.translations != undefined) {
118            for (var property in options.translations) {
119                if (options.translations.hasOwnProperty(property)) {
120                    if (this.userOptions.translations[property]) {
121                        this.userOptions.translations[property] = options.translations[property];
122                    }
123                }
124            }
125        }
126        if (options.placeHolder != undefined) {
127            this.userOptions.placeHolder = options.placeHolder;
128        }
129        if (options.search != undefined) {
130            this.search = options.search;
131        }
132        if (options.remote != undefined && options.remote) {
133
134           // user defined onInit  function
135            if (options.remote.onInit!= undefined && typeof options.remote.onInit === 'function') {
136                this.onInit = options.remote.onInit;
137                this.isInitRemote = true;
138            } 
139            if (options.remote.onInitSize != undefined) {
140                this.onInitSize = options.remote.onInitSize;
141                if (this.onInitSize < 3) this.onInitSize = 3;
142            }
143            // user defined remote search function
144            if (options.remote.onSearch != undefined && typeof options.remote.onSearch === 'function') {
145                this.onSearch = options.remote.onSearch;
146                this.isSearchRemote = true;
147            }
148        }
149
150        if (options.stayOpen != undefined) {
151            this.userOptions.stayOpen = options.stayOpen;
152        }
153
154        if (options.disableSelectAll != undefined) {
155            this.userOptions.disableSelectAll = options.disableSelectAll;
156        }
157
158        if (options.maxSelect != undefined && !isNaN(options.maxSelect) && options.maxSelect >= 1) {
159            this.maxSelect = options.maxSelect;
160            this.userOptions.disableSelectAll = true;
161        }
162
163        if (options.maxOptionWidth != undefined && !isNaN(options.maxOptionWidth) && options.maxOptionWidth >= 20) {
164            this.maxOptionWidth = options.maxOptionWidth;
165            this.ulminWidth = options.maxOptionWidth + 60;
166            this.ulmaxWidth = options.maxOptionWidth + 60;
167        }
168    }
169
170    this.closeOrder = function () {
171        let self = this;
172        if (!self.userOptions.stayOpen) {
173            self.drop.style.visibility = "hidden";
174            if (self.search) {
175                self.inputBox.value = "";
176                Array.prototype.slice.call(self.listElements).forEach(function (x) {
177                    x.classList.remove("hide");
178                });
179            }
180        }
181    }
182
183    this.getCssArray = function (selector) {
184        // Why inline css ? To protect the button display from foreign css files
185        let cssArray = [];
186        if (selector === ".vsb-main button") {
187            cssArray = [
188                { "key": "min-width", "value": "120px" },
189                { "key": "border-radius", "value": "0" },
190                { "key": "width", "value": "100%" },
191                { "key": "text-align", "value": "left" },
192                { "key": "z-index", "value": "1" },
193                { "key": "color", "value": "#333" },
194                { "key": "background", "value": "white !important" },
195                { "key": "border", "value": "1px solid #999 !important" },
196                { "key": "line-height", "value": "20px" },
197                { "key": "font-size", "value": "14px" },
198                { "key": "padding", "value": "6px 12px" }
199            ]
200        }
201
202        return cssArrayToString(cssArray);
203
204        function cssArrayToString(cssList) {
205            let list = "";
206            cssList.forEach(function (x) {
207                list += x.key + ":" + x.value + ";";
208            });
209            return list;
210        }
211    }
212
213    this.init = function () {
214        let self = this;
215        if (self.isInitRemote) {
216            self.onInit("",self.onInitSize)
217                .then(function (data) {
218                    self.buildSelect(data);
219                    self.createTree();
220                });
221        } else {
222            self.createTree();
223        }
224    }
225
226    this.createTree = function () {
227
228        this.rootToken = self.domSelector.replace(/[^A-Za-z0-9]+/, "")
229        this.root.style.display = "none";
230        let already = document.getElementById("btn-group-" + this.rootToken);
231        if (already) {
232            already.remove();
233        }
234        this.main = document.createElement("div");
235        this.root.parentNode.insertBefore(this.main, this.root.nextSibling);
236        this.main.classList.add("vsb-main");
237        this.main.setAttribute("id", "btn-group-" + this.rootToken);
238        this.main.style.marginLeft = this.main.style.marginLeft;
239        if (self.userOptions.stayOpen) {
240            this.main.style.minHeight = (this.userOptions.maxHeight + 10) + "px";
241        }
242
243        if (self.userOptions.stayOpen) {
244            this.button = document.createElement("div");
245        } else {
246            this.button = document.createElement("button");
247            var cssList = self.getCssArray(".vsb-main button");
248            this.button.setAttribute("style", cssList);
249        }
250        this.button.style.maxWidth = this.userOptions.maxWidth + "px";
251        if (this.userOptions.minWidth !== -1) {
252            this.button.style.minWidth = this.userOptions.minWidth + "px";
253        }
254
255        this.main.appendChild(this.button);
256        this.title = document.createElement("span");
257        this.button.appendChild(this.title);
258        this.title.classList.add("title");
259        let caret = document.createElement("span");
260        this.button.appendChild(caret);
261
262        caret.classList.add("caret");
263        caret.style.position = "absolute";
264        caret.style.right = "8px";
265        caret.style.marginTop = "8px";
266
267        if (self.userOptions.stayOpen) {
268            caret.style.display = "none";
269            this.title.style.paddingLeft = "20px";
270            this.title.style.fontStyle = "italic";
271            this.title.style.verticalAlign = "20%";
272        }
273
274        this.drop = document.createElement("div");
275        this.main.appendChild(this.drop);
276        this.drop.classList.add("vsb-menu");
277        this.drop.style.zIndex = 2000 - this.instanceOffset;
278        this.ul = document.createElement("ul");
279        this.drop.appendChild(this.ul);
280
281        this.ul.style.maxHeight = this.userOptions.maxHeight + "px";
282        this.ul.style.minWidth = this.ulminWidth + "px";
283        this.ul.style.maxWidth = this.ulmaxWidth + "px";
284        this.ul.style.minHeight = this.ulminHeight + "px";
285        if (this.isMultiple) {
286            this.ul.classList.add("multi");
287            if (!self.userOptions.disableSelectAll) {
288                let selectAll = document.createElement("option");
289                selectAll.setAttribute("value", 'all');
290                selectAll.innerText = self.userOptions.translations.selectAll;
291                this.root.insertBefore(selectAll, (this.root.hasChildNodes())
292                    ? this.root.childNodes[0]
293                    : null);
294            }
295        }
296        let selectedTexts = ""
297        let sep = "";
298        let nrActives = 0;
299
300        if (this.search) {
301            this.searchZone = document.createElement("div");
302            this.ul.appendChild(this.searchZone);
303            this.searchZone.classList.add("vsb-js-search-zone");
304            this.searchZone.style.zIndex = 2001 - this.instanceOffset;
305            this.inputBox = document.createElement("input");
306            this.searchZone.appendChild(this.inputBox);
307            this.inputBox.setAttribute("type", "text");
308            this.inputBox.setAttribute("id", "search_" + this.rootToken);
309            if (this.maxOptionWidth < Infinity) {
310                this.searchZone.style.maxWidth = self.maxOptionWidth + 30 + "px";
311                this.inputBox.style.maxWidth = self.maxOptionWidth + 30 + "px";
312            }
313
314            var para = document.createElement("p");
315            this.ul.appendChild(para);
316            para.style.fontSize = "12px";
317            para.innerHTML = "&nbsp;";
318            this.ul.addEventListener("scroll", function (e) {
319                var y = this.scrollTop;
320                self.searchZone.parentNode.style.top = y + "px";
321            });
322        }
323
324        this.options = document.querySelectorAll(this.domSelector + " > option");
325        Array.prototype.slice.call(this.options).forEach(function (x) {
326            let text = x.textContent;
327            let value = x.value;
328            let originalAttrs;
329            if (x.hasAttributes()) {
330                originalAttrs = Array.prototype.slice.call(x.attributes)
331                    .filter(function (a) {
332                        return self.forbidenAttributes.indexOf(a.name) === -1
333                    });
334            }
335            let classes = x.getAttribute("class");
336            if (classes) {
337                classes = classes
338                    .split(" ")
339                    .filter(function (c) {
340                        return self.forbidenClasses.indexOf(c) === -1
341                    });
342            } else {
343                classes = [];
344            }
345            let li = document.createElement("li");
346            let isSelected = x.hasAttribute("selected");
347            let isDisabled = x.hasAttribute("disabled");
348
349            self.ul.appendChild(li);
350            li.setAttribute("data-value", value);
351            li.setAttribute("data-text", text);
352
353            if (originalAttrs !== undefined) {
354                originalAttrs.forEach(function (a) {
355                    li.setAttribute(a.name, a.value);
356                });
357            }
358
359            classes.forEach(function (x) {
360                li.classList.add(x);
361            });
362
363            if (self.maxOptionWidth < Infinity) {
364                li.classList.add("short");
365                li.style.maxWidth = self.maxOptionWidth + "px";
366            }
367
368            if (isSelected) {
369                nrActives++;
370                selectedTexts += sep + text;
371                sep = ",";
372                li.classList.add("active");
373                if (!self.isMultiple) {
374                    self.title.textContent = text;
375                    if (classes.length != 0) {
376                        classes.forEach(function (x) {
377                            self.title.classList.add(x);
378                        });
379                    }
380                }
381            }
382            if (isDisabled) {
383                li.classList.add("disabled");
384            }
385            li.appendChild(document.createTextNode(" " + text));
386        });
387
388        if (document.querySelector(self.domSelector + ' optgroup') !== null) {
389            self.isOptgroups = true;
390            self.options = document.querySelectorAll(self.domSelector + " option");
391            let groups = document.querySelectorAll(self.domSelector + ' optgroup');
392            Array.prototype.slice.call(groups).forEach(function (group) {
393                let groupOptions = group.querySelectorAll('option');
394                let li = document.createElement("li");
395                let span = document.createElement("span");
396                let iCheck = document.createElement("i");
397                let labelElement = document.createElement("b");
398                let dataWay = group.getAttribute("data-way");
399                if (!dataWay) dataWay = "closed";
400                if (!dataWay || (dataWay !== "closed" && dataWay !== "open")) dataWay = "closed";
401                li.appendChild(span);
402                li.appendChild(iCheck);
403                self.ul.appendChild(li);
404                li.classList.add('grouped-option');
405                li.classList.add(dataWay);
406                self.currentOptgroup++;
407                let optId = self.rootToken + "-opt-" + self.currentOptgroup;
408                li.id = optId;
409                li.appendChild(labelElement);
410                labelElement.appendChild(document.createTextNode(group.label));
411                li.setAttribute("data-text", group.label);
412                self.ul.appendChild(li);
413
414                Array.prototype.slice.call(groupOptions).forEach(function (x) {
415                    let text = x.textContent;
416                    let value = x.value;
417                    let classes = x.getAttribute("class");
418                    if (classes) {
419                        classes = classes.split(" ");
420                    }
421                    else {
422                        classes = [];
423                    }
424                    classes.push(dataWay);
425                    let li = document.createElement("li");
426                    let isSelected = x.hasAttribute("selected");
427                    self.ul.appendChild(li);
428                    li.setAttribute("data-value", value);
429                    li.setAttribute("data-text", text);
430                    li.setAttribute("data-parent", optId);
431                    if (classes.length != 0) {
432                        classes.forEach(function (x) {
433                            li.classList.add(x);
434                        });
435                    }
436                    if (isSelected) {
437                        nrActives++;
438                        selectedTexts += sep + text;
439                        sep = ",";
440                        li.classList.add("active");
441                        if (!self.isMultiple) {
442                            self.title.textContent = text;
443                            if (classes.length != 0) {
444                                classes.forEach(function (x) {
445                                    self.title.classList.add(x);
446                                });
447                            }
448                        }
449                    }
450                    li.appendChild(document.createTextNode(text));
451                })
452            })
453        }
454
455        let optionsLength = self.options.length - Number(!self.userOptions.disableSelectAll);
456
457        if (optionsLength == nrActives) { // Bastoune idea to preserve the placeholder
458            let wordForAll = self.userOptions.translations.all || "all";
459            selectedTexts = wordForAll;
460        } else if (self.multipleSize != -1) {
461            if (nrActives > self.multipleSize) {
462                let wordForItems = self.userOptions.translations.items || "items"
463                selectedTexts = nrActives + " " + wordForItems;
464            }
465        }
466        if (self.isMultiple) {
467            self.title.innerHTML = selectedTexts;
468        }
469        if (self.userOptions.placeHolder != "" && self.title.textContent == "") {
470            self.title.textContent = self.userOptions.placeHolder;
471        }
472        self.listElements = self.drop.querySelectorAll("li:not(.grouped-option)");
473        if (self.search) {
474            self.inputBox.addEventListener("keyup", function (e) {
475                let searchValue = e.target.value.toUpperCase();
476                let searchValueLength = searchValue.length;
477                let nrFound = 0;
478                let nrChecked = 0;
479                let selectAll = null;
480                if (self.isSearchRemote) {
481                    if (searchValueLength == 0) {
482                        self.remoteSearchIntegrate(null);
483                    } else if (searchValueLength >= 3) {
484                        self.onSearch(searchValue)
485                            .then(function (data) {
486                                self.remoteSearchIntegrate(data);
487                            });
488                    }
489                } else {
490                    if (searchValueLength < 3) {
491                        Array.prototype.slice.call(self.listElements).forEach(function (x) {
492                            if (x.getAttribute('data-value') === 'all') {
493                                selectAll = x;
494                            } else {
495                                x.classList.remove("hidden-search");
496                                nrFound++;
497                                nrChecked += x.classList.contains('active');
498                            }
499                        });
500                    } else {
501                        Array.prototype.slice.call(self.listElements).forEach(function (x) {
502                            if (x.getAttribute('data-value') !== 'all') {
503                                let text = x.getAttribute("data-text").toUpperCase();
504                                if (text.indexOf(searchValue) === -1 && x.getAttribute('data-value') !== 'all') {
505                                    x.classList.add("hidden-search");
506                                } else {
507                                    nrFound++;
508                                    x.classList.remove("hidden-search");
509                                    nrChecked += x.classList.contains('active');
510                                }
511                            } else {
512                                selectAll = x;
513                            }
514                        });
515                    }
516                    if (selectAll) {
517                        if (nrFound === 0) {
518                            selectAll.classList.add('disabled');
519                        } else {
520                            selectAll.classList.remove('disabled');
521                        }
522                        if (nrChecked !== nrFound) {
523                            selectAll.classList.remove("active");
524                            selectAll.innerText = self.userOptions.translations.selectAll;
525                            selectAll.setAttribute('data-selected', 'false')
526                        } else {
527                            selectAll.classList.add("active");
528                            selectAll.innerText = self.userOptions.translations.clearAll;
529                            selectAll.setAttribute('data-selected', 'true')
530                        }
531                    }
532                }
533            }); 
534        }
535
536        if (self.userOptions.stayOpen) {
537            self.drop.style.visibility = "visible";
538            self.drop.style.boxShadow = "none";
539            self.drop.style.minHeight = (this.userOptions.maxHeight + 10) + "px";
540            self.drop.style.position = "relative";
541            self.drop.style.left = "0px";
542            self.drop.style.top = "0px";
543            self.button.style.border = "none";
544        } else {
545            this.main.addEventListener("click", function (e) {
546                if (self.isDisabled) return;
547                self.drop.style.left = self.left + "px";
548                self.drop.style.top = self.top + "px";
549                self.drop.style.visibility = "visible";
550                document.addEventListener("click", docListener);
551                e.preventDefault();
552                e.stopPropagation();
553                if (!self.userOptions.stayOpen) {
554                    VSBoxCounter.closeAllButMe(self.instanceOffset);
555                }
556            });
557        }
558
559        this.drop.addEventListener("click", function (e) {
560            if (self.isDisabled) return;
561            if (e.target.tagName === 'INPUT') return;
562            let isShowHideCommand = e.target.tagName === 'SPAN';
563            let isCheckCommand = e.target.tagName === 'I';
564            let liClicked = e.target.parentElement;
565            if (!liClicked.hasAttribute("data-value")) {
566                if (liClicked.classList.contains("grouped-option")) {
567                    if (!isShowHideCommand && !isCheckCommand) return;
568                    let oldClass, newClass;
569                    if (isCheckCommand) { // check or uncheck children
570                        self.checkUncheckFromParent(liClicked);
571                    } else { //open or close
572                        if (liClicked.classList.contains("open")) {
573                            oldClass = "open"
574                            newClass = "closed"
575                        } else {
576                            oldClass = "closed"
577                            newClass = "open"
578                        }
579                        liClicked.classList.remove(oldClass);
580                        liClicked.classList.add(newClass);
581                        let theChildren = self.drop.querySelectorAll("[data-parent='" + liClicked.id + "']");
582                        theChildren.forEach(function (x) {
583                            x.classList.remove(oldClass);
584                            x.classList.add(newClass);
585                        })
586                    }
587                    return;
588                }
589            }
590            let choiceValue = e.target.getAttribute("data-value");
591            let choiceText = e.target.getAttribute("data-text");
592            let className = e.target.getAttribute("class");
593
594            if (className && className.indexOf("disabled") != -1) {
595                return;
596            }
597
598            if (className && className.indexOf("overflow") != -1) {
599                return;
600            }
601
602            if (choiceValue === 'all') {
603                if (e.target.hasAttribute('data-selected')
604                    && e.target.getAttribute('data-selected') === 'true') {
605                    self.setValue('none')
606                } else {
607                    self.setValue('all');
608                }
609                return;
610            }
611
612            if (!self.isMultiple) {
613                self.root.value = choiceValue;
614                self.title.textContent = choiceText;
615                if (className) {
616                    self.title.setAttribute("class", className + " title");
617                } else {
618                    self.title.setAttribute("class", "title");
619                }
620                Array.prototype.slice.call(self.listElements).forEach(function (x) {
621                    x.classList.remove("active");
622                });
623                if (choiceText != "") {
624                    e.target.classList.add("active");
625                }
626                self.privateSendChange();
627                if (!self.userOptions.stayOpen) {
628                    docListener();
629                }
630            } else {
631                let wasActive = false;
632                if (className) {
633                    wasActive = className.indexOf("active") != -1;
634                }
635                if (wasActive) {
636                    e.target.classList.remove("active");
637                } else {
638                    e.target.classList.add("active");
639                }
640                if (e.target.hasAttribute("data-parent")) {
641                    self.checkUncheckFromChild(e.target);
642                }
643
644                let selectedTexts = ""
645                let sep = "";
646                let nrActives = 0;
647                let nrAll = 0;
648                for (let i = 0; i < self.options.length; i++) {
649                    nrAll++;
650                    if (self.options[i].value == choiceValue) {
651                        self.options[i].selected = !wasActive;
652                    }
653                    if (self.options[i].selected) {
654                        nrActives++;
655                        selectedTexts += sep + self.options[i].textContent;
656                        sep = ",";
657                    }
658                }
659                if (nrAll == nrActives - Number(!self.userOptions.disableSelectAll)) {
660                    let wordForAll = self.userOptions.translations.all || "all";
661                    selectedTexts = wordForAll;
662                } else if (self.multipleSize != -1) {
663                    if (nrActives > self.multipleSize) {
664                        let wordForItems = self.userOptions.translations.items || "items"
665                        selectedTexts = nrActives + " " + wordForItems;
666                    }
667                }
668                self.title.textContent = selectedTexts;
669                self.checkSelectMax(nrActives);
670                self.checkUncheckAll();
671                self.privateSendChange();
672            }
673            e.preventDefault();
674            e.stopPropagation();
675            if (self.userOptions.placeHolder != "" && self.title.textContent == "") {
676                self.title.textContent = self.userOptions.placeHolder;
677            }
678        });
679        function docListener() {
680            document.removeEventListener("click", docListener);
681            self.drop.style.visibility = "hidden";
682            if (self.search) {
683                self.inputBox.value = "";
684                Array.prototype.slice.call(self.listElements).forEach(function (x) {
685                    x.classList.remove("hidden-search");
686                });
687            }
688        }
689    }
690    this.init();
691    this.checkUncheckAll();
692}
693
694vanillaSelectBox.prototype.buildSelect = function (data) {
695    let self = this;
696    if(data == null || data.length < 1) return;
697    if(!self.isOptgroups){
698        self.isOptgroups = data[0].parent != undefined && data[0].parent != "";
699    }
700  
701    if(self.isOptgroups){
702        let groups = {};
703        data = data.filter(function(x){
704            return x.parent != undefined && x.parent != "";
705        });
706    
707        data.forEach(function (x) {
708            if(!groups[x.parent]){
709                groups[x.parent] = true;
710            }
711        });
712        for (let group in groups) {
713            let anOptgroup = document.createElement("optgroup");
714            anOptgroup.setAttribute("label", group);
715            
716            options = data.filter(function(x){
717                return x.parent == group;
718            });
719            options.forEach(function (x) {
720                let anOption = document.createElement("option");
721                anOption.value = x.value;
722                anOption.text = x.text;
723                if(x.selected){
724                    anOption.setAttribute("selected",true)
725                }
726                anOptgroup.appendChild(anOption);
727            });
728            self.root.appendChild(anOptgroup);
729        }
730    }else{
731        data.forEach(function (x) {
732            let anOption = document.createElement("option");
733            anOption.value = x.value;
734            anOption.text = x.text;
735            if(x.selected){
736                anOption.setAttribute("selected",true)
737            }
738            self.root.appendChild(anOption);
739        });
740    }
741}
742
743vanillaSelectBox.prototype.remoteSearchIntegrate = function (data) {
744    let self = this;
745
746    if (data == null || data.length == 0) {
747        let dataChecked = self.optionsCheckedToData();
748        if(dataChecked)
749            data = dataChecked.slice(0);
750        self.remoteSearchIntegrateIt(data);
751    } else {
752        let dataChecked = self.optionsCheckedToData();
753        if (dataChecked.length > 0){
754            for (var i = data.length - 1; i >= 0; i--) {
755                if(dataChecked.indexOf(data[i].id) !=-1){
756                    data.slice(i,1);
757                }
758            }
759        }
760        data = data.concat(dataChecked);
761
762        self.remoteSearchIntegrateIt(data);
763    }
764}
765
766vanillaSelectBox.prototype.optionsCheckedToData = function () {
767    let self = this;
768    let dataChecked = [];
769    let treeOptions = self.ul.querySelectorAll("li.active:not(.grouped-option)");
770    let keepParents = {};
771        if (treeOptions) {
772            Array.prototype.slice.call(treeOptions).forEach(function (x) {
773                let oneData = {"value":x.getAttribute("data-value"),"text":x.getAttribute("data-text"),"selected":true};
774                if(oneData.value !== "all"){
775                    if(self.isOptgroups){
776                        let parentId = x.getAttribute("data-parent");
777                        if(keepParents[parentId]!=undefined){
778                            oneData.parent = keepParents[parentId];
779                        }else{
780                            let parentPtr = self.ul.querySelector("#"+parentId);
781                            let parentName = parentPtr.getAttribute("data-text");
782                            keepParents[parentId] = parentName;
783                            oneData.parent = parentName;
784                        }
785                    }
786                    dataChecked.push(oneData);
787                }
788            });
789        }
790        return dataChecked;
791}
792
793vanillaSelectBox.prototype.removeOptionsNotChecked = function (data) {
794    let self = this;
795    let minimumSize = self.onInitSize;
796    let newSearchSize = data == null ? 0 : data.length;
797    let presentSize = self.root.length;
798    if (presentSize + newSearchSize > minimumSize) {
799        let maxToRemove = presentSize + newSearchSize - minimumSize - 1;
800        let removed = 0;
801        for (var i = self.root.length - 1; i >= 0; i--) {
802            if (self.root.options[i].selected == false) {
803                if (removed <= maxToRemove) {
804                    removed++;
805                    self.root.remove(i);
806                }
807            }
808        }
809    }
810}
811
812vanillaSelectBox.prototype.changeTree = function (data, options) {
813    let self = this;
814    self.empty();
815    self.remoteSearchIntegrateIt(data);
816    if (options && options.onSearch) {
817        if (typeof options.onSearch === 'function') {
818            self.onSearch = options.onSearch;
819            self.isSearchRemote = true;
820        }
821    }
822    self.listElements = this.drop.querySelectorAll("li:not(.grouped-option)");
823}
824
825vanillaSelectBox.prototype.remoteSearchIntegrateIt = function (data) {
826    let self = this;
827    if (data == null || data.length == 0) return;
828    while(self.root.firstChild)
829    self.root.removeChild(self.root.firstChild);
830    
831    self.buildSelect(data);
832    self.reloadTree();
833}
834
835vanillaSelectBox.prototype.reloadTree = function () {
836    let self = this;
837    let lis = self.ul.querySelectorAll("li");
838    if (lis != null) {
839        for (var i = lis.length - 1; i >= 0; i--) {
840            if (lis[i].getAttribute('data-value') !== 'all') {
841                self.ul.removeChild(lis[i]);
842            }
843        }
844    }
845
846    let selectedTexts = ""
847    let sep = "";
848    let nrActives = 0;
849    let nrAll = 0;
850
851    if (self.isOptgroups) {
852        if (document.querySelector(self.domSelector + ' optgroup') !== null) {
853            self.options = document.querySelectorAll(this.domSelector + " option");
854            let groups = document.querySelectorAll(this.domSelector + ' optgroup');
855            Array.prototype.slice.call(groups).forEach(function (group) {
856                let groupOptions = group.querySelectorAll('option');
857                let li = document.createElement("li");
858                let span = document.createElement("span");
859                let iCheck = document.createElement("i");
860                let labelElement = document.createElement("b");
861                let dataWay = group.getAttribute("data-way");
862                if (!dataWay) dataWay = "closed";
863                if (!dataWay || (dataWay !== "closed" && dataWay !== "open")) dataWay = "closed";
864                li.appendChild(span);
865                li.appendChild(iCheck);
866                self.ul.appendChild(li);
867                li.classList.add('grouped-option');
868                li.classList.add(dataWay);
869                self.currentOptgroup++;
870                let optId = self.rootToken + "-opt-" + self.currentOptgroup;
871                li.id = optId;
872                li.appendChild(labelElement);
873                labelElement.appendChild(document.createTextNode(group.label));
874                li.setAttribute("data-text", group.label);
875                self.ul.appendChild(li);
876
877                Array.prototype.slice.call(groupOptions).forEach(function (x) {
878                    let text = x.textContent;
879                    let value = x.value;
880                    let classes = x.getAttribute("class");
881                    if (classes) {
882                        classes = classes.split(" ");
883                    }
884                    else {
885                        classes = [];
886                    }
887                    classes.push(dataWay);
888                    let li = document.createElement("li");
889                    let isSelected = x.hasAttribute("selected");
890                    self.ul.appendChild(li);
891                    li.setAttribute("data-value", value);
892                    li.setAttribute("data-text", text);
893                    li.setAttribute("data-parent", optId);
894                    if (classes.length != 0) {
895                        classes.forEach(function (x) {
896                            li.classList.add(x);
897                        });
898                    }
899                    if (isSelected) {
900                        nrActives++;
901                        selectedTexts += sep + text;
902                        sep = ",";
903                        li.classList.add("active");
904                        if (!self.isMultiple) {
905                            self.title.textContent = text;
906                            if (classes.length != 0) {
907                                classes.forEach(function (x) {
908                                    self.title.classList.add(x);
909                                });
910                            }
911                        }
912                    }
913                    li.appendChild(document.createTextNode(text));
914                })
915            })
916        }
917        self.listElements = this.drop.querySelectorAll("li:not(.grouped-option)");
918    } else {
919        self.options = self.root.querySelectorAll("option");
920        Array.prototype.slice.call(self.options).forEach(function (x) {
921            let text = x.textContent;
922            let value = x.value;
923            if (value != "all") {
924                let originalAttrs;
925                if (x.hasAttributes()) {
926                    originalAttrs = Array.prototype.slice.call(x.attributes)
927                        .filter(function (a) {
928                            return self.forbidenAttributes.indexOf(a.name) === -1
929                        });
930                }
931                let classes = x.getAttribute("class");
932                if (classes) {
933                    classes = classes
934                        .split(" ")
935                        .filter(function (c) {
936                            return self.forbidenClasses.indexOf(c) === -1
937                        });
938                } else {
939                    classes = [];
940                }
941                let li = document.createElement("li");
942                let isSelected = x.hasAttribute("selected");
943
944                let isDisabled = x.disabled;
945
946                self.ul.appendChild(li);
947                li.setAttribute("data-value", value);
948                li.setAttribute("data-text", text);
949
950                if (originalAttrs !== undefined) {
951                    originalAttrs.forEach(function (a) {
952                        li.setAttribute(a.name, a.value);
953                    });
954                }
955
956                classes.forEach(function (x) {
957                    li.classList.add(x);
958                });
959
960                if (self.maxOptionWidth < Infinity) {
961                    li.classList.add("short");
962                    li.style.maxWidth = self.maxOptionWidth + "px";
963                }
964
965                if (isSelected) {
966                    nrActives++;
967                    selectedTexts += sep + text;
968                    sep = ",";
969                    li.classList.add("active");
970                    if (!self.isMultiple) {
971                        self.title.textContent = text;
972                        if (classes.length != 0) {
973                            classes.forEach(function (x) {
974                                self.title.classList.add(x);
975                            });
976                        }
977                    }
978                }
979                if (isDisabled) {
980                    li.classList.add("disabled");
981                }
982                li.appendChild(document.createTextNode(" " + text));
983            }
984        });
985    }
986
987}
988
989vanillaSelectBox.prototype.disableItems = function (values) {
990    let self = this;
991    let foundValues = [];
992    if (vanillaSelectBox_type(values) == "string") {
993        values = values.split(",");
994    }
995
996    if (vanillaSelectBox_type(values) == "array") {
997        Array.prototype.slice.call(self.options).forEach(function (x) {
998            if (values.indexOf(x.value) != -1) {
999                foundValues.push(x.value);
1000                x.setAttribute("disabled", "");
1001            }
1002        });
1003    }
1004    Array.prototype.slice.call(self.listElements).forEach(function (x) {
1005        let val = x.getAttribute("data-value");
1006        if (foundValues.indexOf(val) != -1) {
1007            x.classList.add("disabled");
1008        }
1009    });
1010}
1011
1012vanillaSelectBox.prototype.enableItems = function (values) {
1013    let self = this;
1014    let foundValues = [];
1015    if (vanillaSelectBox_type(values) == "string") {
1016        values = values.split(",");
1017    }
1018
1019    if (vanillaSelectBox_type(values) == "array") {
1020        Array.prototype.slice.call(self.options).forEach(function (x) {
1021            if (values.indexOf(x.value) != -1) {
1022                foundValues.push(x.value);
1023                x.removeAttribute("disabled");
1024            }
1025        });
1026    }
1027
1028    Array.prototype.slice.call(self.listElements).forEach(function (x) {
1029        if (foundValues.indexOf(x.getAttribute("data-value")) != -1) {
1030            x.classList.remove("disabled");
1031        }
1032    });
1033}
1034
1035vanillaSelectBox.prototype.checkSelectMax = function (nrActives) {
1036    let self = this;
1037    if (self.maxSelect == Infinity || !self.isMultiple) return;
1038    if (self.maxSelect <= nrActives) {
1039        Array.prototype.slice.call(self.listElements).forEach(function (x) {
1040            if (x.hasAttribute('data-value')) {
1041                if (!x.classList.contains('disabled') && !x.classList.contains('active')) {
1042                    x.classList.add("overflow");
1043                }
1044            }
1045        });
1046    } else {
1047        Array.prototype.slice.call(self.listElements).forEach(function (x) {
1048            if (x.classList.contains('overflow')) {
1049                x.classList.remove("overflow");
1050            }
1051        });
1052    }
1053}
1054
1055vanillaSelectBox.prototype.checkUncheckFromChild = function (liClicked) {
1056    let self = this;
1057    let parentId = liClicked.getAttribute('data-parent');
1058    let parentLi = document.getElementById(parentId);
1059    if (!self.isMultiple) return;
1060    let listElements = self.drop.querySelectorAll("li");
1061    let childrenElements = Array.prototype.slice.call(listElements).filter(function (el) {
1062        return el.hasAttribute("data-parent") && el.getAttribute('data-parent') == parentId  && !el.classList.contains('hidden-search') ;
1063    });
1064    let nrChecked = 0;
1065    let nrCheckable = childrenElements.length;
1066    if (nrCheckable == 0) return;
1067    childrenElements.forEach(function (el) {
1068        if (el.classList.contains('active')) nrChecked++;
1069    });
1070    if (nrChecked === nrCheckable || nrChecked === 0) {
1071        if (nrChecked === 0) {
1072            parentLi.classList.remove("checked");
1073        } else {
1074            parentLi.classList.add("checked");
1075        }
1076    } else {
1077        parentLi.classList.remove("checked");
1078    }
1079}
1080
1081vanillaSelectBox.prototype.checkUncheckFromParent = function (liClicked) {
1082    let self = this;
1083    let parentId = liClicked.id;
1084    if (!self.isMultiple) return;
1085    let listElements = self.drop.querySelectorAll("li");
1086    let childrenElements = Array.prototype.slice.call(listElements).filter(function (el) {
1087        return el.hasAttribute("data-parent") && el.getAttribute('data-parent') == parentId && !el.classList.contains('hidden-search');
1088    });
1089    let nrChecked = 0;
1090    let nrCheckable = childrenElements.length;
1091    if (nrCheckable == 0) return;
1092    childrenElements.forEach(function (el) {
1093        if (el.classList.contains('active')) nrChecked++;
1094    });
1095    if (nrChecked === nrCheckable || nrChecked === 0) {
1096        //check all or uncheckAll : just do the opposite
1097        childrenElements.forEach(function (el) {
1098            var event = document.createEvent('HTMLEvents');
1099            event.initEvent('click', true, false);
1100            el.dispatchEvent(event);
1101        });
1102        if (nrChecked === 0) {
1103            liClicked.classList.add("checked");
1104        } else {
1105            liClicked.classList.remove("checked");
1106        }
1107    } else {
1108        //check all
1109        liClicked.classList.remove("checked");
1110        childrenElements.forEach(function (el) {
1111            if (!el.classList.contains('active')) {
1112                var event = document.createEvent('HTMLEvents');
1113                event.initEvent('click', true, false);
1114                el.dispatchEvent(event);
1115            }
1116        });
1117    }
1118}
1119
1120vanillaSelectBox.prototype.checkUncheckAll = function () {
1121    let self = this;
1122    if (!self.isMultiple) return;
1123    let nrChecked = 0;
1124    let nrCheckable = 0;
1125    let checkAllElement = null;
1126    if (self.listElements == null) return;
1127    Array.prototype.slice.call(self.listElements).forEach(function (x) {
1128        if (x.hasAttribute('data-value')) {
1129            if (x.getAttribute('data-value') === 'all') {
1130                checkAllElement = x;
1131            }
1132            if (x.getAttribute('data-value') !== 'all'
1133                && !x.classList.contains('hidden-search')
1134                && !x.classList.contains('disabled')) {
1135                nrCheckable++;
1136                nrChecked += x.classList.contains('active');
1137            }
1138        }
1139    });
1140
1141    if (checkAllElement) {
1142        if (nrChecked === nrCheckable) {
1143            // check the checkAll checkbox
1144            self.title.textContent = self.userOptions.translations.all || "all";
1145            checkAllElement.classList.add("active");
1146            checkAllElement.innerText = self.userOptions.translations.clearAll;
1147            checkAllElement.setAttribute('data-selected', 'true')
1148        } else if (nrChecked === 0) {
1149            // uncheck the checkAll checkbox
1150            self.title.textContent = self.userOptions.placeHolder;
1151            checkAllElement.classList.remove("active");
1152            checkAllElement.innerText = self.userOptions.translations.selectAll;
1153            checkAllElement.setAttribute('data-selected', 'false')
1154        }
1155    }
1156}
1157
1158vanillaSelectBox.prototype.setValue = function (values) {
1159    let self = this;
1160    let listElements = self.drop.querySelectorAll("li");
1161
1162    if (values == null || values == undefined || values == "") {
1163        self.empty();
1164    } else {
1165        if (self.isMultiple) {
1166            if (vanillaSelectBox_type(values) == "string") {
1167                if (values === "all") {
1168                    values = [];
1169                    Array.prototype.slice.call(listElements).forEach(function (x) {
1170                        if (x.hasAttribute('data-value')) {
1171                            let value = x.getAttribute('data-value');
1172                            if (value !== 'all') {
1173                                if (!x.classList.contains('hidden-search') && !x.classList.contains('disabled')) {
1174                                    values.push(x.getAttribute('data-value'));
1175                                }
1176                                // already checked (but hidden by search)
1177                                if (x.classList.contains('active')) {
1178                                    if (x.classList.contains('hidden-search') || x.classList.contains('disabled')) {
1179                                        values.push(value);
1180                                    }
1181                                }
1182                            }else{
1183                                x.classList.add("active");
1184                            }
1185                        } else if (x.classList.contains('grouped-option')) {
1186                            x.classList.add("checked");
1187                        }
1188                    });
1189                }
1189 else if (values === "none") {
1190                    values = [];
1191                    Array.prototype.slice.call(listElements).forEach(function (x) {
1192                        if (x.hasAttribute('data-value')) {
1193                            let value = x.getAttribute('data-value');
1194                            if (value !== 'all') {
1195                                if (x.classList.contains('active')) {
1196                                    if (x.classList.contains('hidden-search') || x.classList.contains('disabled')) {
1197                                        values.push(value);
1198                                    }
1199                                }
1200                            }
1201                        } else if (x.classList.contains('grouped-option')) {
1202                            x.classList.remove("checked");
1203                        }
1204                    });
1205                } else {
1206                    values = values.split(",");
1207                }
1208            }
1209            let foundValues = [];
1210            if (vanillaSelectBox_type(values) == "array") {
1211                Array.prototype.slice.call(self.options).forEach(function (x) {
1212                    if (values.indexOf(x.value) !== -1) {
1213                        x.selected = true;
1214                        foundValues.push(x.value);
1215                    } else {
1216                        x.selected = false;
1217                    }
1218                });
1219                let selectedTexts = ""
1220                let sep = "";
1221                let nrActives = 0;
1222                let nrAll = 0;
1223                Array.prototype.slice.call(listElements).forEach(function (x) {
1224                    if (x.value !== 'all') {
1225                        nrAll++;
1226                    }                    
1227                    if (foundValues.indexOf(x.getAttribute("data-value")) != -1) {
1228                        x.classList.add("active");
1229                        nrActives++;
1230                        selectedTexts += sep + x.getAttribute("data-text");
1231                        sep = ",";
1232                    } else {
1233                        x.classList.remove("active");
1234                    }
1235                });
1236                if (nrAll == nrActives - Number(!self.userOptions.disableSelectAll)) {
1237                    let wordForAll = self.userOptions.translations.all || "all";
1238                    selectedTexts = wordForAll;
1239                } else if (self.multipleSize != -1) {
1240                    if (nrActives > self.multipleSize) {
1241                        let wordForItems = self.userOptions.translations.items || "items"
1242                        selectedTexts = nrActives + " " + wordForItems;
1243                    }
1244                }
1245                self.title.textContent = selectedTexts;
1246                self.privateSendChange();
1247            }
1248            self.checkUncheckAll();
1249        } else {
1250            let found = false;
1251            let text = "";
1252            let classNames = ""
1253            Array.prototype.slice.call(listElements).forEach(function (x) {
1254                let liVal = x.getAttribute("data-value") == values;
1255                if(liVal !== "all"){
1256                    if (liVal == values) {
1257                        x.classList.add("active");
1258                        found = true;
1259                        text = x.getAttribute("data-text")
1260                    } else {
1261                        x.classList.remove("active");
1262                    }
1263                }
1264            });
1265            Array.prototype.slice.call(self.options).forEach(function (x) {
1266                if (x.value == values) {
1267                    x.selected = true;
1268                    className = x.getAttribute("class");
1269                    if (!className) className = "";
1270                } else {
1271                    x.selected = false;
1272                }
1273            });
1274            if (found) {
1275                self.title.textContent = text;
1276                if (self.userOptions.placeHolder != "" && self.title.textContent == "") {
1277                    self.title.textContent = self.userOptions.placeHolder;
1278                }
1279                if (className != "") {
1280                    self.title.setAttribute("class", className + " title");
1281                } else {
1282                    self.title.setAttribute("class", "title");
1283                }
1284            }
1285        }
1286    }
1287}
1288
1289vanillaSelectBox.prototype.privateSendChange = function () {
1290    let event = document.createEvent('HTMLEvents');
1291    event.initEvent('change', true, false);
1292    this.root.dispatchEvent(event);
1293}
1294
1295vanillaSelectBox.prototype.empty = function () {
vendor: 2,103 bytes, lines 1296-1359
1296    Array.prototype.slice.call(this.listElements).forEach(function (x) {
1297        x.classList.remove("active");
1298    });
1299    let parentElements = this.drop.querySelectorAll("li.grouped-option");
1300    if(parentElements){
1301        Array.prototype.slice.call(parentElements).forEach(function (x) {
1302            x.classList.remove("checked");
1303        });
1304    }
1305    Array.prototype.slice.call(this.options).forEach(function (x) {
1306        x.selected = false;
1307    });
1308    this.title.textContent = "";
1309    if (this.userOptions.placeHolder != "" && this.title.textContent == "") {
1310        this.title.textContent = this.userOptions.placeHolder;
1311    }
1312    this.checkUncheckAll();
1313    this.privateSendChange();
1314}
1315
1316vanillaSelectBox.prototype.destroy = function () {
1317    let already = document.getElementById("btn-group-" + this.rootToken);
1318    if (already) {
1319        VSBoxCounter.remove(this.instanceOffset);
1320        already.remove();
1321        this.root.style.display = "inline-block";
1322    }
1323}
1324vanillaSelectBox.prototype.disable = function () {
1325    let already = document.getElementById("btn-group-" + this.rootToken);
1326    if (already) {
1327        button = already.querySelector("button")
1328        if (button) button.classList.add("disabled");
1329        this.isDisabled = true;
1330    }
1331}
1332vanillaSelectBox.prototype.enable = function () {
1333    let already = document.getElementById("btn-group-" + this.rootToken);
1334    if (already) {
1335        button = already.querySelector("button")
1336        if (button) button.classList.remove("disabled");
1337        this.isDisabled = false;
1338    }
1339}
1340
1341vanillaSelectBox.prototype.showOptions = function () {
1342    console.log(this.userOptions);
1343}
1344// Polyfills for IE
1345if (!('remove' in Element.prototype)) {
1346    Element.prototype.remove = function () {
1347        if (this.parentNode) {
1348            this.parentNode.removeChild(this);
1349        }
1350    };
1351}
1352
1353function vanillaSelectBox_type(target) {
1354    const computedType = Object.prototype.toString.call(target);
1355    const stripped = computedType.replace("[object ", "").replace("]", "");
1356    const lowercased = stripped.toLowerCase();
1357    return lowercased;
1358}
1359

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.