vendor: 41,656 bytes, lines 1-965
1/** 2 * Display a nice easy to use multiselect list 3 * @Version: 2.4.26 4 * @Author: Patrick Springstubbe 5 * @Contact: @JediNobleclem 6 * @Website: springstubbe.us 7 * @Source: https://github.com/nobleclem/jQuery-MultiSelect 8 * 9 * Usage: 10 * $('select[multiple]').multiselect(); 11 * $('select[multiple]').multiselect({ texts: { placeholder: 'Select options' } }); 12 * $('select[multiple]').multiselect('reload'); 13 * $('select[multiple]').multiselect( 'loadOptions', [{ 14 * name : 'Option Name 1', 15 * value : 'option-value-1', 16 * checked: false, 17 * attributes : { 18 * custom1: 'value1', 19 * custom2: 'value2' 20 * } 21 * },{ 22 * name : 'Option Name 2', 23 * value : 'option-value-2', 24 * checked: false, 25 * attributes : { 26 * custom1: 'value1', 27 * custom2: 'value2' 28 * } 29 * }]); 30 * 31 **/ 32(function($){ 33 var defaults = { 34 columns: 1, // how many columns should be use to show options 35 search : false, // include option search box 36 37 // search filter options 38 searchOptions : { 39 delay : 250, // time (in ms) between keystrokes until search happens 40 showOptGroups: false, // show option group titles if no options remaining 41 searchText : true, // search within the text 42 searchValue : false, // search within the value 43 onSearch : function( element ){} // fires on keyup before search on options happens 44 }, 45 46 // plugin texts 47 texts: { 48 placeholder : 'Select options', // text to use in dummy input 49 search : 'Search', // search input placeholder text 50 searchNoResult : 'No results', // search results not found text 51 selectedOptions: ' selected', // selected suffix text 52 selectAll : 'Select all', // select all text 53 unselectAll : 'Unselect all', // unselect all text 54 noneSelected : 'None Selected' // None selected text 55 }, 56 57 // general options 58 selectAll : false, // add select all option 59 selectGroup : false, // select entire optgroup 60 minHeight : 200, // minimum height of option overlay 61 maxHeight : null, // maximum height of option overlay 62 maxWidth : null, // maximum width of option overlay (or selector) 63 maxPlaceholderWidth : null, // maximum width of placeholder button 64 maxPlaceholderOpts : 10, // maximum number of placeholder options to show until "# selected" shown instead 65 showAllPlaceholderOpts : false, // always show all the selected options 66 showCheckbox : true, // display the checkbox to the user 67 checkboxAutoFit : false, // auto calc checkbox padding 68 optionAttributes : [], // attributes to copy to the checkbox from the option element 69 replacePlaceholderText : true, // replace text of placeholder if button is too small 70 71 // Callbacks 72 onLoad : function( element ){}, // fires at end of list initialization 73 onOptionClick : function( element, option ){}, // fires when an option is clicked 74 onControlOpen : function( element ){}, // fires when the options list is opened 75 onControlClose: function( element ){}, // fires when the options list is closed 76 onSelectAll : function( element, selected ){}, // fires when (un)select all is clicked 77 onPlaceholder : function( element, placeholder, selectedOpts ){}, // fires when the placeholder txt is updated 78 }; 79 80 var msCounter = 1; // counter for each select list 81 var msOptCounter = 1; // counter for each option on page 82 83 // FOR LEGACY BROWSERS (talking to you IE8) 84 if( typeof Array.prototype.map !== 'function' ) { 85 Array.prototype.map = function( callback, thisArg ) { 86 if( typeof thisArg === 'undefined' ) { 87 thisArg = this; 88 } 89 90 return $.isArray( thisArg ) ? $.map( thisArg, callback ) : []; 91 }; 92 } 93 if( typeof String.prototype.trim !== 'function' ) { 94 String.prototype.trim = function() { 95 return this.replace(/^\s+|\s+$/g, ''); 96 }; 97 } 98 99 function MultiSelect( element, options ) 100 { 101 this.element = element; 102 this.options = $.extend( true, {}, defaults, options ); 103 this.updateSelectAll = true; 104 this.updatePlaceholder = true; 105 this.listNumber = msCounter; 106 107 msCounter = msCounter + 1; // increment counter 108 109 /* Make sure its a multiselect list */ 110 if( !$(this.element).prop('multiple') ) { 111 throw new Error( '[jQuery-MultiSelect] Select list must be a multiselect list in order to use this plugin' ); 112 } 113 114 /* Options validation checks */ 115 if( this.options.search ){ 116 if( !this.options.searchOptions.searchText && !this.options.searchOptions.searchValue ){ 117 throw new Error( '[jQuery-MultiSelect] Either searchText or searchValue should be true.' ); 118 } 119 } 120 121 /** BACKWARDS COMPATIBILITY **/ 122 if( 'placeholder' in this.options ) { 123 this.options.texts.placeholder = this.options.placeholder; 124 delete this.options.placeholder; 125 } 126 if( 'default' in this.options.searchOptions ) { 127 this.options.texts.search = this.options.searchOptions['default']; 128 delete this.options.searchOptions['default']; 129 } 130 /** END BACKWARDS COMPATIBILITY **/ 131 132 // load this instance 133 this.load(); 134 } 135 136 MultiSelect.prototype = { 137 /* LOAD CUSTOM MULTISELECT DOM/ACTIONS */ 138 load: function() { 139 var instance = this; 140 141 // make sure this is a select list and not loaded 142 if( (instance.element.nodeName.toUpperCase() != 'SELECT') || $(instance.element).hasClass('jqmsLoaded') ) { 143 return true; 144 } 145 146 // sanity check so we don't double load on a select element 147 $(instance.element).addClass('jqmsLoaded ms-list-'+ instance.listNumber ).data( 'plugin_multiselect-instance', instance ); 148 149 // add option container 150 $(instance.element).after('<div id="ms-list-'+ instance.listNumber +'" class="ms-options-wrap"><button type="button"><span>None Selected</span></button><div class="ms-options"><ul></ul></div></div>'); 151 152 var placeholder = $(instance.element).siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> button:first-child'); 153 var optionsWrap = $(instance.element).siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options'); 154 var optionsList = optionsWrap.find('> ul'); 155 156 // don't show checkbox (add class for css to hide checkboxes) 157 if( !instance.options.showCheckbox ) { 158 optionsWrap.addClass('hide-checkbox'); 159 } 160 else if( instance.options.checkboxAutoFit ) { 161 optionsWrap.addClass('checkbox-autofit'); 162 } 163 164 // check if list is disabled 165 if( $(instance.element).prop( 'disabled' ) ) { 166 placeholder.prop( 'disabled', true ); 167 } 168 169 // set placeholder maxWidth 170 if( instance.options.maxPlaceholderWidth ) { 171 placeholder.css( 'maxWidth', instance.options.maxPlaceholderWidth ); 172 } 173 174 // override with user defined maxHeight 175 if( instance.options.maxHeight ) { 176 var maxHeight = instance.options.maxHeight; 177 } 178 else { 179 // cacl default maxHeight 180 var maxHeight = ($(window).height() - optionsWrap.offset().top + $(window).scrollTop() - 20); 181 } 182 183 // maxHeight cannot be less than options.minHeight 184 maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxHeight; 185 186 optionsWrap.css({ 187 maxWidth : instance.options.maxWidth, 188 minHeight: instance.options.minHeight, 189 maxHeight: maxHeight, 190 }); 191 192 // isolate options scroll 193 // @source: https://github.com/nobleclem/jQuery-IsolatedScroll 194 optionsWrap.on( 'touchmove mousewheel DOMMouseScroll', function ( e ) { 195 if( ($(this).outerHeight() < $(this)[0].scrollHeight) ) { 196 var e0 = e.originalEvent, 197 delta = e0.wheelDelta || -e0.detail; 198 199 if( ($(this).outerHeight() + $(this)[0].scrollTop) > $(this)[0].scrollHeight ) { 200 e.preventDefault(); 201 this.scrollTop += ( delta < 0 ? 1 : -1 ); 202 } 203 } 204 }); 205 206 // hide options menus if click happens off of the list placeholder button 207 $(document).off('click.ms-hideopts').on('click.ms-hideopts', function( event ){ 208 if( !$(event.target).closest('.ms-options-wrap').length ) { 209 $('.ms-options-wrap.ms-active > .ms-options').each(function(){ 210 $(this).closest('.ms-options-wrap').removeClass('ms-active'); 211 212 var listID = $(this).closest('.ms-options-wrap').attr('id'); 213 214 var thisInst = $(this).parent().siblings('.'+ listID +'.jqmsLoaded').data('plugin_multiselect-instance'); 215 216 // USER CALLBACK 217 if( typeof thisInst.options.onControlClose == 'function' ) { 218 thisInst.options.onControlClose( thisInst.element ); 219 } 220 }); 221 } 222 // hide open option lists if escape key pressed 223 }).on('keydown', function( event ){ 224 if( (event.keyCode || event.which) == 27 ) { // esc key 225 $(this).trigger('click.ms-hideopts'); 226 } 227 }); 228 229 // handle pressing enter|space while tabbing through 230 placeholder.on('keydown', function( event ){ 231 var code = (event.keyCode || event.which); 232 if( (code == 13) || (code == 32) ) { // enter OR space 233 placeholder.trigger( 'mousedown' ); 234 } 235 }); 236 237 // disable button action 238 placeholder.on( 'mousedown', function( event ){ 239 // ignore if its not a left click 240 if( event.which && (event.which != 1) ) { 241 return true; 242 } 243 244 // hide other menus before showing this one 245 $('.ms-options-wrap.ms-active').each(function(){ 246 if( $(this).siblings( '.'+ $(this).attr('id') +'.jqmsLoaded')[0] != optionsWrap.parent().siblings('.ms-list-'+ instance.listNumber +'.jqmsLoaded')[0] ) { 247 $(this).removeClass('ms-active'); 248 249 var thisInst = $(this).siblings( '.'+ $(this).attr('id') +'.jqmsLoaded').data('plugin_multiselect-instance'); 250 251 // USER CALLBACK 252 if( typeof thisInst.options.onControlClose == 'function' ) { 253 thisInst.options.onControlClose( thisInst.element ); 254 } 255 } 256 }); 257 258 // show/hide options 259 optionsWrap.closest('.ms-options-wrap').toggleClass('ms-active'); 260 261 // recalculate height 262 if( optionsWrap.closest('.ms-options-wrap').hasClass('ms-active') ) { 263 // USER CALLBACK 264 if( typeof instance.options.onControlOpen == 'function' ) { 265 instance.options.onControlOpen( instance.element ); 266 } 267 268 optionsWrap.css( 'maxHeight', '' ); 269 270 // override with user defined maxHeight 271 if( instance.options.maxHeight ) { 272 var maxHeight = instance.options.maxHeight; 273 } 274 else { 275 // cacl default maxHeight 276 var maxHeight = ($(window).height() - optionsWrap.offset().top + $(window).scrollTop() - 20); 277 } 278 279 if( maxHeight ) { 280 // maxHeight cannot be less than options.minHeight 281 maxHeight = maxHeight < instance.options.minHeight ? instance.options.minHeight : maxHeight; 282 283 optionsWrap.css( 'maxHeight', maxHeight ); 284 } 285 } 286 else if( typeof instance.options.onControlClose == 'function' ) { 287 instance.options.onControlClose( instance.element ); 288 } 289 }).click(function( event ){ event.preventDefault(); }); 290 291 // add placeholder copy 292 if( instance.options.texts.placeholder ) { 293 placeholder.find('span').text( instance.options.texts.placeholder ); 294 } 295 296 // add search box 297 if( instance.options.search ) { 298 optionsList.before('<div class="ms-search"><input type="text" value="" placeholder="'+ instance.options.texts.search +'" /></div>'); 299 optionsList.after('<div class="no-result-message">' + instance.options.texts.searchNoResult + '</div>'); 300 301 var search = optionsWrap.find('.ms-search input'); 302 303 search.on('keyup', function(){ 304 // ignore keystrokes that don't make a difference 305 if( $(this).data('lastsearch') == $(this).val() ) { 306 return true; 307 } 308 309 // pause timeout 310 if( $(this).data('searchTimeout') ) { 311 clearTimeout( $(this).data('searchTimeout') ); 312 } 313 314 var thisSearchElem = $(this); 315 316 $(this).data('searchTimeout', setTimeout(function(){ 317 thisSearchElem.data('lastsearch', thisSearchElem.val() ); 318 319 // USER CALLBACK 320 if( typeof instance.options.searchOptions.onSearch == 'function' ) { 321 instance.options.searchOptions.onSearch( instance.element ); 322 } 323 324 // search non optgroup li's 325 var searchString = search.val().toLowerCase().trim(); 326 if( searchString ) { 327 optionsList.find('li[data-search-term*="'+ searchString +'"]:not(.optgroup)').removeClass('ms-hidden'); 328 optionsList.find('li:not([data-search-term*="'+ searchString +'"], .optgroup)').addClass('ms-hidden'); 329 } 330 else { 331 optionsList.find('.ms-hidden').removeClass('ms-hidden'); 332 } 333 334 // show/hide optgroups based on if there are items visible within 335 if( !instance.options.searchOptions.showOptGroups ) { 336 optionsList.find('.optgroup').each(function(){ 337 if( $(this).find('li:not(.ms-hidden)').length ) { 338 $(this).show(); 339 } 340 else { 341 $(this).hide(); 342 } 343 }); 344 } 345 346 instance._updateSelectAllText(); 347 }, instance.options.searchOptions.delay )); 348 }); 349 } 350 351 // add global select all options 352 if( instance.options.selectAll ) { 353 optionsList.before('<a href="#" class="ms-selectall global">' + instance.options.texts.selectAll + '</a>'); 354 } 355 356 // handle select all option 357 optionsWrap.on('click', '.ms-selectall', function( event ){ 358 event.preventDefault(); 359 360 instance.updateSelectAll = false; 361 instance.updatePlaceholder = false; 362 363 var select = optionsWrap.parent().siblings('.ms-list-'+ instance.listNumber +'.jqmsLoaded'); 364 365 if( $(this).hasClass('global') ) { 366 // check if any options are not selected if so then select them 367 if( optionsList.find('li:not(.optgroup, .selected, .ms-hidden) input[type="checkbox"]:not(:disabled)').length ) { 368 // get unselected vals, mark as selected, return val list 369 optionsList.find('li:not(.optgroup, .selected, .ms-hidden) input[type="checkbox"]:not(:disabled)').closest('li').addClass('selected'); 370 optionsList.find('li.selected input[type="checkbox"]:not(:disabled)').prop( 'checked', true ); 371 } 372 // deselect everything 373 else { 374 optionsList.find('li:not(.optgroup, .ms-hidden).selected input[type="checkbox"]:not(:disabled)').closest('li').removeClass('selected'); 375 optionsList.find('li:not(.optgroup, .ms-hidden, .selected) input[type="checkbox"]:not(:disabled)').prop( 'checked', false ); 376 } 377 } 378 else if( $(this).closest('li').hasClass('optgroup') ) { 379 var optgroup = $(this).closest('li.optgroup'); 380 381 // check if any selected if so then select them 382 if( optgroup.find('li:not(.selected, .ms-hidden) input[type="checkbox"]:not(:disabled)').length ) { 383 optgroup.find('li:not(.selected, .ms-hidden) input[type="checkbox"]:not(:disabled)').closest('li').addClass('selected'); 384 optgroup.find('li.selected input[type="checkbox"]:not(:disabled)').prop( 'checked', true ); 385 } 386 // deselect everything 387 else { 388 optgroup.find('li:not(.ms-hidden).selected input[type="checkbox"]:not(:disabled)').closest('li').removeClass('selected'); 389 optgroup.find('li:not(.ms-hidden, .selected) input[type="checkbox"]:not(:disabled)').prop( 'checked', false ); 390 } 391 } 392 393 var vals = []; 394 optionsList.find('li.selected input[type="checkbox"]').each(function(){ 395 vals.push( $(this).val() ); 396 }); 397 select.val( vals ).trigger('change'); 398 399 instance.updateSelectAll = true; 400 instance.updatePlaceholder = true; 401 402 // USER CALLBACK 403 if( typeof instance.options.onSelectAll == 'function' ) { 404 instance.options.onSelectAll( instance.element, vals.length ); 405 } 406 407 instance._updateSelectAllText(); 408 instance._updatePlaceholderText(); 409 }); 410 411 // add options to wrapper 412 var options = []; 413 $(instance.element).children().each(function(){ 414 if( this.nodeName.toUpperCase() == 'OPTGROUP' ) { 415 var groupOptions = []; 416 417 $(this).children('option').each(function(){ 418 var thisOptionAtts = {}; 419 for( var i = 0; i < instance.options.optionAttributes.length; i++ ) { 420 var thisOptAttr = instance.options.optionAttributes[ i ]; 421 422 if( $(this).attr( thisOptAttr ) !== undefined ) { 423 thisOptionAtts[ thisOptAttr ] = $(this).attr( thisOptAttr ); 424 } 425 } 426 427 groupOptions.push({ 428 name : $(this).text(), 429 value : $(this).val(), 430 checked: $(this).prop( 'selected' ), 431 attributes: thisOptionAtts 432 }); 433 }); 434 435 options.push({ 436 label : $(this).attr('label'), 437 options: groupOptions 438 }); 439 } 440 else if( this.nodeName.toUpperCase() == 'OPTION' ) { 441 var thisOptionAtts = {}; 442 for( var i = 0; i < instance.options.optionAttributes.length; i++ ) { 443 var thisOptAttr = instance.options.optionAttributes[ i ]; 444 445 if( $(this).attr( thisOptAttr ) !== undefined ) { 446 thisOptionAtts[ thisOptAttr ] = $(this).attr( thisOptAttr ); 447 } 448 } 449 450 options.push({ 451 name : $(this).text(), 452 value : $(this).val(), 453 checked : $(this).prop( 'selected' ), 454 attributes: thisOptionAtts 455 }); 456 } 457 else { 458 // bad option 459 return true; 460 } 461 }); 462 instance.loadOptions( options, true, false ); 463 464 // BIND SELECT ACTION 465 optionsWrap.on( 'click', 'input[type="checkbox"]', function(){ 466 $(this).closest( 'li' ).toggleClass( 'selected' ); 467 468 var select = optionsWrap.parent().siblings('.ms-list-'+ instance.listNumber +'.jqmsLoaded'); 469 470 // toggle clicked option 471 select.find('option[value="'+ instance._escapeSelector( $(this).val() ) +'"]').prop( 472 'selected', $(this).is(':checked') 473 ).closest('select').trigger('change'); 474 475 // USER CALLBACK 476 if( typeof instance.options.onOptionClick == 'function' ) { 477 instance.options.onOptionClick(instance.element, this); 478 } 479 480 instance._updateSelectAllText(); 481 instance._updatePlaceholderText(); 482 }); 483 484 // BIND FOCUS EVENT 485 optionsWrap.on('focusin', 'input[type="checkbox"]', function(){ 486 $(this).closest('label').addClass('focused'); 487 }).on('focusout', 'input[type="checkbox"]', function(){ 488 $(this).closest('label').removeClass('focused'); 489 }); 490 491 // USER CALLBACK 492 if( typeof instance.options.onLoad === 'function' ) { 493 instance.options.onLoad( instance.element ); 494 } 495 496 // hide native select list 497 $(instance.element).hide(); 498 }, 499 500 /* LOAD SELECT OPTIONS */ 501 loadOptions: function( options, overwrite, updateSelect ) { 502 // console.log( options ); 503 overwrite = (typeof overwrite == 'boolean') ? overwrite : true; 504 updateSelect = (typeof updateSelect == 'boolean') ? updateSelect : true; 505 506 var instance = this; 507 var select = $(instance.element); 508 var optionsList = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options > ul'); 509 var optionsWrap = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options'); 510 511 if( overwrite ) { 512 optionsList.find('> li').remove(); 513 514 if( updateSelect ) { 515 select.find('> *').remove(); 516 } 517 } 518 519 var containers = []; 520 for( var key in options ) { 521 // Prevent prototype methods injected into options from being iterated over. 522 if( !options.hasOwnProperty( key ) ) { 523 continue; 524 } 525 526 var thisOption = options[ key ]; 527 var container = $('<li/>'); 528 var appendContainer = true; 529 530 // OPTION 531 if( thisOption.hasOwnProperty('value') ) { 532 if( instance.options.showCheckbox && instance.options.checkboxAutoFit ) { 533 container.addClass('ms-reflow'); 534 } 535 536 // add option to ms dropdown 537 instance._addOption( container, thisOption ); 538 539 if( updateSelect ) { 540 var selOption = $('<option/>', { 541 value: thisOption.value, 542 text : thisOption.name 543 }); 544 545 // add custom user attributes 546 if( thisOption.hasOwnProperty('attributes') && Object.keys( thisOption.attributes ).length ) { 547 selOption.attr( thisOption.attributes ); 548 } 549 550 // mark option as selected 551 if( thisOption.checked ) { 552 selOption.prop( 'selected', true ); 553 } 554 555 select.append( selOption ); 556 } 557 } 558 // OPTGROUP 559 else if( thisOption.hasOwnProperty('options') ) { 560 var optGroup = $('<optgroup/>', { 561 label: thisOption.label 562 }); 563 564 optionsList.find('> li.optgroup > span.label').each(function(){ 565 if( $(this).text() == thisOption.label ) { 566 container = $(this).closest('.optgroup'); 567 appendContainer = false; 568 } 569 }); 570 571 // prepare to append optgroup to select element 572 if( updateSelect ) { 573 if( select.find('optgroup[label="'+ thisOption.label +'"]').length ) { 574 optGroup = select.find('optgroup[label="'+ thisOption.label +'"]'); 575 } 576 else { 577 select.append( optGroup ); 578 } 579 } 580 581 // setup container 582 if( appendContainer ) { 583 container.addClass('optgroup'); 584 container.append('<span class="label">'+ thisOption.label +'</span>'); 585 container.find('> .label').css({ 586 clear: 'both' 587 }); 588 589 // add select all link 590 if( instance.options.selectGroup ) { 591 container.append('<a href="#" class="ms-selectall">' + instance.options.texts.selectAll + '</a>'); 592 } 593 594 container.append('<ul/>'); 595 } 596 597 for( var gKey in thisOption.options ) { 598 // Prevent prototype methods injected into options from 599 // being iterated over. 600 if( !thisOption.options.hasOwnProperty( gKey ) ) { 601 continue; 602 } 603 604 var thisGOption = thisOption.options[ gKey ]; 605 var gContainer = $('<li/>'); 606 if( instance.options.showCheckbox && instance.options.checkboxAutoFit ) { 607 gContainer.addClass('ms-reflow'); 608 } 609 610 // no clue what this is we hit (skip) 611 if( !thisGOption.hasOwnProperty('value') ) { 612 continue; 613 } 614 615 instance._addOption( gContainer, thisGOption ); 616 617 container.find('> ul').append( gContainer ); 618 619 // add option to optgroup in select element 620 if( updateSelect ) { 621 var selOption = $('<option/>', { 622 value: thisGOption.value, 623 text : thisGOption.name 624 }); 625 626 // add custom user attributes 627 if( thisGOption.hasOwnProperty('attributes') && Object.keys( thisGOption.attributes ).length ) { 628 selOption.attr( thisGOption.attributes ); 629 } 630 631 // mark option as selected 632 if( thisGOption.checked ) { 633 selOption.prop( 'selected', true ); 634 } 635 636 optGroup.append( selOption ); 637 } 638 } 639 } 640 else { 641 // no clue what this is we hit (skip) 642 continue; 643 } 644 645 if( appendContainer ) { 646 containers.push( container ); 647 } 648 } 649 optionsList.append( containers ); 650 651 // pad out label for room for the checkbox 652 if( instance.options.checkboxAutoFit && instance.options.showCheckbox && !optionsWrap.hasClass('hide-checkbox') ) { 653 var chkbx = optionsList.find('.ms-reflow:eq(0) input[type="checkbox"]'); 654 if( chkbx.length ) { 655 var checkboxWidth = chkbx.outerWidth(); 656 checkboxWidth = checkboxWidth ? checkboxWidth : 15; 657 658 optionsList.find('.ms-reflow label').css( 659 'padding-left', 660 (parseInt( chkbx.closest('label').css('padding-left') ) * 2) + checkboxWidth 661 ); 662 663 optionsList.find('.ms-reflow').removeClass('ms-reflow'); 664 } 665 } 666 667 // update placeholder text 668 instance._updatePlaceholderText(); 669 670 // RESET COLUMN STYLES 671 optionsWrap.find('ul').css({ 672 'column-count' : '', 673 'column-gap' : '', 674 '-webkit-column-count': '', 675 '-webkit-column-gap' : '', 676 '-moz-column-count' : '', 677 '-moz-column-gap' : '' 678 }); 679 680 // COLUMNIZE 681 if( select.find('optgroup').length ) { 682 // float non grouped options 683 optionsList.find('> li:not(.optgroup)').css({ 684 'float': 'left', 685 width: (100 / instance.options.columns) +'%' 686 }); 687 688 // add CSS3 column styles 689 optionsList.find('li.optgroup').css({ 690 clear: 'both' 691 }).find('> ul').css({ 692 'column-count' : instance.options.columns, 693 'column-gap' : 0, 694 '-webkit-column-count': instance.options.columns, 695 '-webkit-column-gap' : 0, 696 '-moz-column-count' : instance.options.columns, 697 '-moz-column-gap' : 0 698 }); 699 700 // for crappy IE versions float grouped options 701 if( this._ieVersion() && (this._ieVersion() < 10) ) { 702 optionsList.find('li.optgroup > ul > li').css({ 703 'float': 'left', 704 width: (100 / instance.options.columns) +'%' 705 }); 706 } 707 } 708 else { 709 // add CSS3 column styles 710 optionsList.css({ 711 'column-count' : instance.options.columns, 712 'column-gap' : 0, 713 '-webkit-column-count': instance.options.columns, 714 '-webkit-column-gap' : 0, 715 '-moz-column-count' : instance.options.columns, 716 '-moz-column-gap' : 0 717 }); 718 719 // for crappy IE versions float grouped options 720 if( this._ieVersion() && (this._ieVersion() < 10) ) { 721 optionsList.find('> li').css({ 722 'float': 'left', 723 width: (100 / instance.options.columns) +'%' 724 }); 725 } 726 } 727 728 // update un/select all logic 729 instance._updateSelectAllText(); 730 }, 731 732 /* UPDATE MULTISELECT CONFIG OPTIONS */ 733 settings: function( options ) { 734 this.options = $.extend( true, {}, this.options, options ); 735 this.reload(); 736 }, 737 738 /* RESET THE DOM */ 739 unload: function() { 740 $(this.element).siblings('#ms-list-'+ this.listNumber +'.ms-options-wrap').remove(); 741 $(this.element).show(function(){ 742 $(this).css('display','').removeClass('jqmsLoaded'); 743 }); 744 }, 745 746 /* RELOAD JQ MULTISELECT LIST */ 747 reload: function() { 748 // remove existing options 749 $(this.element).siblings('#ms-list-'+ this.listNumber +'.ms-options-wrap').remove(); 750 $(this.element).removeClass('jqmsLoaded'); 751 752 // load element 753 this.load(); 754 }, 755 756 // RESET BACK TO DEFAULT VALUES & RELOAD 757 reset: function() { 758 var defaultVals = []; 759 $(this.element).find('option').each(function(){ 760 if( $(this).prop('defaultSelected') ) { 761 defaultVals.push( $(this).val() ); 762 } 763 }); 764 765 $(this.element).val( defaultVals ); 766 767 this.reload(); 768 }, 769 770 disable: function( status ) { 771 status = (typeof status === 'boolean') ? status : true; 772 $(this.element).prop( 'disabled', status ); 773 $(this.element).siblings('#ms-list-'+ this.listNumber +'.ms-options-wrap').find('button:first-child') 774 .prop( 'disabled', status ); 775 }, 776 777 /** PRIVATE FUNCTIONS **/ 778 // update the un/select all texts based on selected options and visibility 779 _updateSelectAllText: function(){ 780 if( !this.updateSelectAll ) { 781 return; 782 } 783 784 var instance = this; 785 786 // select all not used at all so just do nothing 787 if( !instance.options.selectAll && !instance.options.selectGroup ) { 788 return; 789 } 790 791 var optionsWrap = $(instance.element).siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options'); 792 793 // update un/select all text 794 optionsWrap.find('.ms-selectall').each(function(){ 795 var unselected = $(this).parent().find('li:not(.optgroup,.selected,.ms-hidden)'); 796 797 $(this).text( 798 unselected.length ? instance.options.texts.selectAll : instance.options.texts.unselectAll 799 ); 800 }); 801 802 var shownOptionsCount = optionsWrap.find('> ul li:not(.optgroup,.ms-hidden)').length; 803 804 // show/hide no-results message 805 optionsWrap.find('.no-result-message').toggle( shownOptionsCount ? false : true ); 806 807 // show/hide (un)select all element as necessary 808 optionsWrap.find('.ms-selectall.global').toggle( shownOptionsCount ? true : false ); 809 }, 810 811 // update selected placeholder text 812 _updatePlaceholderText: function(){ 813 if( !this.updatePlaceholder ) { 814 return; 815 } 816 817 var instance = this; 818 var select = $(instance.element); 819 var selectVals = select.val() ? select.val() : []; 820 var placeholder = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> button:first-child'); 821 var placeholderTxt = placeholder.find('span'); 822 var optionsWrap = select.siblings('#ms-list-'+ instance.listNumber +'.ms-options-wrap').find('> .ms-options'); 823 824 // if there are disabled options get those values as well 825 if( select.find('option:selected:disabled').length ) { 826 selectVals = []; 827 select.find('option:selected').each(function(){ 828 selectVals.push( $(this).val() ); 829 }); 830 } 831 832 // get selected options 833 var selOpts = []; 834 for( var key in selectVals ) { 835 // Prevent prototype methods injected into options from being iterated over. 836 if( !selectVals.hasOwnProperty( key ) ) { 837 continue; 838 } 839 840 selOpts.push( 841 select.find('option[value="'+ instance._escapeSelector( selectVals[ key ] ) +'"]').text().trim() 842 ); 843 844 if( selOpts.length >= instance.options.maxPlaceholderOpts ) { 845 break; 846 } 847 } 848 849 // UPDATE PLACEHOLDER TEXT WITH OPTIONS SELECTED 850 placeholderTxt.text( selOpts.join( ', ' ) ); 851 852 if( selOpts.length ) { 853 optionsWrap.closest('.ms-options-wrap').addClass('ms-has-selections'); 854 855 // USER CALLBACK 856 if( typeof instance.options.onPlaceholder == 'function' ) { 857 instance.options.onPlaceholder( instance.element, placeholderTxt, selOpts ); 858 } 859 } 860 else { 861 optionsWrap.closest('.ms-options-wrap').removeClass('ms-has-selections'); 862 } 863 864 // replace placeholder text 865 if( !selOpts.length ) { 866 placeholderTxt.text( instance.options.texts.placeholder ); 867 } 868 // if copy is larger than button width use "# selected" 869 else if( !instance.options.showAllPlaceholderOpts && instance.options.replacePlaceholderText && ((placeholderTxt.width() > placeholder.width()) || (selOpts.length != selectVals.length)) ) { 870 placeholderTxt.text( selectVals.length + instance.options.texts.selectedOptions ); 871 } 872 }, 873 874 // Add option to the custom dom list 875 _addOption: function( container, option ) { 876 var instance = this; 877 var optionNameText = $('<div/>').html( option.name ).text(); 878 879 var thisOption = $('<label/>', { 880 for : 'ms-opt-'+ msOptCounter 881 }).text( optionNameText ); 882 883 var thisCheckbox = $('<input>', { 884 type : 'checkbox', 885 title: optionNameText, 886 id : 'ms-opt-'+ msOptCounter, 887 value: option.value 888 }); 889 890 // add user defined attributes 891 if( option.hasOwnProperty('attributes') && Object.keys( option.attributes ).length ) { 892 thisCheckbox.attr( option.attributes ); 893 } 894 895 if( option.checked ) { 896 container.addClass('default selected'); 897 thisCheckbox.prop( 'checked', true ); 898 } 899 900 thisOption.prepend( thisCheckbox ); 901 902 var searchTerm = ''; 903 if( instance.options.searchOptions.searchText ) { 904 searchTerm += ' ' + optionNameText.toLowerCase(); 905 } 906 if( instance.options.searchOptions.searchValue ) { 907 searchTerm += ' ' + option.value.toLowerCase(); 908 } 909 910 container.attr( 'data-search-term', searchTerm.trim() ).prepend( thisOption ); 911 912 msOptCounter = msOptCounter + 1; 913 }, 914 915 // check ie version 916 _ieVersion: function() { 917 var myNav = navigator.userAgent.toLowerCase(); 918 return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false; 919 }, 920 921 // escape selector 922 _escapeSelector: function( string ) { 923 if( typeof $.escapeSelector == 'function' ) { 924 return $.escapeSelector( string ); 925 } 926 else { 927 return string.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&"); 928 } 929 } 930 }; 931 932 // ENABLE JQUERY PLUGIN FUNCTION 933 $.fn.multiselect = function( options ){ 934 if( !this.length ) { 935 return; 936 } 937 938 var args = arguments; 939 var ret; 940 941 // menuize each list 942 if( (options === undefined) || (typeof options === 'object') ) { 943 return this.each(function(){ 944 if( !$.data( this, 'plugin_multiselect' ) ) { 945 $.data( this, 'plugin_multiselect', new MultiSelect( this, options ) ); 946 } 947 }); 948 } else if( (typeof options === 'string') && (options[0] !== '_') && (options !== 'init') ) { 949 this.each(function(){ 950 var instance = $.data( this, 'plugin_multiselect' ); 951 952 if( instance instanceof MultiSelect && typeof instance[ options ] === 'function' ) { 953 ret = instance[ options ].apply( instance, Array.prototype.slice.call( args, 1 ) ); 954 } 955 956 // special destruct handler 957 if( options === 'unload' ) { 958 $.data( this, 'plugin_multiselect', null ); 959 } 960 }); 961 962 return ret; 963 } 964 }; 965}(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.