PageSourceSearch

https://www.borsaitaliana.it/media-rwd/assets/extra-js/multiselect/jquery.multiselect.js

js borsaitaliana.it collected 2026-10-02 02:16:34 UTC 22,354 bytes, 738 lines download raw bytes

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