PageSourceSearch

https://gunsitestore.com/js/jquery.multiselect.js

js gunsitestore.com collected 2026-09-26 16:51:57 UTC 22,867 bytes, 551 lines download raw bytes

1/**
2 * Display a nice easy to use multiselect list
3 * @Version: 2.0
4 * @Author: Patrick Springstubbe
5 * @Contact: @JediNobleclem
6 * @Website: springstubbe.us
7 * @Source: https://github.com/nobleclem/jQuery-MultiSelect
8 * @Notes: If select list is hidden on page load use the jquery.actual plugin
9 *         to resolve issues with preselected items placeholder text
10 *         https://github.com/dreamerslab/jquery.actual
11 *
12 * Usage:
13 *     $('select[multiple]').multiselect();
14 *     $('select[multiple]').multiselect({ placeholder: 'Select options' });
15 *     $('select[multiple]').multiselect('reload');
16 *     $('select[multiple]').multiselect( 'loadOption', [{
17 *         name   : 'Option Name 1',
18 *         value  : 'option-value-1',
19 *         checked: false
20 *     },{
21 *         name   : 'Option Name 2',
22 *         value  : 'option-value-2',
23 *         checked: false
24 *     }]);
25 *
26 **/
27(function($){
28    var defaults = {
29        placeholder   : 'Select options', // text to use in dummy input
30        columns       : 1,                // how many columns should be use to show options
31        search        : false,            // include option search box
32        // search filter options
33        searchOptions : {
34            'default'    : 'Search',             // search input placeholder text
35            showOptGroups: false,                // show option group titles if no options remaining
36            onSearch     : function( element ){} // fires on keyup before search on options happens
37        },
38        selectAll     : true, // add select all option
39        selectGroup   : false, // select entire optgroup
40        minHeight     : 200,   // minimum height of option overlay
41        maxHeight     : null,  // maximum height of option overlay
42        showCheckbox  : true,  // display the checkbox to the user
43        jqActualOpts  : {},    // options for jquery.actual
44
45        // Callbacks
46        onLoad        : function( element ) {  // fires at end of list initialization
47            $(element).hide();
48        },
49        onOptionClick : function( element, option ){}, // fires when an option is clicked
50
51        // @NOTE: these are for future development
52        maxWidth      : null,  // maximum width of option overlay (or selector)
53        minSelect     : false, // minimum number of items that can be selected
54        maxSelect     : false, // maximum number of items that can be selected
55    };
56
57    var msCounter = 1;
58
59    function MultiSelect( element, options )
60    {
61        this.element = element;
62        this.options = $.extend( {}, defaults, options );
63        this.load();
64    }
65
66    MultiSelect.prototype = {
67        /* LOAD CUSTOM MULTISELECT DOM/ACTIONS */
68        load: function() {
69            var instance = this;
70
71            // make sure this is a select list and not loaded
72            if( (instance.element.nodeName != 'SELECT') || $(instance.element).hasClass('jqmsLoaded') ) {
73                return true;
74            }
75
76            // sanity check so we don't double load on a select element
77            $(instance.element).addClass('jqmsLoaded');
78
79            // add option container
80            $(instance.element).after('<div class="ms-options-wrap"><button>None Selected</button><div class="ms-options"><ul></ul></div></div>');
81            var placeholder = $(instance.element).next('.ms-options-wrap').find('> button:first-child');
82            var optionsWrap = $(instance.element).next('.ms-options-wrap').find('> .ms-options');
83            var optionsList = optionsWrap.find('> ul');
84            var hasOptGroup = $(instance.element).find('optgroup').length ? true : false;
85
86            var maxWidth = null;
87            if( typeof instance.options.width == 'number' ) {
88                optionsWrap.parent().css( 'position', 'relative' );
89                maxWidth = instance.options.width;
90            }
91            else if( typeof instance.options.width == 'string' ) {
92                $( instance.options.width ).css( 'position', 'relative' );
93                maxWidth = '100%';
94            }
95            else {
96                optionsWrap.parent().css( 'position', 'relative' );
97            }
98
99            var maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
100            if( instance.options.maxHeight ) {
101                maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
102                maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxheight;
103            }
104
105            maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxHeight;
106
107            optionsWrap.css({
108                maxWidth : maxWidth,
109                minHeight: instance.options.minHeight,
110                maxHeight: maxHeight,
111                overflow : 'auto'
112            }).hide();
113
114            // isolate options scroll
115            // @source: https://github.com/nobleclem/jQuery-IsolatedScroll
116            optionsWrap.bind( 'touchmove mousewheel DOMMouseScroll', function ( e ) {
117                if( ($(this).outerHeight() < $(this)[0].scrollHeight) ) {
118                    var e0 = e.originalEvent,
119                        delta = e0.wheelDelta || -e0.detail;
120
121                    if( ($(this).outerHeight() + $(this)[0].scrollTop) > $(this)[0].scrollHeight ) {
122                        e.preventDefault();
123                        this.scrollTop += ( delta < 0 ? 1 : -1 );
124                    }
125                }
126            });
127
128            // hide options menus if click happens off of the list placeholder button
129            $(document).off('click.ms-hideopts').on('click.ms-hideopts', function( event ){
130                if( !$(event.target).closest('.ms-options-wrap').length ) {
131                    $('.ms-options-wrap > .ms-options:visible').hide();
132                }
133            });
134
135            // disable button action
136            placeholder.bind('mousedown',function( event ){
137                // ignore if its not a left click
138                if( event.which != 1 ) {
139                    return true;
140                }
141
142                // hide other menus before showing this one
143                $('.ms-options-wrap > .ms-options:visible').each(function(){
144                    if( $(this).parent().prev()[0] != optionsWrap.parent().prev()[0] ) {
145                        $(this).hide();
146                    }
147                });
148
149                // show/hide options
150                optionsWrap.toggle();
151
152                // recalculate height
153                if( optionsWrap.is(':visible') ) {
154                    optionsWrap.css( 'maxHeight', '' );
155
156                    var maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
157                    if( instance.options.maxHeight ) {
158                        maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
159                        maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxheight;
160                    }
161                    maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxHeight;
162
163                    optionsWrap.css( 'maxHeight', maxHeight );
164                }
165            }).click(function( event ){ event.preventDefault(); });
166
167            // add placeholder copy
168            if( instance.options.placeholder ) {
169                placeholder.text( instance.options.placeholder );
170            }
171
172            // add search box
173            if( instance.options.search ) {
174                optionsList.before('<div class="ms-search"><input type="text" value="" placeholder="'+ instance.options.searchOptions['default'] +'" /></div>');
175
176                var search = optionsWrap.find('.ms-search input');
177                search.on('keyup', function(){
178                    // ignore keystrokes that don't make a difference
179                    if( $(this).data('lastsearch') == $(this).val() ) {
180                        return true;
181                    }
182
183                    $(this).data('lastsearch', $(this).val() );
184
185                    // USER CALLBACK
186                    if( typeof instance.options.searchOptions.onSearch == 'function' ) {
187                        instance.options.searchOptions.onSearch( instance.element );
188                    }
189
190                    // search non optgroup li's
191                    optionsList.find('li:not(.optgroup)').each(function(){
192                        var optText = $(this).text();
193
194                        // show option if string exists
195                        if( optText.toLowerCase().indexOf( search.val().toLowerCase() ) > -1 ) {
196                            $(this).show();
197                        }
198                        // don't hide selected items
199                        else if( !$(this).hasClass('selected') ) {
200                            $(this).hide();
201                        }
202
203                        // hide / show optgroups depending on if options within it are visible
204                        if( !instance.options.searchOptions.showOptGroups && $(this).closest('li.optgroup') ) {
205                            $(this).closest('li.optgroup').show();
206
207                            if( $(this).closest('li.optgroup').find('li:visible').length ) {
208                                $(this).closest('li.optgroup').show();
209                            }
210                            else {
211                                $(this).closest('li.optgroup').hide();
212                            }
213                        }
214                    });
215                });
216            }
217
218            // add global select all options
219            if( instance.options.selectAll ) {
220                 optionsList.before('<input type="checkbox" class="ms-selectall global" id="manu_selectall" title="Select All"><a href="#" class="ms-selectall global">Select all</a><button type="button" id="manu_search_button" style="width: 15%;float: right;"> <i class="fa fa-search"></i> </button>');
221            }
222
223            // handle select all option
224            optionsWrap.on('click', '.ms-selectall', function( event ){
225                event.preventDefault();
226
227                if( $(this).hasClass('global') ) {
228                    // check if any selected if so then select them
229                    if( optionsList.find('li:not(.optgroup)').filter(':not(.selected)').length ) {
230                        optionsList.find('li:not(.optgroup)').filter(':not(.selected)').find('input[type="checkbox"]').trigger('click');
231                        $("#manu_selectall").prop("checked", true);
232                    }
233                    // deselect everything
234                    else {
235                        optionsList.find('li:not(.optgroup).selected input[type="checkbox"]').trigger('click');
236                        $("#manu_selectall").prop("checked", false);
237                    }
238                }
239                else if( $(this).closest('li').hasClass('optgroup') ) {
240                    var optgroup = $(this).closest('li.optgroup');
241
242                    // check if any selected if so then select them
243                    if( optgroup.find('li:not(.selected)').length ) {
244                        optgroup.find('li:not(.selected) input[type="checkbox"]').trigger('click');
245                    }
246                    // deselect everything
247                    else {
248                        optgroup.find('li.selected input[type="checkbox"]').trigger('click');
249                    }
250                }
251            });
252
253            // add options to wrapper
254            var options = [];
255            $(instance.element).children().each(function(){
256                if( this.nodeName == 'OPTGROUP' ) {
257                    var groupOptions = [];
258
259                    $(this).children('option').each(function(){
260                        groupOptions[ $(this).val() ] = {
261                            name   : $(this).text(),
262                            value  : $(this).val(),
263                            checked: $(this).prop( 'selected' )
264                        };
265                    });
266
267                    options.push({
268                        label  : $(this).attr('label'),
269                        options: groupOptions
270                    });
271                }
272                else if( this.nodeName == 'OPTION' ) {
273                    options.push({
274                        name   : $(this).text(),
275                        value  : $(this).val(),
276                        checked: $(this).prop( 'selected' )
277                    });
278                }
279                else {
280                    // bad option
281                    return true;
282                }
283            });
284            instance.loadOptions( options );
285
286            // COLUMNIZE
287            if( hasOptGroup ) {
288                // float non grouped options
289                optionsList.find('> li:not(.optgroup)').css({
290                    float: 'left',
291                    width: (100 / instance.options.columns) +'%'
292                });
293
294                // add CSS3 column styles
295                optionsList.find('li.optgroup').css({
296                    clear: 'both'
297                }).find('> ul').css({
298                    'column-count'        : instance.options.columns,
299                    'column-gap'          : 0,
300                    '-webkit-column-count': instance.options.columns,
301                    '-webkit-column-gap'  : 0,
302                    '-moz-column-count'   : instance.options.columns,
303                    '-moz-column-gap'     : 0
304                });
305
306                // for crappy IE versions float grouped options
307                if( this._ieVersion() && (this._ieVersion() < 10) ) {
308                    optionsList.find('li.optgroup > ul > li').css({
309                        float: 'left',
310                        width: (100 / instance.options.columns) +'%'
311                    });
312                }
313            }
314            else {
315                // add CSS3 column styles
316                optionsList.css({
317                    'column-count'        : instance.options.columns,
318                    'column-gap'          : 0,
319                    '-webkit-column-count': instance.options.columns,
320                    '-webkit-column-gap'  : 0,
321                    '-moz-column-count'   : instance.options.columns,
322                    '-moz-column-gap'     : 0
323                });
324
325                // for crappy IE versions float grouped options
326                if( this._ieVersion() && (this._ieVersion() < 10) ) {
327                    optionsList.find('> li').css({
328                        float: 'left',
329                        width: (100 / instance.options.columns) +'%'
330                    });
331                }
332            }
333
334            // BIND SELECT ACTION
335            optionsWrap.on( 'click', 'input[type="checkbox"]', function(){
336                $(this).closest( 'li' ).toggleClass( 'selected' );
337
338                var select = optionsWrap.parent().prev();
339
340                // toggle clicked option
341                select.find('option[value="'+ $(this).val() +'"]').prop(
342                    'selected', $(this).is(':checked')
343                ).closest('select').trigger('change');
344
345                if( typeof instance.options.onOptionClick == 'function' ) {
346                    instance.options.onOptionClick();
347                }
348
349                instance._updatePlaceholderText();
350            });
351
352            // hide native select list
353            if( typeof instance.options.onLoad === 'function' ) {
354                instance.options.onLoad( instance.element );
355            }
356            else {
357                $(instance.element).hide();
358            }
359        },
360
361        /* LOAD SELECT OPTIONS */
362        loadOptions: function( options, overwrite ) {
363            overwrite = (typeof overwrite == 'boolean') ? overwrite : true;
364
365            var instance    = this;
366            var optionsList = $(instance.element).next('.ms-options-wrap').find('> .ms-options > ul');
367
368            if( overwrite ) {
369                optionsList.find('> li').remove();
370            }
371
372            for( var key in options ) {
373                var thisOption = options[ key ];
374                var container  = $('<li></li>');
375
376                // optgroup
377                if( thisOption.hasOwnProperty('options') ) {
378                    container.addClass('optgroup');
379                    container.append('<span class="label">'+ thisOption.label +'</span>');
380                    container.find('> .label').css({
381                        clear: 'both'
382                    });
383
384                    if( instance.options.selectGroup ) {
385                        container.append('<a href="#" class="ms-selectall">Select all</a>')
386                    }
387                    
388                    container.append('<ul></ul>');
389
390                    for( var gKey in thisOption.options ) {
391                        var thisGOption = thisOption.options[ gKey ];
392                        var gContainer  = $('<li></li>').addClass('ms-reflow');
393
394                        instance._addOption( gContainer, thisGOption );
395
396                        container.find('> ul').append( gContainer );
397                    }
398                }
399                // option
400                else if( thisOption.hasOwnProperty('value') ) {
401                    container.addClass('ms-reflow')
402
403                    instance._addOption( container, thisOption );
404                }
405
406                optionsList.append( container );
407            }
408
409            optionsList.find('.ms-reflow input[type="checkbox"]').each(function( idx ){
410                if( $(this).css('display').match(/block$/) ) {
411                    var checkboxWidth = $(this).outerWidth();
412                        checkboxWidth = checkboxWidth ? checkboxWidth : 15;
413
414                    $(this).closest('label').css(
415                        'padding-left',
416                        (parseInt( $(this).closest('label').css('padding-left') ) * 2) + checkboxWidth
417                    );
418
419                    $(this).closest('.ms-reflow').removeClass('ms-reflow');
420                }
421            });
422
423            instance._updatePlaceholderText();
424        },
425
426        /* RESET THE DOM */
427        unload: function() {
428            $(this.element).next('.ms-options-wrap').remove();
429            $(this.element).show(function(){
430                $(this).css('display','').removeClass('jqmsLoaded');
431            });
432        },
433
434        /* RELOAD JQ MULTISELECT LIST */
435        reload: function() {
436            // remove existing options
437            $(this.element).next('.ms-options-wrap').remove();
438            $(this.element).removeClass('jqmsLoaded');
439
440            // load element
441            this.load();
442        },
443
444        /** PRIVATE FUNCTIONS **/
445        // update selected placeholder text
446        _updatePlaceholderText: function(){
447        	return false;
448            var instance    = this;
449            var placeholder = $(instance.element).next('.ms-options-wrap').find('> button:first-child');
450            var optionsWrap = $(instance.element).next('.ms-options-wrap').find('> .ms-options');
451            var select      = optionsWrap.parent().prev();
452
453            // get selected options
454            var selOpts = [];
455            select.find('option:selected').each(function(){
456                selOpts.push( $(this).text() );
457            });
458
459            // UPDATE PLACEHOLDER TEXT WITH OPTIONS SELECTED
460            placeholder.text( selOpts.join( ', ' ) );
461            var copy = placeholder.clone().css({
462                display   : 'inline',
463                width     : 'auto',
464                visibility: 'hidden'
465            }).appendTo( optionsWrap.parent() );
466
467            // if the jquery.actual plugin is loaded use it to get the widths
468            var copyWidth  = (typeof $.fn.actual !== 'undefined') ? copy.actual( 'width', instance.options.jqActualOpts ) : copy.width();
469            var placeWidth = (typeof $.fn.actual !== 'undefined') ? placeholder.actual( 'width', instance.options.jqActualOpts ) : placeholder.width();
470
471            // if copy is larger than button width use "# selected"
472            if( copyWidth > placeWidth ) {
473                placeholder.text( selOpts.length +' selected' );
474            }
475            // if options selected then use those
476            else if( selOpts.length ) {
477                placeholder.text( selOpts.join( ', ' ) );
478            }
479            // replace placeholder text
480            else {
481                placeholder.text( instance.options.placeholder );
482            }
483
484            // remove dummy element
485            copy.remove();
486        },
487
488        // Add option to the custom dom list
489        _addOption: function( container, option ) {
490            container.text( option.name );
491            container.prepend(
492                $('<input type="checkbox" value="" title="" />')
493                    .val( option.value )
494                    .attr( 'title', option.name )
495                    .attr( 'id', 'ms-opt-'+ msCounter )
496            );
497
498            if( option.checked ) {
499                container.addClass('default');
500                container.addClass('selected');
501                container.find( 'input[type="checkbox"]' ).prop( 'checked', true );
502            }
503
504            var label = $('<label></label>').attr( 'for', 'ms-opt-'+ msCounter );
505            container.wrapInner( label );
506
507
508            if( !this.options.showCheckbox ) {
509                container.find('input[id="ms-opt-'+ msCounter +'"]').hide();
510            }
511
512            msCounter = msCounter + 1;
513        },
514
515        // check ie version
516        _ieVersion: function() {
517            var myNav = navigator.userAgent.toLowerCase();
518            return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
519        }
520    };
521
522    // ENABLE JQUERY PLUGIN FUNCTION
523    $.fn.multiselect = function( options ){
524        var args = arguments;
525        var ret;
526
527        // menuize each list
528        if( (options === undefined) || (typeof options === 'object') ) {
529            return this.each(function(){
530                if( !$.data( this, 'plugin_multiselect' ) ) {
531                    $.data( this, 'plugin_multiselect', new MultiSelect( this, options ) );
532                }
533            });
534        } else if( (typeof options === 'string') && (options[0] !== '_') && (options !== 'init') ) {
535            this.each(function(){
536                var instance = $.data( this, 'plugin_multiselect' );
537
538                if( instance instanceof MultiSelect && typeof instance[ options ] === 'function' ) {
539                    ret = instance[ options ].apply( instance, Array.prototype.slice.call( args, 1 ) );
540                }
541
542                // special destruct handler
543                if( options === 'unload' ) {
544                    $.data( this, 'plugin_multiselect', null );
545                }
546            });
547
548            return ret;
549        }
550    };
551}(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.