PageSourceSearch

https://www.euro-coins.tv/multiselect/jquery.multiselect.js

js euro-coins.tv collected 2026-10-02 23:22:47 UTC 22,500 bytes, 742 lines download raw bytes

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 1.14pre
4 * Copyright (c) 2012 Eric Hynds
5 *
6 * http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
7 *
8 * Depends:
9 *   - jQuery 1.4.2+
10 *   - jQuery UI 1.8 widget factory
11 *
12 * Optional:
13 *   - jQuery UI effects
14 *   - jQuery UI position utility
15 *
16 * Dual licensed under the MIT and GPL licenses:
17 *   http://www.opensource.org/licenses/mit-license.php
18 *   http://www.gnu.org/licenses/gpl.html
19 *
20 */
21(function($, undefined) {
22
23  var multiselectID = 0;
24  var $doc = $(document);
25
26  $.widget("ech.multiselect", {
27
28    // default options
29    options: {
30      header: true,
31      //height: 175,
32      height: 250,
33      //minWidth: 225,
34      //minWidth: 100,
35      classes: '',
36      //checkAllText: 'Check all',
37      checkAllText: 'Alle Auswählen',
38      //uncheckAllText: 'Uncheck all',
39      uncheckAllText: 'Alle Abwählen',
40      //noneSelectedText: 'Select options',
41      noneSelectedText: 'Rubriken auswählen',
42      //selectedText: '# selected',
43      selectedText: '# Rubriken ausgewählt',
44      selectedList: 0,
45      show: null,
46      hide: null,
47      autoOpen: false,
48      multiple: true,
49      position: {},
50      appendTo: "body"
51      //appendTo: "test"
52    },
53
54    _create: function() {
55      var el = this.element.hide();
56      var o = this.options;
57
58      this.speed = $.fx.speeds._default; // default speed for effects
59      this._isOpen = false; // assume no
60
61      // create a unique namespace for events that the widget
62      // factory cannot unbind automatically. Use eventNamespace if on
63      // jQuery UI 1.9+, and otherwise fallback to a custom string.
64      this._namespaceID = this.eventNamespace || ('multiselect' + multiselectID);
65
66      var button = (this.button = $('<button type="button"><span class="ui-icon ui-icon-triangle-1-s"></span></button>'))
67        .addClass('ui-multiselect ui-widget ui-state-default ui-corner-all')
68        .addClass(o.classes)
69        .attr({ 'title':el.attr('title'), 'aria-haspopup':true, 'tabIndex':el.attr('tabIndex') })
70        .insertAfter(el),
71
72        buttonlabel = (this.buttonlabel = $('<span />'))
73          .html(o.noneSelectedText)
74          .appendTo(button),
75
76        menu = (this.menu = $('<div />'))
77          .addClass('ui-multiselect-menu ui-widget ui-widget-content ui-corner-all')
78          .addClass(o.classes)
79          .appendTo($(o.appendTo)),
80
81        header = (this.header = $('<div />'))
82          .addClass('ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix')
83          .appendTo(menu),
84
85        headerLinkContainer = (this.headerLinkContainer = $('<ul />'))
86          .addClass('ui-helper-reset')
87          .html(function() {
88            if(o.header === true) {
89              return '<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>' + o.checkAllText + '</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>' + o.uncheckAllText + '</span></a></li>';
90            } else if(typeof o.header === "string") {
91              return '<li>' + o.header + '</li>';
92            } else {
93              return '';
94            }
95          })
96          .append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>')
97          .appendTo(header),
98
99        checkboxContainer = (this.checkboxContainer = $('<ul />'))
100          .addClass('ui-multiselect-checkboxes ui-helper-reset')
101          .appendTo(menu);
102
103        // perform event bindings
104        this._bindEvents();
105
106        // build menu
107        this.refresh(true);
108
109        // some addl. logic for single selects
110        if(!o.multiple) {
111          menu.addClass('ui-multiselect-single');
112        }
113
114        // bump unique ID
115        multiselectID++;
116    },
117
118    _init: function() {
119      if(this.options.header === false) {
120        this.header.hide();
121      }
122      if(!this.options.multiple) {
123        this.headerLinkContainer.find('.ui-multiselect-all, .ui-multiselect-none').hide();
124      }
125      if(this.options.autoOpen) {
126        this.open();
127      }
128      if(this.element.is(':disabled')) {
129        this.disable();
130      }
131    },
132
133    refresh: function(init) {
134      var el = this.element;
135      var o = this.options;
136      var menu = this.menu;
137      var checkboxContainer = this.checkboxContainer;
138      var optgroups = [];
139      var html = "";
140      var id = el.attr('id') || multiselectID++; // unique ID for the label & option tags
141
142      // build items
143      el.find('option').each(function(i) {
144        var $this = $(this);
145        var parent = this.parentNode;
146        var description = this.innerHTML;
147        var title = this.title;
148        var value = this.value;
149        var inputID = 'ui-multiselect-' + (this.id || id + '-option-' + i);
150        var isDisabled = this.disabled;
151        var isSelected = this.selected;
152        var labelClasses = [ 'ui-corner-all' ];
153        var liClasses = (isDisabled ? 'ui-multiselect-disabled ' : ' ') + this.className;
154        var optLabel;
155
156        // is this an optgroup?
157        if(parent.tagName === 'OPTGROUP') {
158          optLabel = parent.getAttribute('label');
159
160          // has this optgroup been added already?
161          if($.inArray(optLabel, optgroups) === -1) {
162            html += '<li class="ui-multiselect-optgroup-label ' + parent.className + '"><a href="#">
162' + optLabel + '</a></li>';
163            optgroups.push(optLabel);
164          }
165        }
166
167        if(isDisabled) {
168          labelClasses.push('ui-state-disabled');
169        }
170
171        // browsers automatically select the first option
172        // by default with single selects
173        if(isSelected && !o.multiple) {
174          labelClasses.push('ui-state-active');
175        }
176
177        html += '<li class="' + liClasses + '">';
178
179        // create the label
180        html += '<label for="' + inputID + '" title="' + title + '" class="' + labelClasses.join(' ') + '">';
181        html += '<input id="' + inputID + '" name="multiselect_' + id + '" type="' + (o.multiple ? "checkbox" : "radio") + '" value="' + value + '" title="' + title + '"';
182
183        // pre-selected?
184        if(isSelected) {
185          html += ' checked="checked"';
186          html += ' aria-selected="true"';
187        }
188
189        // disabled?
190        if(isDisabled) {
191          html += ' disabled="disabled"';
192          html += ' aria-disabled="true"';
193        }
194
195        // add the title and close everything off
196        html += ' /><span>' + description + '</span></label></li>';
197      });
198
199      // insert into the DOM
200      checkboxContainer.html(html);
201
202      // cache some moar useful elements
203      this.labels = menu.find('label');
204      this.inputs = this.labels.children('input');
205
206      // set widths
207      this._setButtonWidth();
208      this._setMenuWidth();
209
210      // remember default value
211      this.button[0].defaultValue = this.update();
212
213      // broadcast refresh event; useful for widgets
214      if(!init) {
215        this._trigger('refresh');
216      }
217    },
218
219    // updates the button text. call refresh() to rebuild
220    update: function() {
221      var o = this.options;
222      var $inputs = this.inputs;
223      var $checked = $inputs.filter(':checked');
224      var numChecked = $checked.length;
225      var value;
226
227      if(numChecked === 0) {
228        value = o.noneSelectedText;
229      } else {
230        if($.isFunction(o.selectedText)) {
231          value = o.selectedText.call(this, numChecked, $inputs.length, $checked.get());
232        } else if(/\d/.test(o.selectedList) && o.selectedList > 0 && numChecked <= o.selectedList) {
233          value = $checked.map(function() { return $(this).next().html(); }).get().join(', ');
234        } else {
235          value = o.selectedText.replace('#', numChecked).replace('#', $inputs.length);
236        }
237      }
238
239      this._setButtonValue(value);
240
241      return value;
242    },
243
244    // this exists as a separate method so that the developer 
245    // can easily override it.
246    _setButtonValue: function(value) {
247      this.buttonlabel.text(value);
248    },
249
250    // binds events
251    _bindEvents: function() {
252      var self = this;
253      var button = this.button;
254
255      function clickHandler() {
256        self[ self._isOpen ? 'close' : 'open' ]();
257        return false;
258      }
259
260      // webkit doesn't like it when you click on the span :(
261      button
262        .find('span')
263        .bind('click.multiselect', clickHandler);
264
265      // button events
266      button.bind({
267        click: clickHandler,
268        keypress: function(e) {
269          switch(e.which) {
270            case 27: // esc
271              case 38: // up
272              case 37: // left
273              self.close();
274            break;
275            case 39: // right
276              case 40: // down
277              self.open();
278            break;
279          }
280        },
281        mouseenter: function() {
282          if(!button.hasClass('ui-state-disabled')) {
283            $(this).addClass('ui-state-hover');
284          }
285        },
286        mouseleave: function() {
287          $(this).removeClass('ui-state-hover');
288        },
289        focus: function() {
290          if(!button.hasClass('ui-state-disabled')) {
291            $(this).addClass('ui-state-focus');
292          }
293        },
294        blur: function() {
295          $(this).removeClass('ui-state-focus');
296        }
297      });
298
299      // header links
300      this.header.delegate('a', 'click.multiselect', function(e) {
301        // close link
302        if($(this).hasClass('ui-multiselect-close')) {
303          self.close();
304
305          // check all / uncheck all
306        } else {
307          self[$(this).hasClass('ui-multiselect-all') ? 'checkAll' : 'uncheckAll']();
308        }
309
310        e.preventDefault();
311      });
312
313      // optgroup label toggle support
314      this.menu.delegate('li.ui-multiselect-optgroup-label a', 'click.multiselect', function(e) {
315        e.preventDefault();
316
317        var $this = $(this);
318        var $inputs = $this.parent().nextUntil('li.ui-multiselect-optgroup-label').find('input:visible:not(:disabled)');
319        var nodes = $inputs.get();
320        var label = $this.parent().text();
321
322        // trigger event and bail if the return is false
323        if(self._trigger('beforeoptgrouptoggle', e, { inputs:nodes, label:label }) === false) {
324          return;
325        }
326
327        // toggle inputs
328        self._toggleChecked(
329          $inputs.filter(':checked').length !== $inputs.length,
330          $inputs
331        );
332
333        self._trigger('optgrouptoggle', e, {
334          inputs: nodes,
335          label: label,
336          checked: nodes[0].checked
337        });
338      })
339      .delegate('label', 'mouseenter.multiselect', function() {
340        if(!$(this).hasClass('ui-state-disabled')) {
341          self.labels.removeClass('ui-state-hover');
342          $(this).addClass('ui-state-hover').find('input').focus();
343        }
344      })
345      .delegate('label', 'keydown.multiselect', function(e) {
346        e.preventDefault();
347
348        switch(e.which) {
349          case 9: // tab
350            case 27: // esc
351            self.close();
352          break;
353          case 38: // up
354            case 40: // down
355            case 37: // left
356            case 39: // right
357            self._traverse(e.which, this);
358          break;
359          case 13: // enter
360            $(this).find('input')[0].click();
361          break;
362        }
363      })
364      .delegate('input[type="checkbox"], input[type="radio"]', 'click.multiselect', function(e) {
365        var $this = $(this);
366        var val = this.value;
367        var checked = this.checked;
368        var tags = self.element.find('option');
369
370        // bail if this input is disabled or the event is cancelled
371        if(this.disabled || self._trigger('click', e, { value: val, text: this.title, checked: checked }) === false) {
372          e.preventDefault();
373          return;
374        }
375
376        // make sure the input has focus. otherwise, the esc key
377        // won't close the menu after clicking an item.
378        $this.focus();
379
380        // toggle aria state
381        $this.attr('aria-selected', checked);
382
383        // change state on the original option tags
384        tags.each(function() {
385          if(this.value === val) {
386            this.selected = checked;
387          } else if(!self.options.multiple) {
388            this.selected = false;
389          }
390        });
391
392        // some additional single select-specific logic
393        if(!self.options.multiple) {
394          self.labels.removeClass('ui-state-active');
395          $this.closest('label').toggleClass('ui-state-active', checked);
396
397          // close menu
398          self.close();
399        }
400
401        // fire change on the select box
402        self.element.trigger("change");
403
404        // setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
405        // http://bugs.jquery.com/ticket/3827
406        setTimeout($.proxy(self.update, self), 10);
407      });
408
409      // close each widget when clicking on any other element/anywhere else on the page
410      $doc.bind('mousedown.' + this._namespaceID, function(event) {
411        var target = event.target;
412
413        if(self._isOpen
414            && target !== self.button[0]
415            && target !== self.menu[0]
416            && !$.contains(self.menu[0], target)
417            && !$.contains(self.button[0], target)
418          ) {
419          self.close();
420        }
421      });
422
423      // deal with form resets.  the problem here is that buttons aren't
424      // restored to their defaultValue prop on form reset, and the reset
425      // handler fires before the form is actually reset.  delaying it a bit
426      // gives the form inputs time to clear.
427      $(this.element[0].form).bind('reset.multiselect', function() {
428        setTimeout($.proxy(self.refresh, self), 10);
429      });
430    },
431
432    // set button width
433    _setButtonWidth: function() {
434      var width = this.element.outerWidth();
435      var o = this.options;
436
437      if(/\d/.test(o.minWidth) && width < o.minWidth) {
438        width = o.minWidth;
439      }
440
441      // set widths
442      this.button.outerWidth(width);
443    },
444
445    // set menu width
446    _setMenuWidth: function() {
447      var m = this.menu;
448      m.outerWidth(this.button.outerWidth());
449    },
450
451    // move up or down within the menu
452    _traverse: function(which, start) {
453      var $start = $(start);
454      var moveToLast = which === 38 || which === 37;
455
456      // select the first li that isn't an optgroup label / disabled
457      var $next = $start.parent()[moveToLast ? 'prevAll' : 'nextAll']('li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)').first();
458
459      // if at the first/last element
460      if(!$next.length) {
461        var $container = this.menu.find('ul').last();
462
463        // move to the first/last
464        this.menu.find('label')[ moveToLast ? 'last' : 'first' ]().trigger('mouseover');
465
466        // set scroll position
467        $container.scrollTop(moveToLast ? $container.height() : 0);
468
469      } else {
470        $next.find('label').trigger('mouseover');
471      }
472    },
473
474    // This is an internal function to toggle the checked property and
475    // other related attributes of a checkbox.
476    //
477    // The context of this function should be a checkbox; do not proxy it.
478    _toggleState: function(prop, flag) {
479      return function() {
480        if(!this.disabled) {
481          this[ prop ] = flag;
482        }
483
484        if(flag) {
485          this.setAttribute('aria-selected', true);
486        } else {
487          this.removeAttribute('aria-selected');
488        }
489      };
490    },
491
492    _toggleChecked: function(flag, group) {
493      var $inputs = (group && group.length) ?  group : this.inputs;
494      var self = this;
495
496      // toggle state on inputs
497      $inputs.each(this._toggleState('checked', flag));
498
499      // give the first input focus
500      $inputs.eq(0).focus();
501
502      // update button text
503      this.update();
504
505      // gather an array of the values that actually changed
506      var values = $inputs.map(function() {
507        return this.value;
508      }).get();
509
510      // toggle state on original option tags
511      this.element
512        .find('option')
513        .each(function() {
514          if(!this.disabled && $.inArray(this.value, values) > -1) {
515            self._toggleState('selected', flag).call(this);
516          }
517        });
518
519      // trigger the change event on the select
520      if($inputs.length) {
521        this.element.trigger("change");
522      }
523    },
524
525    _toggleDisabled: function(flag) {
526      this.button.attr({ 'disabled':flag, 'aria-disabled':flag })[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled');
527
528      var inputs = this.menu.find('input');
529      var key = "ech-multiselect-disabled";
530
531      if(flag) {
532        // remember which elements this widget disabled (not pre-disabled)
533        // elements, so that they can be restored if the widget is re-enabled.
534        inputs = inputs.filter(':enabled').data(key, true)
535      } else {
536        inputs = inputs.filter(function() {
537          return $.data(this, key) === true;
538        }).removeData(key);
539      }
540
541      inputs
542        .attr({ 'disabled':flag, 'arial-disabled':flag })
543        .parent()[ flag ? 'addClass' : 'removeClass' ]('ui-state-disabled');
544
545      this.element.attr({
546        'disabled':flag,
547        'aria-disabled':flag
548      });
549    },
550
551    // open the menu
552    open: function(e) {
553      var self = this;
554      var button = this.button;
555      var menu = this.menu;
556      var speed = this.speed;
557      var o = this.options;
558      var args = [];
559
560      // bail if the multiselectopen event returns false, this widget is disabled, or is already open
561      if(this._trigger('beforeopen') === false || button.hasClass('ui-state-disabled') || this._isOpen) {
562        return;
563      }
564
565      var $container = menu.find('ul').last();
566      var effect = o.show;
567
568      // figure out opening effects/speeds
569      if($.isArray(o.show)) {
570        effect = o.show[0];
571        speed = o.show[1] || self.speed;
572      }
573
574      // if there's an effect, assume jQuery UI is in use
575      // build the arguments to pass to show()
576      if(effect) {
577        args = [ effect, speed ];
578      }
579
580      // set the scroll of the checkbox container
581      $container.scrollTop(0).height(o.height);
582
583      // positon
584      this.position();
585
586      // show the menu, maybe with a speed/effect combo
587      $.fn.show.apply(menu, args);
588
589      // select the first not disabled option
590      // triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover
591      // will actually trigger mouseenter.  the mouseenter trigger is there for when it's eventually fixed
592      this.labels.filter(':not(.ui-state-disabled)').eq(0).trigger('mouseover').trigger('mouseenter').find('input').trigger('focus');
593
594      button.addClass('ui-state-active');
595      this._isOpen = true;
596      this._trigger('open');
597    },
598
599    // close the menu
600    close: function() {
601      if(this._trigger('beforeclose') === false) {
602        return;
603      }
604
605      var o = this.options;
606      var effect = o.hide;
607      var speed = this.speed;
608      var args = [];
609
610      // figure out opening effects/speeds
611      if($.isArray(o.hide)) {
612        effect = o.hide[0];
613        speed = o.hide[1] || this.speed;
614      }
615
616      if(effect) {
617        args = [ effect, speed ];
618      }
619
620      $.fn.hide.apply(this.menu, args);
621      this.button.removeClass('ui-state-active').trigger('blur').trigger('mouseleave');
622      this._isOpen = false;
623      this._trigger('close');
624    },
625
626    enable: function() {
627      this._toggleDisabled(false);
628    },
629
630    disable: function() {
631      this._toggleDisabled(true);
632    },
633
634    checkAll: function(e) {
635      this._toggleChecked(true);
636      this._trigger('checkAll');
637    },
638
639    uncheckAll: function() {
640      this._toggleChecked(false);
641      this._trigger('uncheckAll');
642    },
643
644    getChecked: function() {
645      return this.menu.find('input').filter(':checked');
646    },
647
648    destroy: function() {
649      // remove classes + data
650      $.Widget.prototype.destroy.call(this);
651
652      // unbind events
653      $doc.unbind(this._namespaceID);
654
655      this.button.remove();
656      this.menu.remove();
657      this.element.show();
658
659      return this;
660    },
661
662    isOpen: function() {
663      return this._isOpen;
664    },
665
666    widget: function() {
667      return this.menu;
668    },
669
670    getButton: function() {
671      return this.button;
672    },
673
674    position: function() {
675      var o = this.options;
676
677      // use the position utility if it exists and options are specifified
678      if($.ui.position && !$.isEmptyObject(o.position)) {
679        o.position.of = o.position.of || this.button;
680
681        this.menu
682          .show()
683          .position(o.position)
684          .hide();
685
686        // otherwise fallback to custom positioning
687      } else {
688        var pos = this.button.offset();
689
690        this.menu.css({
691          top: pos.top + this.button.outerHeight(),
692          left: pos.left
693        });
694      }
695    },
696
697    // react to option changes after initialization
698    _setOption: function(key, value) {
699      var menu = this.menu;
700
701      switch(key) {
702        case 'header':
703          menu.find('div.ui-multiselect-header')[value ? 'show' : 'hide']();
704          break;
705        case 'checkAllText':
706          menu.find('a.ui-multiselect-all span').eq(-1).text(value);
707          break;
708        case 'uncheckAllText':
709          menu.find('a.ui-multiselect-none span').eq(-1).text(value);
710          break;
711        case 'height':
712          menu.find('ul').last().height(parseInt(value, 10));
713          break;
714        case 'minWidth':
715          this.options[key] = parseInt(value, 10);
716          this._setButtonWidth();
717          this._setMenuWidth();
718          break;
719        case 'selectedText':
720        case 'selectedList':
721        case 'noneSelectedText':
722          this.options[key] = value; // these all needs to update immediately for the update() call
723          this.update();
724          break;
725        case 'classes':
726          menu.add(this.button).removeClass(this.options.classes).addClass(value);
727          break;
728        case 'multiple':
729          menu.toggleClass('ui-multiselect-single', !value);
730          this.options.multiple = value;
731          this.element[0].multiple = value;
732          this.refresh();
733          break;
734        case 'position':
735          this.position();
736      }
737
738      $.Widget.prototype._setOption.apply(this, arguments);
739    }
740  });
741
742})(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.