PageSourceSearch

https://service.essbay.de/_assets/4aa87cbc97b19002293e590a434cf15d…/jquery.multiselect.filter.js?1741693464

js essbay.de collected 2026-10-04 13:25:22 UTC 10,343 bytes, 286 lines download raw bytes

vendor: 10,343 bytes, lines 1-286
1/* jshint forin:true, noarg:true, noempty:true, eqeqeq:true, boss:true, undef:true, curly:true, browser:true, jquery:true */
2/*
3 * jQuery MultiSelect UI Widget Filtering Plugin 3.0.0
4 * Copyright (c) 2012 Eric Hynds
5 *
6 * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
7 *
8 * Depends:
9 *   - jQuery UI MultiSelect widget
10 *
11 * Dual licensed under the MIT and GPL licenses:
12 *   http://www.opensource.org/licenses/mit-license.php
13 *   http://www.gnu.org/licenses/gpl.html
14 *
15 */
16(function($) {
17  var rEscape = /[\-\[\]{}()*+?.,\\\^$|#\s]/g;
18
19  // "{{term}}" is a placeholder below for where the search term
20  // would be inserted in the resulting regular expression.
21  var filterRules = {
22      contains: '{{term}}',
23      beginsWith: '^{{term}}',
24      endsWith: '{{term}}$',
25      exactMatch: '^{{term}}$',
26      containsNumber: '\d',
27      isNumeric: '^\d+$',
28      isNonNumeric: '^\D+$',
29  };
30
31  var headerSelector = '.ui-multiselect-header';
32  var hasFilterClass = 'ui-multiselect-hasfilter';
33  var filterClass = 'ui-multiselect-filter';
34  var optgroupClass = 'ui-multiselect-optgroup';
35  var groupLabelClass = 'ui-multiselect-grouplabel';
36  var hiddenClass = 'ui-multiselect-excluded';
37
38  /**
39   * This comes courtesy of underscore.js
40   * @param {function} func to debounce
41   * @param {number} wait period
42   * @param {bool} immediate perform once immediately
43   * @return {function} input function with a debounce period
44   */
45  function debounce(func, wait, immediate) {
46    var timeout;
47    return function() {
48      var context = this; var args = arguments; // eslint-disable-line prefer-rest-params
49      var later = function() {
50        timeout = null;
51        if (!immediate) {
52          func.apply(context, args);
53        }
54      };
55      var callNow = immediate && !timeout;
56      clearTimeout(timeout);
57      timeout = setTimeout(later, wait);
58      if (callNow) {
59        func.apply(context, args);
60      }
61    };
62  }
63
64  $.widget('ech.multiselectfilter', {
65
66    options: {
67      label: 'Filter:', // (string) The label to show with the input
68      placeholder: 'Enter keywords', // (string) The placeholder text to show in the input
69      filterRule: 'contains', // (string) Either a named filter rule from above or a regular expression containing {{term}} as a placeholder
70      searchGroups: false, // (true | false) If true, search option group labels and show an entire group on a match.
71      autoReset: false, // (true | false) If true, clear the filter each time the widget menu is closed.
72      width: null, // (number) Override default width set in css file (px). null will inherit
73      debounceMS: 250, // (number) Number of milleseconds to wait between running the search handler.
74    },
75
76   /**
77    * Performs widget creation
78    * Widget API has already set this.element and this.options for us
79    *   - Find the multiselect widget.
80    *   - Create the filter input
81    *   - Set up event handlers
82    *   - Insert in header
83    * - Create text cache
84    *   - Override toggleState
85    */
86    _create: function() {
87      var opts = this.options;
88      var $element = this.element;
89
90      // get the multiselect instance
91      this.instance = $element.data('ech-multiselect');
92
93      // store header; add filter class so the close/check all/uncheck all links can be positioned correctly
94      this.$header = this.instance.$menu.find(headerSelector).addClass(hasFilterClass);
95
96      // wrapper $element
97      this.$input = $(document.createElement('input'))
98        .attr({
99          placeholder: opts.placeholder,
100          type: 'search',
101        })
102        .css({width: (typeof opts.width === 'string')
103                       ? this.instance._parse2px(opts.width, this.$header).px + 'px'
104                       : (/\d/.test(opts.width) ? opts.width + 'px' : null),
105             });
106      this._bindInputEvents();
107      // automatically reset the widget on close?
108      if (this.options.autoReset) {
109        $element.on('multiselectbeforeclose', $.proxy(this._reset, this));
110      }
111
112      var $label = $(document.createElement('label')).text(opts.label).append(this.$input).addClass('ui-multiselect-filter-label');
113      this.$wrapper = $(document.createElement('div'))
114                                .addClass(filterClass)
115                                .append($label)
116                                .prependTo(this.$header);
117
118      // If menu already opened, have to reset menu height since
119      // addition of the filter input changes the header height calc.
120      if (!!this.instance._isOpen) {
121         this.instance._setMenuHeight(true);
122      }
123
124      // cache input values for searching
125      this.updateCache();
126
127      // Change the normal _toggleChecked fxn behavior so that when checkAll/uncheckAll
128      // is fired, only the currently displayed filtered inputs are checked if filter entered.
129      var instance = this.instance;
130      var filter = this.$input[0];
131      instance._oldToggleChecked = instance._toggleChecked;
132      instance._toggleChecked = function(flag, group) {
133         instance._oldToggleChecked(flag, group, !!filter.value);
134      };
135    },
136
137    /**
138     * Binds keyboard events to the input
139     * This is where special behavior like ALT-R for reset is bound
140     */
141    _bindInputEvents: function() {
142      this.$input.on({
143        keydown: function(e) {
144          // prevent the enter key from submitting the form / closing the widget
145          if (e.which === 13) {
146            e.preventDefault();
147          } else if (e.which === 27) {
148            $element.multiselect('close');
149            e.preventDefault();
150          } else if (e.which === 9 && e.shiftKey) {
151            $element.multiselect('close');
152            e.preventDefault();
153          } else if (e.altKey) {
154            switch (e.which) {
155              case 82:
156                e.preventDefault();
157                $(this).val('').trigger('input', '');
158                break;
159              case 65:
160                $element.multiselect('checkAll');
161                break;
162              case 85:
163                $element.multiselect('uncheckAll');
164                break;
165              case 70:
166                $element.multiselect('flipAll');
167                break;
168              case 76:
169                $element.multiselect('instance').$labels.first().trigger('mouseenter');
170                break;
171            }
172          }
173        },
174        input: $.proxy(debounce(this._handler, this.options.debounceMS), this),
175        search: $.proxy(this._handler, this),
176      });
177    },
178
179   /**
180    * Handles searches as text is entered in the filter box.
181    * Uses a text cache to speed up searching.
182    * Debouncing is done to limit how often this is ran.
183    * Alternate filter rules can be used.
184    * Option group labels may be searched, also.
185    * @param {event} e event object from original event.
186    */
187    _handler: function(e) {
188      var term = this.$input[0].value.toLowerCase().replace(/^\s+|\s+$/g, '');
189      var filterRule = this.options.filterRule || 'contains';
190      var regex = new RegExp( ( filterRules[filterRule] || filterRule ).replace('{{term}}', term.replace(rEscape, '\\$&')), 'i');
191      var searchGroups = !!this.options.searchGroups;
192      var $checkboxes = this.instance.$checkboxes;
193      var cache = this.cache; // Cached text() object
194
195      this.$rows.toggleClass(hiddenClass, !!term);
196      var filteredInputs = $checkboxes.children().map(function(x) {
197        var elem = this;
198        var $groupItems = $(elem);
199        var groupShown = false;
200
201         // Account for optgroups
202         // If we are searching in option group labels and we match an optgroup label,
203         // then show all its children and return all its inputs also.
204        if (elem.classList.contains(optgroupClass)) {
205          var $groupItems = $groupItems.find('li');
206          if (searchGroups && regex.test( cache[x] ) ) {
207             elem.classList.remove(hiddenClass);
208             $groupItems.removeClass(hiddenClass);
209             return $groupItems.find('input').get();
210          }
211        }
212
213        return $groupItems.map(function(y) {
214          if ( regex.test( cache[x + '.' + y] ) ) {
215            // Show the opt group heading if needed
216            if (!groupShown) {
217               elem.classList.remove(hiddenClass);
218               groupShown = true;
219            }
220            this.classList.remove(hiddenClass);
221            return this.getElementsByTagName('input')[0];
222          }
223          return null;
224        });
225      });
226      if (term) {
227         this._trigger('filter', e, filteredInputs);
228      }
229      if (!this.instance.options.listbox && this.instance._isOpen) {
230         this.instance._setMenuHeight(true);
231         this.instance.position();
232      }
233      return;
234    },
235
236    _reset: function() {
237      this.$input.val('');
238      var event = document.createEvent('Event');
239      event.initEvent('reset', true, true);
240      this.$input.get(0).dispatchEvent(event);
241      this._handler(event);
242    },
243
244   /**
245    * Creates a text cache object from the widget options' text.
246    * @param {Boolean} alsoRefresh causes the displayed search results to refresh.
247    */
248    updateCache: function(alsoRefresh) {
249      var cache = {}; // keys are like 0, 0.1, 1, 1.0, 1.1 etc.
250      this.instance.$checkboxes.children().each(function(x) {
251         var $element = $(this);
252         // Account for optgroups
253         if (this.classList.contains(optgroupClass)) {
254            // Single number keys are the option labels
255            cache[x] = this.getElementsByClassName(groupLabelClass)[0].textContent;
256            $element = $element.find('li');
257         }
258         $element.each(function(y) {
259            cache[x + '.' + y] = this.textContent;
260         });
261      });
262      this.cache = cache;
263      this.$rows = this.instance.$checkboxes.find('li');
264      if (!!alsoRefresh) {
265         this._handler();
266      }
267    },
268
269   /**
270    * @return {object} Returns the input wrapper div
271    */
272    widget: function() {
273      return this.$wrapper;
274    },
275
276   /**
277    * Destroys this widget
278    */
279    destroy: function() {
280      $.Widget.prototype.destroy.call(this);
281      this.$input.val('').trigger('keyup').off('keydown input search');
282      this.instance.$menu.find(headerSelector).removeClass(hasFilterClass);
283      this.$wrapper.remove();
284    },
285  });
286})(jQuery);

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.