PageSourceSearch

https://rsez.lv/wp-content/plugins/codespacing-progress-map/assets/js/tail-select/tail.select.js?ver=5.7.1

js rsez.lv collected 2026-10-02 19:11:06 UTC 64,178 bytes, 1,586 lines download raw bytes

1/*
2 |  tail.select - The vanilla solution to make your HTML select fields AWESOME!
3 |  @file       ./js/tail.select.js
4 |  @author     wolffe <[email protected]>
5 |  @author     SamBrishes <[email protected]>
6 |  @version    0.5.20
7 |
8 |  @website    https://github.com/wolffe/tail.select.js
9 |  @license    X11 / MIT License
10 |  @copyright  Copyright © 2020 - 2021 wolffe <[email protected]>
11 |  @copyright  Copyright © 2014 - 2019 SamBrishes, pytesNET <[email protected]>
12 */
13;(function(root, factory){
14    if(typeof define === "function" && define.amd){
15        define(function(){ return factory(root); });
16    } else if(typeof module === "object" && module.exports){
17        module.exports = factory(root);
18    } else {
19        if(typeof root.tail === "undefined"){
20            root.tail = {};
21        }
22        root.tail.select = factory(root);
23
24        // jQuery Support
25        if(typeof jQuery !== "undefined"){
26            jQuery.fn.tailselect = function(o){
27                var r = [], i;
28                this.each(function(){ if((i = tail.select(this, o)) !== false){ r.push(i); } });
29                return (r.length === 1)? r[0]: (r.length === 0)? false: r;
30            };
31        }
32
33        // MooTools Support
34        if(typeof(MooTools) != "undefined"){
35            Element.implement({ tailselect: function(o){ return new tail.select(this, o); } });
36        }
37    }
38}(window, function(root){
39    "use strict";
40    var w = root, d = root.document;
41
42    // Internal Helper Methods
43    function cHAS(el, name){
44        return (el && "classList" in el)? el.classList.contains(name): false;
45    }
46    function cADD(el, name){
47        return (el && "classList" in el)? el.classList.add(name): undefined;
48    }
49    function cREM(el, name){
50        return (el && "classList" in el)? el.classList.remove(name): undefined;
51    }
52    function trigger(el, event, opt){
53        if(CustomEvent && CustomEvent.name){
54            var ev = new CustomEvent(event, opt);
55        } else {
56            var ev = d.createEvent("CustomEvent");
57            ev.initCustomEvent(event, !!opt.bubbles, !!opt.cancelable, opt.detail);
58        }
59        return el.dispatchEvent(ev);
60    }
61    function clone(obj, rep){
62        if(typeof Object.assign === "function"){
63            return Object.assign({}, obj, rep || {});
64        }
65        var clone = Object.constructor();
66        for(var key in obj){
67            clone[key] = (key in rep)? rep[key]: obj[key];
68        }
69        return clone;
70    }
71    function create(tag, classes){
72        var r = d.createElement(tag);
73            r.className = (classes && classes.join)? classes.join(" "): classes || "";
74        return r;
75    }
76
77    /*
78     |  SELECT CONSTRUCTOR
79     |  @since  0.5.12 [0.3.0]
80     */
81    var select = function(el, config){
82        el = (typeof(el) == "string")? d.querySelectorAll(el): el;
83        if(el instanceof NodeList || el instanceof HTMLCollection || el instanceof Array){
84            for(var _r = [], l = el.length, i = 0; i < l; i++){
85                _r.push(new select(el[i], clone(config, {})));
86            }
87            return (_r.length === 1)? _r[0]: ((_r.length === 0)? false: _r);
88        }
89        if(!(el instanceof Element) || !(this instanceof select)){
90            return !(el instanceof Element)? false: new select(el, config);
91        }
92
93        // Check Element
94        if(select.inst[el.getAttribute("data-tail-select")]){
95            return select.inst[el.getAttribute("data-tail-select")];
96        }
97        if(el.getAttribute("data-select")){
98            var test = JSON.parse(el.getAttribute("data-select").replace(/\'/g, '"'));
99            if(test instanceof Object){
100                config = clone(config, test); // This is a unofficial function ;3
101            }
102        }
103
104        // Get Element Options
105        var placeholder = el.getAttribute("placeholder") || el.getAttribute("data-placeholder"),
106            fb1 = "bindSourceSelect", fb2 = "sourceHide"; // Fallbacks
107        config = (typeof(config) == "object")? config: {};
108        config.multiple = ("multiple" in config)? config.multiple: el.multiple;
109        config.disabled = ("disabled" in config)? config.disabled: el.disabled;
110        config.placeholder = placeholder || config.placeholder || null;
111        config.width = (config.width === "auto")? el.offsetWidth + 50: config.width;
112        config.sourceBind = (fb1 in config)? config[fb1]: config.sourceBind || false;
113        config.sourceHide = (fb2 in config)? config[fb2]: config.sourceHide || true;
114        config.multiLimit = (config.multiLimit >= 0)? config.multiLimit: Infinity;
115
116        // Init Instance
117        this.e = el;
118        this.id = ++select.count;
119        this.con = clone(select.defaults, config);
120        this.events = {};
121        select.inst["tail-" + this.id] = this;
122
123        var ret = this.init().bind();
124
125        return ret;
126    }, options;
127    select.count = 0;
128    select.inst = {};
129
130    /*
131     |  STORAGE :: DEFAULT OPTIONS
132     */
133    select.defaults = {
134        animate: true,              // [0.3.0]      Boolean
135        classNames: null,           // [0.2.0]      Boolean, String, Array, null
136        csvOutput: false,           // [0.3.4]      Boolean
137        csvSeparator: ",",          // [0.3.4]      String
138        descriptions: false,        // [0.3.0]      Boolean
139        deselect: false,            // [0.3.0]      Boolean
140        disabled: false,            // [0.5.0]      Boolean
141        height: 350,                // [0.2.0]      Integer, null
142        hideDisabled: false,        // [0.3.0]      Boolean
143        hideSelected: false,        // [0.3.0]      Boolean
144        items: {},                  // [0.3.0]      Object
145        locale: "en",               // [0.5.0]      String
146        linguisticRules: {          // [0.5.9]      Object
147            "е": "ё",
148            "a": "ä",
149            "o": "ö",
150            "u": "ü",
151            "ss": "ß"
152        },
153        multiple: false,            // [0.2.0]      Boolean
154        multiLimit: Infinity,       // [0.3.0]      Integer, Infinity
155        multiPinSelected: false,    // [0.5.0]      Boolean
156        multiContainer: false,      // [0.3.0]      Boolean, String
157        multiShowCount: true,       // [0.3.0]      Boolean
158        multiShowLimit: false,      // [0.5.0]      Boolean
159        multiSelectAll: false,      // [0.4.0]      Boolean
160        multiSelectGroup: true,     // [0.4.0]      Boolean
161        openAbove: null,            // [0.3.0]      Boolean, null
162        placeholder: null,          // [0.2.0]      String, null
163        search: false,              // [0.3.0]      Boolean
164        searchConfig: [             // [0.5.13]     Array
165            "text", "value"
166        ],
167        searchFocus: true,          // [0.3.0]      Boolean
168        searchMarked: true,         // [0.3.0]      Boolean
169        searchMinLength: 1,         // [0.5.13]     Integer
170        searchDisabled: true,       // [0.5.5]      Boolean
171        sortItems: false,           // [0.3.0]      String, Function, false
172        sortGroups: false,          // [0.3.0]      String, Function, false
173        sourceBind: false,          // [0.5.0]      Boolean
174        sourceHide: true,           // [0.5.0]      Boolean
175        startOpen: false,           // [0.3.0]      Boolean
176        stayOpen: false,            // [0.3.0]      Boolean
177        width: null,                // [0.2.0]      Integer, String, null
178        cbComplete: undefined,      // [0.5.0]      Function
179        cbEmpty: undefined,         // [0.5.0]      Function
180        cbLoopItem: undefined,      // [0.4.0]      Function
181        cbLoopGroup: undefined      // [0.4.0]      Function
182    };
183
184    /*
185     |  STORAGE :: STRINGS
186     */
187    select.strings = {
188        en: {
189            all: "All",
190            none: "None",
191            empty: "No Options available",
192            emptySearch: "No Options found",
193            limit: "You can't select more Options",
194            placeholder: "Select an Option...",
195            placeholderMulti: "Select up to :limit Options...",
196            search: "Type in to search...",
197            disabled: "This Field is disabled"
198        },
199        modify: function(locale, id, string){
200            if(!(locale in this)){
201                return false;
202            }
203            if((id instanceof Object)){
204                for(var key in id){
205                    this.modify(locale, key, id[key]);
206                }
207            } else {
208                this[locale][id] = (typeof(string) == "string")? string: this[locale][id];
209            }
210            return true;
211        },
212        register: function(locale, object){
213            if(typeof(locale) != "string" || !(object instanceof Object)){
214                return false;
215            }
216            this[locale] = object;
217            return true;
218        }
219    };
220
221    /*
222     |  TAIL.SELECT HANDLER
223     */
224    select.prototype = {
225        /*
226         |  INERNAL :: TRANSLATE
227         |  @since  0.5.8 [0.5.8]
228         */
229        _e: function(string, replace, def){
230            if(!(string in this.__)){
231                return (!def)? string: def;
232            }
233
234            var string = this.__[string];
235            if(typeof(string) === "function"){
236                string = string.call(this, replace);
237            }
238            if(typeof(replace) === "object"){
239                for(var key in replace){
240                    string = string.replace(key, replace[key]);
241                }
242            }
243            return string;
244        },
245
246        /*
247         |  INTERNAL :: INIT SELECT FIELD
248         |  @since  0.5.13 [0.3.0]
249         */
250        init: function(){
251            var self = this, classes = ["tail-select"], con = this.con,
252                regexp = /^[0-9.]+(?:cm|mm|in|px|pt|pc|em|ex|ch|rem|vw|vh|vmin|vmax|\%)$/i;
253
254            // Init ClassNames
255            var c = (con.classNames === true)? this.e.className: con.classNames;
256            classes.push((c && c.push)? c.join(" "): (c && c.split)? c: "no-classe
256s");
257            if(con.hideSelected){    classes.push("hide-selected"); }
258            if(con.hideDisabled){    classes.push("hide-disabled"); }
259            if(con.multiLimit == 0){ classes.push("disabled");      }
260            if(con.multiple){        classes.push("multiple");      }
261            if(con.deselect){        classes.push("deselect");      }
262            if(con.disabled){        classes.push("disabled");      }
263
264            // Init Variables
265            this.__ = clone(select.strings.en, select.strings[con.locale] || {});
266            this._init = true;
267            this._query = false;
268            this.select = create("DIV", classes);
269            this.label = create("DIV", "select-label");
270            this.dropdown = create("DIV", "select-dropdown");
271            this.search = create("DIV", "dropdown-search");
272            this.csvInput = create("INPUT", "select-search");
273
274            // Build :: Select
275            if(this.e.getAttribute("tabindex") !== null){
276                this.select.setAttribute("tabindex", this.e.getAttribute("tabindex"));
277            } else {
278                this.select.setAttribute("tabindex", 0);
279            }
280            if(con.width && regexp.test(con.width)){
281                this.select.style.width = con.width;
282            } else if(con.width && !isNaN(parseFloat(con.width, 10))){
283                this.select.style.width = con.width + "px";
284            }
285
286            // Build :: Label
287            this.label.addEventListener("click", function(event){
288                self.toggle.call(self, self.con.animate);
289            });
290            this.select.appendChild(this.label);
291
292            // Build :: Dropdown
293            if(!isNaN(parseInt(con.height, 10))){
294                this.dropdown.style.maxHeight = parseInt(con.height, 10) + "px";
295            }
296            if(con.search){
297                this.search.innerHTML = '<input type="text" class="search-input" />';
298                this.search.children[0].placeholder = this._e("search");
299                this.search.children[0].addEventListener("input", function(event){
300                    self.query.call(self, (this.value.length > con.searchMinLength)? this.value: undefined);
301                });
302                this.dropdown.appendChild(this.search);
303            }
304            this.select.appendChild(this.dropdown);
305
306            // Build :: CSV Input
307            this.csvInput.type = "hidden";
308            if(con.csvOutput){
309                this.csvInput.name = this.e.name;
310                this.e.removeAttribute("name");
311                this.select.appendChild(this.csvInput);
312            }
313
314            // Prepare Container
315            if(con.multiple && con.multiContainer){
316                if(d.querySelector(con.multiContainer)){
317                    this.container = d.querySelector(con.multiContainer);
318                    this.container.className += " tail-select-container";
319                } else if(con.multiContainer === true){
320                    this.container = this.label;
321                    this.container.className += " tail-select-container";
322                }
323            }
324
325            // Prepare Options
326            this.options = new options(this.e, this);
327            for(var l = this.e.options.length, i = 0; i < l; i++){
328                this.options.set(this.e.options[i], false);
329            }
330            for(var key in con.items){
331                if(typeof(con.items[key]) == "string"){
332                    con.items[key] = {value: con.items[key]};
333                }
334                this.options.add(con.items[key].key || key, con.items[key].value,
335                    con.items[key].group, con.items[key].selected,
336                    con.items[key].disabled, con.items[key].description);
337            }
338            this.query();
339
340            // Append and Return
341            if(this.e.nextElementSibling){
342                this.e.parentElement.insertBefore(this.select, this.e.nextElementSibling);
343            } else {
344                this.e.parentElement.appendChild(this.select);
345            }
346            if(con.sourceHide){
347                if(this.e.style.display == "none"){
348                    this.select.style.display = "none";
349                    this.e.setAttribute("data-select-hidden", "display");
350                } else if(this.e.style.visibility == "hidden"){
351                    this.select.style.visibiltiy = "hidden";
352                    this.e.setAttribute("data-select-hidden", "visibility");
353                } else {
354                    this.e.style.display = "none";
355                    this.e.setAttribute("data-select-hidden", "0");
356                }
357            }
358            this.e.setAttribute("data-tail-select", "tail-" + this.id);
359            if(self.con.startOpen){
360                this.open(con.animate);
361            }
362            (con.cbComplete || function(){ }).call(this, this.select);
363
364            return (this._init = false)? this: this;
365        },
366
367        /*
368         |  INTERNAL :: EVENT LISTENER
369         |  @since  0.5.13 [0.3.0]
370         */
371        bind: function(){
372            var self = this;
373
374            /* Seems to be last call from init */
375            if (typeof this.options_initial === 'undefined') {
376                var init_selected = [];
377
378                for (var idx = 0; idx < this.options.selected.length; ++idx) {
379                    init_selected.push(this.options.selected[idx]);
380                }
381
382                this.options_initial = {
383                    selected: init_selected
384                }
385            }
386
387            // Keys Listener
388            d.addEventListener("keydown", function(event){
389                var key = (event.keyCode || event.which), opt, inner, e, temp, tmp;
390                var space = (key == 32 && self.select === document.activeElement);
391                if(!space && (!cHAS(self.select, "active") || [13, 27, 38, 40].indexOf(key) < 0)){
392                    return false;
393                }
394                event.preventDefault();
395                event.stopPropagation();
396
397                // Space
398                if(key === 32){
399                    return self.open(self.con.animate);
400                }
401
402                // Enter || Escape
403                if(key == 13){
404                    if((opt = self.dropdown.querySelector(".dropdown-option.hover:not(.disabled)"))){
405                        self.options.select.call(self.options, opt);
406                    }
407                }
408                if(key == 27 || key == 13){
409                    return self.close(self.con.animate);
410                }
411
412                // Top || Down
413                if((opt = self.dropdown.querySelector(".dropdown-option.hover:not(.disabled)"))){
414                    cREM(opt, "hover"); e = [((key == 40)? "next": "previous") + "ElementSibling"];
415                    do {
416                        if((temp = opt[e]) !== null && opt.tagName == "LI"){
417                            opt = temp;
418                        } else if((temp = opt.parentElement[e]) !== null && temp.children.length > 0 && temp.tagName == "UL"){
419                            opt = temp.children[(key == 40)? 0: temp.children.length-1];
420                        } else {
421                            opt = false;
422                        }
423                        if(opt && (!cHAS(opt, "dropdown-option") || cHAS(opt, "disabled"))){
424                            continue;
425                        }
426                        break;
427                    } while(true);
428                }
429                if(!opt && key == 40){
430                    opt = self.dropdown.querySelector(".dropdown-option:not(.disabled)");
431                } else if(!opt && key == 38){
432                    tmp = self.dropdown.querySelectorAll(".dropdown-option:not(.disabled)");
433                    opt = tmp[tmp.length - 1];
434                }
435                if(opt && (inner = self.dropdown.querySelector(".dropdown-inner"))){
436                    var pos = (function(el){
437                        var _r = {top: el.offsetTop, height: el.offsetHeight};
438                        while((el = el.parentElement) != inner){
439                            _r.top += el.offsetTop;
440                        }
441                        return _r;
442                    })(opt);
443                    cADD(opt, "hover");
444                    inner.scrollTop = Math.max(0, pos.top - (pos.height * 2));
445                }
446                return true;
447            });
448
449            // Close
450            d.addEventListener("click", function(ev){
451                if(!cHAS(self.select, "active") || cHAS(self.select, "idle")){ return false; }
452                if(self.con.stayOpen === true){ return false; }
453
454                var targets = [self.e, self.select, self.container];
455                for(var l = targets.length, i = 0; i < l; i++){
456                    if(targets[i] && (targets[i].contains(ev.target) || targets[i] == ev.target)){
457                        return false;
458                    }
459                    if(!ev.target.parentElement){ return false; }
460                }
461                return self.close.call(self, self.con.animate);
462            });
463
464            // Bind Source Select
465            if(!this.con.sourceBind){
466                return true;
467            }
468            this.e.addEventListener("change", function(event){
469                if(event.detail != undefined){
470                    return false;
471                }
472                event.preventDefault();
473                event.stopPropagation();
474                if(!this.multiple && this.selectedIndex){
475                    self.options.select.call(self.options, this.options[this.selectedIndex]);
476                } else {
477                    var u = [].concat(self.options.selected);
478                    var s = [].filter.call(this.querySelectorAll("option:checked"), function(item){
479                        if(u.indexOf(item) >= 0){
480                            u.splice(u.indexOf(item), 1);
481                            return false;
482                        }
483                        return true;
484                    });
485                    self.options.walk.call(self.options, "unselect", u);
486                    self.options.walk.call(self.options, "select", s);
487                }
488            });
489            return true;
490        },
491
492        /*
493         |  INTERNAL :: INTERNAL CALLBACK
494         |  @since  0.5.14 [0.3.0]
495         */
496        callback: function(item, state, _force){
497            var rkey = item.key.replace(/('|\\)/g, "\\$1"),
498                rgrp = item.group.replace(/('|\\)/g, "\\$1"),
499                rsel = "[data-key='" + rkey + "'][data-group='" + rgrp + "']";
500            if(state == "rebuild"){ return this.query(); }
501
502            // Set Element-Item States
503            var element = this.dropdown.querySelector(rsel);
504            if(element && ["select", "disable"].indexOf(state) >= 0){
505                cADD(element, (state == "select"? "selected": "disabled"));
506            } else if(element && ["unselect", "enable"].indexOf(state) >= 0){
507                cREM(element, (state == "unselect"? "selected": "disabled"));
508            }
509
510            // Handle
511            this.update(item);
512            return (_force === true)? true: this.trigger("change", item, state);
513        },
514
515        /*
516         |  INTERNAL :: TRIGGER EVENT HANDLER
517         |  @since  0.5.2 [0.4.0]
518         */
519        trigger: function(event){
520            if(this._init){ return false; }
521            var obj = {bubbles: false, cancelable: true, detail: {args: arguments, self: this}};
522            if(event == "change" && arguments[2] && arguments[2].indexOf("select") >= 0){
523                trigger(this.e, "input", obj);
524                trigger(this.e, "change", obj);
525            }
526            trigger(this.select, "tail::" + event, obj);
527
528            var args = [], pass;
529            Array.prototype.map.call(arguments, function(item, i){
530                if(i > 0){ args.push(item); }
531            });
532            (this.events[event] || []).forEach(function(item){
533                pass = [].concat(args);
534                pass.push(item.args || null);
535                (item.cb || function(){ }).apply(obj.detail.self, pass);
536            });
537            return true;
538        },
539
540        /*
541         |  INTERNAL :: CALCULATE DROPDOWN
542         |  @since  0.5.4 [0.5.0]
543         */
544        calc: function(){
545            var clone = this.dropdown.cloneNode(true), height = this.con.height, search = 0,
546                inner = this.dropdown.querySelector(".dropdown-inner");
547
548            // Calculate Dropdown Height
549            clone = this.dropdown.cloneNode(true);
550            clone.style.cssText = "height:auto;min-height:auto;max-height:none;opacity:0;display:block;visibility:hidden;";
551            clone.style.maxHeight = this.con.height + "px";
552            clone.className += " cloned";
553            this.dropdown.parentElement.appendChild(clone);
554            height = (height > clone.clientHeight)? clone.clientHeight: height;
555            if(this.con.search){
556                search = clone.querySelector(".dropdown-search").clientHeight;
557            }
558            this.dropdown.parentElement.removeChild(clone);
559
560            // Calculate Viewport
561            var pos = this.select.getBoundingClientRect(),
562                bottom = w.innerHeight-(pos.top+pos.height),
563                view = ((height+search) > bottom)? pos.top > bottom: false;
564            if(this.con.openAbove === true || (this.con.openAbove !== false && view)){
565                view = true, height = Math.min((height), pos.top-10);
566                cADD(this.select, "open-top");
567            } else {
568                view = false, height = Math.min((height), bottom-10);
569                cREM(this.select, "open-top");
570            }
571            if(inner){
572                this.dropdown.style.maxHeight = height + "px";
573                inner.style.maxHeight = (height-search) + "px";
574            }
575            return this;
576        },
577
578        /*
579         |  API :: QUERY OPTIONS
580         |  @since  0.5.13 [0.5.0]
581         */
582        query: function(search, conf){
583            var item, tp, ul, li, a1, a2;                           // Pre-Definition
584            var self = this, con = this.con, g = "getAttribute";    // Shorties
585            var root = create("DIV", "dropdown-inner"),             // Contexts
586                func = (!search)? "walker": "finder",
587                args = (!search)? [con.sortItems, con.sortGroups]: [search, conf];
588
589            // Option Walker
590            this._query = (typeof(search) == "string")? search: false;
591            while(item = this.options[func].apply(this.options, args)){
592                if(!ul || (ul && ul[g]("data-group") !== item.group)){
593                    tp = (con.cbLoopGroup || this.cbGroup).call(this, item.group, search, root);
594                    if(tp instanceof Element){
595                        ul = tp;
596                        ul.setAttribute("data-group", item.group);
597                        root.appendChild(ul);
598                    } else { break; }
599                }
600
601                // Create Item
602                if((li = (con.cbLoopItem || this.cbItem).call(this, item, ul, search, root)) === null){
603                    continue;
604                }
605                if(li === false){ break; }
606                li.setAttribute("data-key", item.key);
607                li.setAttribute("data-group", item.group);
608                li.addEventListener("click", function(event){
609                    if(!this.hasAttribute("data-key")){ return false; }
610                    var key = this[g]("data-key"), group = this[g]("data-group") || "#";
611                    if(self.options.toggle.call(self.options, key, group)){
612                        if(self.con.stayOpen === false && !self.con.multiple){
613                            self.close.call(self, self.con.animate);
614                        }
615                    }
616                });
617                ul.appendChild(li);
618            }
619
620            // Empty
621            var count = root.querySelectorAll("*[data-key]").length;
622            if(count == 0){
623                (this.con.cbEmpty || function(element){
624                    var li = create("SPAN", "dropdown-empty");
625                    li.innerText = this._e("empty");
626                    element.appendChild(li);
627                }).call(this, root, search);
628            }
629
630            // Select All
631            if(count > 0 && con.multiple && con.multiLimit == Infinity && con.multiSelectAll){
632                a1 = create("BUTTON", "tail-all"), a2 = create("BUTTON", "tail-none");
633                a1.innerText = this._e("all");
634                a1.addEventListener("click", function(event){
635                    event.preventDefault();
636                    var options = self.dropdown.querySelectorAll(".dropdown-inner .dropdown-option");
637                    self.options.walk.call(self.options, "select", options);
638                })
639                a2.innerText = this._e("none");
640                a2.addEventListener("click", function(event){
641                    event.preventDefault();
642                    var options = self.dropdown.querySelectorAll(".dropdown-inner .dropdown-option");
643                    self.options.walk.call(self.options, "unselect", options);
644                })
645
646                // Add Element
647                li = create("SPAN", "dropdown-action");
648                li.appendChild(a1);
649                li.appendChild(a2);
650                root.insertBefore(li, root.children[0]);
651            }
652
653            // Add and Return
654            var data = this.dropdown.querySelector(".dropdown-inner");
655            this.dropdown[(data? "replace": "append") + "Child"](root, data);
656            if(cHAS(this.select, "active")){
657                this.calc();
658            }
659            return this.updateCSV().updateLabel();
660        },
661
662        /*
663         |  API :: CALLBACK -> CREATE GROUP
664         |  @since  0.5.8 [0.4.0]
665         */
666        cbGroup: function(group, search){
667            var ul = create("UL", "dropdown-optgroup"), self = this, a1, a2;
668            if(group == "#"){ return ul; }
669            ul.innerHTML = '<li class="optgroup-title"><b>' + group + '</b></li>';
670            if(this.con.multiple && this.con.multiLimit == Infinity && this.con.multiSelectAll){
671                a1 = create("BUTTON", "tail-none"), a2 = create("BUTTON", "tail-all");
672                a1.innerText = this._e("none");
673                a1.addEventListener("click", function(event){
674                    event.preventDefault();
675                    var grp = this.parentElement.parentElement.getAttribute("data-group");
676                    self.options.all.call(self.options, "unselect", grp);
677                });
678                a2.innerText = this._e("all");
679                a2.addEventListener("click", function(event){
680                    event.preventDefault();
681                    var grp = this.parentElement.parentElement.getAttribute("data-group");
682                    self.options.all.call(self.options, "select", grp);
683                });
684                ul.children[0].appendChild(a1);
685                ul.children[0].appendChild(a2);
686            }
687            return ul;
688        },
689
690        /*
691         |  API :: CALLBACK -> CREATE ITEM
692         |  @since  0.5.13 [0.4.0]
693         */
694        cbItem: function(item, optgroup, search){
695            var li = create("LI", "dropdown-option" + (item.selected? " selected": "") + (item.disabled? " disabled": ""));
696            li.title = item.option.title;
697
698            // Inner Text
699            if(search && search.length > 0 && this.con.searchMarked){
700                search = this.options.applyLinguisticRules(search);
701                li.innerHTML = item.value.replace(new RegExp("(" + search + ")", "i"), "<mark>$1</mark>");
702            } else {
703                li.innerText = item.value;
704            }
705
706            // Inner Description
707            if(this.con.descriptions && item.description){
708                li.innerHTML += '<span class="option-description">' + item.description + '</span>';
709            }
710            return li;
711        },
712
713        /*
714         |  API :: UPDATE EVERYTHING
715         |  @since  0.5.0 [0.5.0]
716         */
717        update: function(item){
718            return this.updateLabel().updateContainer(item).updatePin(item).updateCSV(item);
719        },
720
721        /*
722         |  API :: UPDATE LABEL
723         |  @since  0.5.8 [0.5.0]
724         */
725        updateLabel: function(label){
726            if(this.container == this.label && this.options.selected.length > 0){
727                if(this.label.querySelector(".label-inner")){
728                    this.label.removeChild(this.label.querySelector(".label-inner"));
729                }
730                if(this.label.querySelector(".label-count")){
731                    this.label.removeChild(this.label.querySelector(".label-count"));
732                }
733                return this;
734            }
735            var c = this.con, len = this.options.selected.length, limit;
736            if(typeof(label) != "string"){
737                if(c.disabled){
738                    label = "disabled";
739                } else if(this.dropdown.querySelectorAll("*[data-key]").length == 0){
740                    label = "empty" + (cHAS(this.select, "in-search")? "Search": "");
741                } else if(c.multiLimit <= len){
742                    label = "limit";
743                } else if(!c.multiple && this.options.selected.length > 0){
744                    label = this.options.selected[0].innerText;
745                } else if(typeof(c.placeholder) == "string"){
746                    label = c.placeholder;
747                } else {
748                    label = "placeholder" + (c.multiple && c.multiLimit < Infinity? "Multi": "");
749                }
750            }
751
752            // Set HTML
753            label = this._e(label, {":limit": c.multiLimit}, label);
754            label = '<span class="label-inner">' + label + '</span>',
755            limit = (c.multiShowLimit && c.multiLimit < Infinity);
756            if(c.multiple && c.multiShowCount){
757                var selected_class = (len) ? 'label-count--selected' : '';
758
759                label = '<span class="label-count ' + selected_class + '">:c</span>' + label;
760                label = label.replace(":c", len + (limit? (" / " + c.multiLimit): ""));
761            }
762            this.label.innerHTML = label;
763            return this;
764        },
765
766        /*
767         |  API :: UPDATE CONTAINER
768         |  @since  0.5.0 [0.5.0]
769         */
770        updateContainer: function(item){
771            if(!this.container || !this.con.multiContainer){
772                return this;
773            }
774            var s = "[data-group='" + item.group + "'][data-key='" + item.key + "']";
775            if(this.container.querySelector(s)){
776                if(!item.selected){
777                    this.container.removeChild(this.container.querySelector(s));
778                }
779                return this;
780            }
781
782            // Create Item
783            if(item.selected){
784                var self = this, hndl = create("DIV", "select-handle");
785                hndl.innerText = item.value;
786                hndl.setAttribute("data-key", item.key);
787                hndl.setAttribute("data-group", item.group);
788                hndl.addEventListener("click", function(event){
789                    event.preventDefault();
790                    event.stopPropagation();
791                    var key = this.getAttribute("data-key"), grp = this.getAttribute("data-group");
792                    self.options.unselect.call(self.options, key, grp);
793                });
794                this.container.appendChild(hndl);
795            }
796            return this;
797        },
798
799        /*
800         |  API :: UPDATE PIN POSITION
801         |  @since  0.5.3 [0.5.0]
802         */
803        updatePin: function(item){
804            var inner = this.dropdown.querySelector(".dropdown-inner ul"),
805                option = "li[data-key='" + item.key + "'][data-group='" + item.group + "']";
806            if(!this.con.multiPinSelected || !inner || this._query !== false){
807                return this;
808            }
809
810            // Create Item
811            option = this.dropdown.querySelector(option);
812            if(item.selected){
813                inner.insertBefore(option, inner.children[0]);
814            } else {
815                var grp = this.dropdown.querySelector("ul[data-group='" + item.group + "']"),
816                    prev = this.options[item.index-1], found = false;
817                while(prev && prev.group == item.group){
818                    if(found = grp.querySelector("li[data-key='" + prev.key + "']")){
819                        break;
820                    }
821                    prev = this.options[prev.index-1];
822                }
823                if(found && found.nextElementSibling){
824                    grp.insertBefore(option, found.nextElementSibling);
825                } else {
826                    grp.appendChild(option);
827                }
828            }
829            return this;
830        },
831
832        /*
833         |  API :: UPDATE CSV INPUT
834         |  @since  0.5.0 [0.5.0]
835         */
836        updateCSV: function(item){
837            if(!this.csvInput || !this.con.csvOutput){
838                return this;
839            }
840            for(var selected = [], l = this.options.selected.length, i = 0; i < l; i++){
841                selected.push(this.options.selected[i].value);
842            }
843            this.csvInput.value = selected.join(this.con.csvSeparator || ",");
844            return this;
845        },
846
847        /*
848         |  PUBLIC :: OPEN DROPDOWN
849         |  @since  0.5.0 [0.3.0]
850         */
851        open: function(animate){
852            if(cHAS(this.select, "active") || cHAS(this.select, "idle") || this.con.disabled){
853                return false;
854            }
855            this.calc();
856
857            // Final Function
858            var final = function(){
859                cADD(self.select, "active");
860                cREM(self.select, "idle");
861                this.dropdown.style.height = "auto";
862                this.dropdown.style.overflow = "visible";
863
864                this.label.removeAttribute("style");
865                if(this.con.search && this.con.searchFocus){
866                    this.dropdown.querySelector("input").focus();
867                }
868                this.trigger.call(this, "open");
869            }, self = this, e = this.dropdown.style;
870
871            // Open
872            if(animate !== false){
873                this.label.style.zIndex = 25;
874                this.dropdown.style.cssText += "height:0;display:block;overflow:hidden;";
875                cADD(self.select, "idle");
876                (function animate(){
877                    var h = parseInt(e.height, 10), m = parseInt(e.maxHeight, 10);
878                    if(h >= m){
879                        return final.call(self);
880                    }
881                    e.height = ((h+50 > m)? m: h+50) + "px";
882                    setTimeout(animate, 20);
883                })();
884            } else {
885                e.cssText = "height:" + e.maxHeight + ";display:block;overflow:hidden;";
886                final.call(this);
887            }
888            return this;
889        },
890
891        /*
892         |  PUBLIC :: CLOSE DROPDOWN
893         |  @since  0.5.0 [0.3.0]
894         */
895        close: function(animate){
896            if(!cHAS(this.select, "active") || cHAS(this.select, "idle")){
897                return false;
898            }
899            var final = function(){
900                cREM(this.select, "active");
901                cREM(this.select, "idle");
902                this.dropdown.removeAttribute("style");
903                this.dropdown.querySelector(".dropdown-inner").removeAttribute("style");
904                this.trigger.call(this, "close");
905            }, self = this, e = this.dropdown;
906
907            // Close
908            if(animate !== false){
909                cADD(this.select, "idle");
910                this.dropdown.style.overflow = "hidden";
911                (function animate(){
912                    if((parseInt(e.offsetHeight, 10)-50) <= 0){
913                        return final.call(self);
914                    }
915                    e.style.height = (parseInt(e.offsetHeight, 10)-50) + "px";
916                    setTimeout(animate, 20);
917                })();
918            } else {
919                final.call(this);
920            }
921            return this;
922        },
923
924        /*
925         |  PUBLIC :: TOGGLE DROPDOWN
926         |  @since  0.5.0 [0.3.0]
927         */
928        toggle: function(animate){
929            if(cHAS(this.select, "active")){
930                return this.close(animate);
931            }
932            return !cHAS(this.select, "idle")? this.open(animate): this;
933        },
934
935        /*
936         |  PUBLIC :: REMOVE SELECT
937         |  @since  0.5.3 [0.3.0]
938         */
939        remove: function(){
940            this.e.removeAttribute("data-tail-select");
941            if(this.e.hasAttribute("data-select-hidden")){
942                if(this.e.getAttribute("data-select-hidden") == "0"){
943                    this.e.style.removeProperty("display");
944                }
945                this.e.removeAttribute("data-select-hidden");
946            }
947            Array.prototype.map.call(this.e.querySelectorAll("[data-select-option='add']"), function(item){
948                item.parentElement.removeChild(item);
949            })
950            Array.prototype.map.call(this.e.querySelectorAll("[data-select-optgroup='add']"), function(item){
951                item.parentElement.removeChild(item);
952            })
953            this.e.name = (this.csvInput.hasAttribute("name"))? this.csvInput.name: this.e.name;
954            if(this.select.parentElement){
955                this.select.parentElement.removeChild(this.select);
956            }
957            if(this.container){
958                var handles = this.container.querySelectorAll(".select-handle");
959                for(var l = handles.length, i = 0; i < l; i++){
960                    this.container.removeChild(handles[i]);
961                }
962            }
963            return this;
964        },
965
966        /*
967         |  PUBLIC :: RELOAD SELECT
968         |  @since  0.5.0 [0.3.0]
969         */
970        reload: function(){
971            return this.remove().init();
972        },
973
974        /*
975         |  PUBLIC :: RELOAD SELECT
976         |  @since  0.5.20 [0.3.0]
977         */
978        reset: function(){
979            /* Set all to unselected */
980            for(var idx = 0; idx < this.options.element.length; ++idx){
981                this.options.element[idx].selected = false;
982                this.options.unselect(this.options.element[idx].value, "#", true);
983            }
984
985            /* Restore initial selection */
986            for(var idx = 0; idx < this.options_initial.selected.length; ++idx){
987                this.options_initial.selected[idx].selected = true;
988                this.options.select(this.options_initial.selected[idx].value, "#", true);
989            }
990
991            /* Reset input field */
992            this.dropdown.querySelector('input').value = '';
993            this.query.call(this, '');
994        },
995
996        /*
997         |  PUBLIC :: GET|SET CONFIG
998         |  @since  0.5.15 [0.4.0]
999         */
1000        config: function(key, value, rebuild){
1001            if(key instanceof Object){
1002                for(var k in key){ this.config(k, key[k], false); }
1003                return this.reload()? this.con: this.con;
1004            }
1005            if(typeof(key) == "undefined"){
1006                return this.con;
1007            } else if(!(key in this.con)){
1008                return false;
1009            }
1010
1011            // Set | Return
1012            if(typeof(value) == "undefined"){
1013                return this.con[key];
1014            }
1015            this.con[key] = value;
1016            if(rebuild !== false){
1017                this.reload();
1018            }
1019            return this;
1020        },
1021        enable: function(update){
1022            cREM(this.select, "disabled");
1023            this.e.disabled = false;
1024            this.con.disabled = false;
1025            return (update === false)? this: this.reload();
1026        },
1027        disable: function(update){
1028            cADD(this.select, "disabled");
1029            this.e.disabled = true;
1030            this.con.disabled = true;
1031            return (update === false)? this: this.reload();
1032        },
1033
1034        /*
1035         |  PUBLIC :: CUSTOM EVENT LISTENER
1036         |  @since  0.5.0 [0.4.0]
1037         */
1038        on: function(event, callback, args){
1039            if(["open", "close", "change"].indexOf(event) < 0 || typeof(callback) != "function"){
1040                return false;
1041            }
1042            if(!(event in this.events)){
1043                this.events[event] = [];
1044            }
1045            this.events[event].push({cb: callback, args: (args instanceof Array)? args: []});
1046            return this;
1047        },
1048
1049        /*
1050         |  PUBLIC :: VALUE
1051         |  @since  0.5.13 [0.5.13]
1052         */
1053        value: function(){
1054            if(this.options.selected.length == 0){
1055                return null;
1056            }
1057            if(this.con.multiple){
1058                return this.options.selected.map(function(opt){
1059                    return opt.value;
1060                });
1061            }
1062            return this.options.selected[0].value;
1063        }
1064    };
1065
1066    /*
1067     |  OPTIONS CONSTRUCTOR
1068     |  @since  0.5.12 [0.3.0]
1069     */
1070    options = select.options = function(select, parent){
1071        if(!(this instanceof options)){
1072            return new options(select, parent);
1073        }
1074        this.self = parent;
1075        this.element = select;
1076        this.length = 0;
1077        this.selected = [];
1078        this.disabled = [];
1079        this.items = {"#": {}};
1080        this.groups = {};
1081        return this;
1082    }
1083
1084    /*
1085     |  TAIL.OPTIONS HANDLER
1086     */
1087    options.prototype = {
1088        /*
1089         |  INTERNAL :: REPLACE TYPOs
1090         |  @since  0.5.0 [0.3.0]
1091         */
1092        _r: function(state){
1093            return state.replace("disabled", "disable").replace("enabled", "enable")
1094                        .replace("selected", "select").replace("unselected", "unselect");
1095        },
1096
1097        /*
1098         |  GET AN EXISTING OPTION
1099         |  @since  0.5.7 [0.3.0]
1100         */
1101        get: function(key, grp){
1102            var g = "getAttribute";
1103            if(typeof(key) == "object" && key.key && key.group){
1104                grp = key.group || grp;
1105                key = key.key;
1106            } else if(key instanceof Element){
1107                if(key.tagName == "OPTION"){
1108                    grp = key.parentElement.label || "#";
1109                    key = key.value || key.innerText;
1110                } else if(key.hasAttribute("data-key")){
1111                    grp = key[g]("data-group") || key.parentElement[g]("data-group") || "#";
1112                    key = key[g]("data-key");
1113                }
1114            } else if(typeof(key) != "string"){
1115                return false;
1116            }
1117            key = (/^[0-9]+$/.test(key))? "_" + key: key;
1118            return (grp in this.items)? this.items[grp][key]: false;
1119        },
1120
1121        /*
1122         |  SET AN EXISTING OPTION
1123         |  @since  0.5.15 [0.3.0]
1124         */
1125        set: function(opt, rebuild){
1126            var key = opt.value || opt.innerText, grp = opt.parentElement.label || "#";
1127            if(!(grp in this.items)){
1128                this.items[grp] = {};
1129                this.groups[grp] = opt.parentElement;
1130            }
1131            if(key in this.items[grp]){
1132                return false;
1133            }
1134            var id = (/^[0-9]+$/.test(key))? "_" + key: key;
1135
1136            // Validate Selection
1137            var con = this.self.con;
1138            if(con.multiple && this.selected.length >= con.multiLimit){
1139                opt.selected = false;
1140            }
1141            if(opt.selected && con.deselect && (!opt.hasAttribute("selected") || con.multiLimit == 0)){
1142                opt.selected = false;
1143                opt.parentElement.selectedIndex = -1;
1144            }
1145
1146            // Sanitize Description
1147            if(opt.hasAttribute("data-description")){
1148                var span = create("SPAN");
1149                    span.innerHTML = opt.getAttribute("data-description");
1150                opt.setAttribute("data-description", span.innerHTML);
1151            }
1152
1153            // Add Item
1154            this.items[grp][id] = {
1155                key: key,
1156                value: opt.text,
1157                description: opt.getAttribute("data-description") || null,
1158                group: grp,
1159                option: opt,
1160                optgroup: (grp != "#")? this.groups[grp]: undefined,
1161                selected: opt.selected,
1162                disabled: opt.disabled,
1163                hidden: opt.hidden || false
1164            };
1165            this.length++;
1166            if(opt.selected){ this.select(this.items[grp][id]); }
1167            if(opt.disabled){ this.disable(this.items[grp][id]); }
1168            return (rebuild)? this.self.callback(this.items[grp][key], "rebuild"): true;
1169        },
1170
1171        /*
1172         |  CREATE A NEW OPTION
1173         |  @since  0.5.13 [0.3.0]
1174         */
1175        add: function(key, value, group, selected, disabled, description, rebuild){
1176            if(key instanceof Object){
1177                for(var k in key){
1178                    this.add(key[k].key || k, key[k].value, key[k].group, key[k].selected, key[k].disabled, key[k].description, false);
1179                }
1180                return this.self.query();
1181            }
1182            if(this.get(key, group)){
1183                return false;
1184            }
1185
1186            // Check Group
1187            group = (typeof(group) == "string")? group: "#";
1188            if(group !== "#" && !(group in this.groups)){
1189                var optgroup = create("OPTGROUP");
1190                optgroup.label = group;
1191                optgroup.setAttribute("data-select-optgroup", "add");
1192                this.element.appendChild(optgroup);
1193                this.items[group] = {};
1194                this.groups[group] = optgroup;
1195            }
1196
1197            // Validate Selection
1198            if(this.self.con.multiple && this.selected.length >= this.self.con.multiLimit){
1199                selected = false;
1200            }
1201            disabled = !!disabled;
1202
1203            // Create Option
1204            var option = d.createElement("OPTION");
1205            option.value = key;
1206            option.selected = selected;
1207            option.disabled = disabled;
1208            option.innerText = value;
1209            option.setAttribute("data-select-option", "add");
1210            if(description && description.length > 0){
1211                option.setAttribute("data-description", description);
1212            }
1213
1214            // Add Option and Return
1215            ((group == "#")? this.element: this.groups[group]).appendChild(option);
1216            return this.set(option, rebuild);
1217        },
1218
1219        /*
1220         |  MOVE AN EXISTING OPTION
1221         |  @since  0.5.0 [0.5.0]
1222         */
1223        move: function(item, group, new_group, rebuild){
1224            if(!(item = this.get(item, group))){ return false; }
1225
1226            // Create Group
1227            if(new_group !== "#" && !(new_group in this.groups)){
1228                var optgroup = create("OPTGROUP");
1229                optgroup.label = new_group;
1230                this.element.appendChild(optgroup);
1231                this.items[new_group] = {};
1232                this.groups[new_group] = optgroup;
1233                this.groups[new_group].appendChild(item.option);
1234            }
1235
1236            // Move To Group
1237            delete this.items[item.group][item.key];
1238            item.group = new_group;
1239            item.optgroup = this.groups[new_group] || undefined;
1240            this.items[new_group][item.key] = item;
1241            return (rebuild)? this.self.query(): true;
1242        },
1243
1244        /*
1245         |  REMOVE AN EXISTING OPTION
1246         |  @since  0.5.7 [0.3.0]
1247         */
1248        remove: function(item, group, rebuild){
1249            if(!(item = this.get(item, group))){ return false; }
1250            if(item.selected){ this.unselect(item); }
1251            if(item.disabled){ this.enable(item); }
1252
1253            // Remove Data
1254            item.option.parentElement.removeChild(item.option);
1255            var id = (/^[0-9]+$/.test(item.key))? "_" + item.key: item.key;
1256            delete this.items[item.group][id];
1257            this.length--;
1258
1259            // Remove Optgroup
1260            if(Object.keys(this.items[item.group]).length === 0){
1261                delete this.items[item.group];
1262                delete this.groups[item.group];
1263            }
1264            return (rebuild)? this.self.query(): true;
1265        },
1266
1267        /*
1268         |  CHECK AN EXISTING OPTION
1269         |  @since  0.5.0 [0.3.0]
1270         */
1271        is: function(state, key, group){
1272            var state = this._r(state), item = this.get(key, group);
1273            if(!item || ["select", "unselect", "disable", "enable"].indexOf(state) < 0){
1274                return null;
1275            }
1276            if(state == "disable" || state == "enable"){
1277                return (state == "disable")? item.disabled: !item.disabled;
1278            } else if(state == "select" || state == "unselect"){
1279                return (state == "select")? item.selected: !item.selected;
1280            }
1281            return false;
1282        },
1283
1284        /*
1285         |  INTERACT WITH AN OPTION
1286         |  @since  0.5.3 [0.3.0]
1287         */
1288        handle: function(state, key, group, _force){
1289            var item = this.get(key, group), state = this._r(state);
1290            if(!item || ["select", "unselect", "disable", "enable"].indexOf(state) < 0){
1291                return null;
1292            }
1293
1294            // Disable || Enable
1295            if(state == "disable" || state == "enable"){
1296                if(!(item.option in this.disabled) && state == "disable"){
1297                    this.disabled.push(item.option);
1298                } else if((item.option in this.disabled) && state == "enable"){
1299                    this.disabled.splice(this.disabled.indexOf(item.option), 1);
1300                }
1301                item.disabled = (state == "disable");
1302                item.option.disabled = (state == "disable");
1303                return this.self.callback.call(this.self, item, state);
1304            }
1305
1306            // Select || Unselect
1307            var dis = (cHAS(this.self.select, "disabled") || item.disabled || item.option.disabled),
1308                lmt = (this.self.con.multiple && this.self.con.multiLimit <= this.selected.length),
1309                sgl = (!this.self.con.multiple && this.selected.indexOf(item.option) > 0),
1310                del = (this.self.con.multiLimit == 0 && this.self.con.deselect == true),
1311                uns = (!this.self.con.multiple && !this.self.con.deselect && _force !== true);
1312            if(state == "select"){
1313                if(dis || lmt || del || sgl){
1314                    return false;
1315                }
1316                if(!this.self.con.multiple){
1317                    for(var i in this.selected){
1318                        this.unselect(this.selected[i], undefined, true);
1319                    }
1320                }
1321                if(this.selected.indexOf(item.option) < 0){
1322                    this.selected.push(item.option);
1323                }
1324            } else if(state == "unselect"){
1325                if(dis || uns){
1326                    return false;
1327                }
1328                this.selected.splice(this.selected.indexOf(item.option), 1);
1329            }
1330            item.selected = (state == "select");
1331            item.option.selected = (state == "select");
1332            item.option[(state.length > 6? "remove": "set") + "Attribute"]("selected", "selected");
1333            return this.self.callback.call(this.self, item, state, _force);
1334        },
1335        enable: function(key, group){
1336            return this.handle("enable", key, group, false);
1337        },
1338        disable: function(key, group){
1339            return this.handle("disable", key, group, false);
1340        },
1341        select: function(key, group){
1342            return this.handle("select", key, group, false);
1343        },
1344        unselect: function(key, group, _force){
1345            return this.handle("unselect", key, group, _force);
1346        },
1347        toggle: function(item, group){
1348            if(!(item = this.get(item, group))){ return false; }
1349            return this.handle((item.selected? "unselect": "select"), item, group, false);
1350        },
1351
1352        /*
1353         |  INVERT CURRENT <STATE>
1354         |  @since  0.5.15 [0.3.0]
1355         */
1356        invert: function(state){
1357            state = this._r(state);
1358            if(["enable", "disable"].indexOf(state) >= 0){
1359                var invert = this.disabled, action = (state == "enable")? "disable": "enable";
1360            } else if(["select", "unselect"].indexOf(state) >= 0){
1361                var invert = this.selected, action = (state == "select")? "unselect": "select";
1362            }
1363            var convert = Array.prototype.filter.call(this, function(element){
1364                return !(element in invert);
1365            }), self = this;
1366
1367            // Loop
1368            [].concat(invert).forEach(function(item){
1369                self.handle.call(self, action, item);
1370            });
1371            [].concat(convert).forEach(function(item){
1372                self.handle.call(self, state, item);
1373            });
1374            return true;
1375        },
1376
1377        /*
1378         |  SET <STATE> ON ALL OPTIONs
1379         |  @since  0.5.0 [0.5.0]
1380         */
1381        all: function(state, group){
1382            var self = this, list = this;
1383            if(group in this.items){
1384                list = Object.keys(this.items[group]);
1385            } else if(["unselect", "enable"].indexOf(state) >= 0){
1386                list = [].concat((state == "unselect")? this.selected: this.disabled);
1387            }
1388            Array.prototype.forEach.call(list, function(item){
1389                self.handle.call(self, state, item, group, false);
1390            });
1391            return true;
1392        },
1393
1394        /*
1395         |  SET <STATE> FOR A BUNCH OF OPTIONs
1396         |  @since  0.5.4 [0.5.3]
1397         */
1398        walk: function(state, items, args){
1399            if(items instanceof Array || items.length){
1400                for(var l = items.length, i = 0; i < l; i++){
1401                    this.handle.apply(this, [state, items[i], null].concat(args));
1402                }
1403            } else if(items instanceof Object){
1404                var self = this;
1405                if(items.forEach){
1406                    items.forEach(function(value){
1407                        self.handle.apply(self, [state, value, null].concat(args));
1408                    });
1409                } else {
1410                    for(var key in items){
1411                        if(typeof(items[key]) != "string" && typeof(items[key]) != "number" && !(items[key] instanceof Element)){
1412                            continue;
1413                        }
1414                        this.handle.apply(this, [state, items[key], (key in this.items? key: null)]).concat(args);
1415                    }
1416                }
1417            }
1418            return this;
1419        },
1420
1421        /*
1422         |  APPLY LINGUSTIC RULES
1423         |  @since  0.5.13 [0.5.13]
1424         */
1425        applyLinguisticRules: function(search, casesensitive){
1426            var rules = this.self.con.linguisticRules, values = [];
1427            
1428            // Prepare Rules
1429            Object.keys(rules).forEach(function(key){ 
1430                values.push("(" + key + "|[" + rules[key] + "])");
1431            });
1432            if(casesensitive){
1433                values = values.concat(values.map(function(s){ return s.toUpperCase(); })); 
1434            }
1435
1436            return search.replace(new RegExp(values.join("|"), (casesensitive)? "g": "ig"), function(m){
1437                return values[[].indexOf.call(arguments, m, 1) - 1];
1438            });
1439        },
1440    
1441
1442        /*
1443         |  FIND SOME OPTIONs - ARRAY EDITION
1444         |  @since  0.5.15 [0.3.0]
1445         */
1446        find: function(search, config){
1447            var self = this, matches, has = {};
1448            
1449            // Get Config
1450            if(!config){
1451                config = this.self.con.searchConfig;
1452            }
1453
1454            // Config Callback
1455            if(typeof config === "function"){
1456                matches = config.bind(this, search);
1457            }
1458
1459            // Config Handler
1460            else {
1461                config = (config instanceof Array)? config: [config];
1462                config.forEach(function(c){
1463                    if(typeof(c) === "string"){ has[c] = true; }
1464                });
1465                has.any = (!has.any)? has.attributes && has.value: has.any;
1466                
1467                // Cleanup & Prepare
1468                if(!has.regex || has.text){
1469                    search = search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1470                }
1471                if(!has.exactglyphes){
1472                    search = this.self.options.applyLinguisticRules(search, has.case);
1473                }
1474                if(has.word){
1475                    search = '\\b' + search + '\\b';
1476                }
1477
1478                // Search
1479                var regex = new RegExp(search, (!has.case)? "mi": "m"),
1480                    sfunc = function(opt){ return regex.test(opt.text || opt.value); };
1481                
1482                // Handle
1483                if(has.any){
1484                    matches = function(opt){ return sfunc(opt) || [].some.call(opt.attributes, sfunc); };
1485                } else if(has.attributes){
1486                    matches = function(opt){ return [].some.call(opt.attributes, sfunc); };
1487                } else {
1488                    matches = sfunc;
1489                }
1490
1491                if(!this.self.con.searchDisabled){
1492                    var temp = matches;
1493                    matches = function(opt){ return !opt.disabled && temp(opt); };
1494                }
1495            }
1496
1497            // Hammer Time
1498            return [].filter.call(this.self.e.options, matches).map(function(opt){
1499                return opt.hidden? false: self.get(opt) 
1500            });
1501        },
1502
1503        /*
1504         |  FIND SOME OPTIONs - WALKER EDITION
1505         |  @since  0.5.5 [0.3.0]
1506         */
1507        finder: function(search, config){
1508            if(this._finderLoop === undefined){
1509                this._finderLoop = this.find(search, config);
1510            }
1511            var item;
1512            while((item = this._finderLoop.shift()) !== undefined){
1513                return item;
1514            }
1515            delete this._finderLoop;
1516            return false;
1517        },
1518
1519        /*
1520         |  NEW OPTIONS WALKER
1521         |  @since  0.5.15 [0.4.0]
1522         */
1523        walker: function(orderi, orderg){
1524            if(typeof(this._inLoop) != "undefined" && this._inLoop){
1525                if(this._inItems.length > 0){
1526                    do {
1527                        var temp = this.items[this._inGroup][this._inItems.shift()];
1528                    } while(temp.hidden === true);
1529                    return temp;
1530                }
1531
1532                // Sort Items
1533                if(this._inGroups.length > 0){
1534                    while(this._inGroups.length > 0){
1535                        var group = this._inGroups.shift();
1536                        if(!(group in this.items)){
1537                            return false;
1538                        }
1539
1540                        var keys = Object.keys(this.items[group]);
1541                        if(keys.length > 0){
1542                            break;
1543                        }
1544                    }
1545                    if(orderi == "ASC"){
1546                        keys.sort();
1547                    } else if(orderi == "DESC"){
1548                        keys.sort().reverse();
1549                    } else if(typeof(orderi) == "function"){
1550                        keys = orderi.call(this, keys);
1551                    }
1552                    this._inItems = keys;
1553                    this._inGroup = group;
1554                    return this.walker(null, null);
1555                }
1556
1557                // Delete and Exit
1558                delete this._inLoop;
1559                delete this._inItems;
1560                delete this._inGroup;
1561                delete this._inGroups;
1562                return false;
1563            }
1564
1565            // Sort Groups
1566            var groups = Object.keys(this.groups) || [];
1567            if(orderg == "ASC"){
1568                groups.sort();
1569            } else if(orderg == "DESC"){
1570                groups.sort().reverse();
1571            } else if(typeof(orderg) == "function"){
1572                groups = orderg.call(this, groups);
1573            }
1574            groups.unshift("#");
1575
1576            // Init Loop
1577            this._inLoop = true;
1578            this._inItems = [];
1579            this._inGroups = groups;
1580            return this.walker(orderi, null);
1581        }
1582    };
1583
1584    // Return
1585    return select;
1586}));

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.