vendor: 10,037 bytes, lines 1-230
1/** 2 * Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect) 3 * 4 * Apache License, Version 2.0: 5 * Copyright (c) 2012 - 2015 David Stutz 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 8 * use this file except in compliance with the License. You may obtain a 9 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 * License for the specific language governing permissions and limitations 15 * under the License. 16 * 17 * BSD 3-Clause License: 18 * Copyright (c) 2012 - 2015 David Stutz 19 * All rights reserved. 20 * 21 * Redistribution and use in source and binary forms, with or without 22 * modification, are permitted provided that the following conditions are met: 23 * - Redistributions of source code must retain the above copyright notice, 24 * this list of conditions and the following disclaimer. 25 * - Redistributions in binary form must reproduce the above copyright notice, 26 * this list of conditions and the following disclaimer in the documentation 27 * and/or other materials provided with the distribution. 28 * - Neither the name of David Stutz nor the names of its contributors may be 29 * used to endorse or promote products derived from this software without 30 * specific prior written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 33 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 34 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 35 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 36 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 37 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 38 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 39 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 40 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 41 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 43 */ 44!function ($) { 45 "use strict";// jshint ;_; 46 47 if (typeof ko !== 'undefined' && ko.bindingHandlers && !ko.bindingHandlers.multiselect) { 48 ko.bindingHandlers.multiselect = { 49 after: ['options', 'value', 'selectedOptions', 'enable', 'disable'], 50 51 init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 52 var $element = $(element); 53 var config = ko.toJS(valueAccessor()); 54 55 $element.multiselect(config); 56 57 if (allBindings.has('options')) { 58 var options = allBindings.get('options'); 59 if (ko.isObservable(options)) { 60 ko.computed({ 61 read: function() { 62 options(); 63 setTimeout(function() { 64 var ms = $element.data('multiselect'); 65 if (ms) 66 ms.updateOriginalOptions();//Not sure how beneficial this is. 67 $element.multiselect('rebuild'); 68 }, 1); 69 }, 70 disposeWhenNodeIsRemoved: element 71 }); 72 } 73 } 74 75 //value and selectedOptions are two-way, so these will be triggered even by our own actions. 76 //It needs some way to tell if they are triggered because of us or because of outside change. 77 //It doesn't loop but it's a waste of processing. 78 if (allBindings.has('value')) { 79 var value = allBindings.get('value'); 80 if (ko.isObservable(value)) { 81 ko.computed({ 82 read: function() { 83 value(); 84 setTimeout(function() { 85 $element.multiselect('refresh'); 86 }, 1); 87 }, 88 disposeWhenNodeIsRemoved: element 89 }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); 90 } 91 } 92 93 //Switched from arrayChange subscription to general subscription using 'refresh'. 94 //Not sure performance is any better using 'select' and 'deselect'. 95 if (allBindings.has('selectedOptions')) { 96 var selectedOptions = allBindings.get('selectedOptions'); 97 if (ko.isObservable(selectedOptions)) { 98 ko.computed({ 99 read: function() { 100 selectedOptions(); 101 setTimeout(function() { 102 $element.multiselect('refresh'); 103 }, 1); 104 }, 105 disposeWhenNodeIsRemoved: element 106 }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); 107 } 108 } 109 110 var setEnabled = function (enable) { 111 setTimeout(function () { 112 if (enable) 113 $element.multiselect('enable'); 114 else 115 $element.multiselect('disable'); 116 }); 117 }; 118 119 if (allBindings.has('enable')) { 120 var enable = allBindings.get('enable'); 121 if (ko.isObservable(enable)) { 122 ko.computed({ 123 read: function () { 124 setEnabled(enable()); 125 }, 126 disposeWhenNodeIsRemoved: element 127 }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); 128 } else { 129 setEnabled(enable); 130 } 131 } 132 133 if (allBindings.has('disable')) { 134 var disable = allBindings.get('disable'); 135 if (ko.isObservable(disable)) { 136 ko.computed({ 137 read: function () { 138 setEnabled(!disable()); 139 }, 140 disposeWhenNodeIsRemoved: element 141 }).extend({ rateLimit: 100, notifyWhenChangesStop: true }); 142 } else { 143 setEnabled(!disable); 144 } 145 } 146 147 ko.utils.domNodeDisposal.addDisposeCallback(element, function() { 148 $element.multiselect('destroy'); 149 }); 150 }, 151 152 update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 153 var $element = $(element); 154 var config = ko.toJS(valueAccessor()); 155 156 $element.multiselect('setOptions', config); 157 $element.multiselect('rebuild'); 158 } 159 }; 160 } 161 162 function forEach(array, callback) { 163 for (var index = 0; index < array.length; ++index) { 164 callback(array[index], index); 165 } 166 } 167 168 /** 169 * Constructor to create a new multiselect using the given select. 170 * 171 * @param {jQuery} select 172 * @param {Object} options 173 * @returns {Multiselect} 174 */ 175 function Multiselect(select, options) { 176 177 this.$select = $(select); 178 179 // Placeholder via data attributes 180 if (this.$select.attr("data-placeholder")) { 181 options.nonSelectedText = this.$select.data("placeholder"); 182 } 183 184 this.options = this.mergeOptions($.extend({}, options, this.$select.data())); 185 186 // Initialization. 187 // We have to clone to create a new reference. 188 this.originalOptions = this.$select.clone()[0].options; 189 this.query = ''; 190 this.searchTimeout = null; 191 this.lastToggledInput = null; 192 193 this.options.multiple = this.$select.attr('multiple') === "multiple"; 194 this.options.onChange = $.proxy(this.options.onChange, this); 195 this.options.onSelectAll = $.proxy(this.options.onSelectAll, this); 196 this.options.onDeselectAll = $.proxy(this.options.onDeselectAll, this); 197 this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this); 198 this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this); 199 this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this); 200 this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this); 201 this.options.onInitialized = $.proxy(this.options.onInitialized, this); 202 this.options.onFiltering = $.proxy(this.options.onFiltering, this); 203 204 // Build select all if enabled. 205 this.buildContainer(); 206 this.buildButton(); 207 this.buildDropdown(); 208 this.buildSelectAll(); 209 this.buildDropdownOptions(); 210 this.buildFilter(); 211 212 this.updateButtonText(); 213 this.updateSelectAll(true); 214 215 if (this.options.enableClickableOptGroups && this.options.multiple) { 216 this.updateOptGroups(); 217 } 218 219 if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { 220 this.disable(); 221 } 222 223 this.$select.wrap('<span class="hide-native-select">').after(this.$container); 224 this.options.onInitialized(this.$select, this.$container); 225 } 226 227 Multiselect.prototype = { 228 229 defaults: { 230 /**
231 * Default text function will either print 'None selected' in case no 232 * option is selected or a list of the selected options up to a length 233 * of 3 selected options. 234 * 235 * @param {jQuery} options 236 * @param {jQuery} select 237 * @returns {String} 238 */ 239 buttonText: function(options, select) { 240 if (this.disabledText.length > 0 241 && (select.prop('disabled') || (options.length == 0 && this.disableIfEmpty))) { 242 243 return this.disabledText; 244 } 245 else if (options.length === 0) { 246 return this.nonSelectedText; 247 } 248 else if (this.allSelectedText 249 && options.length === $('option', $(select)).length 250 && $('option', $(select)).length !== 1 251 && this.multiple) { 252 253 if (this.selectAllNumber) { 254 return this.allSelectedText + ' (' + options.length + ')'; 255 } 256 else { 257 return this.allSelectedText; 258 } 259 } 260 else if (options.length > this.numberDisplayed) { 261 return options.length + ' ' + this.nSelectedText; 262 } 263 else { 264 var selected = ''; 265 var delimiter = this.delimiterText; 266 267 options.each(function() { 268 var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); 269 selected += label + delimiter; 270 }); 271 272 return selected.substr(0, selected.length - this.delimiterText.length); 273 } 274 }, 275 /** 276 * Updates the title of the button similar to the buttonText function. 277 * 278 * @param {jQuery} options 279 * @param {jQuery} select 280 * @returns {@exp;selected@call;substr} 281 */ 282 buttonTitle: function(options, select) { 283 if (options.length === 0) { 284 return this.nonSelectedText; 285 } 286 else { 287 var selected = ''; 288 var delimiter = this.delimiterText; 289 290 options.each(function () { 291 var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); 292 selected += label + delimiter; 293 }); 294 return selected.substr(0, selected.length - this.delimiterText.length); 295 } 296 }, 297 checkboxName: function(option) { 298 return false; // no checkbox name 299 }, 300 /** 301 * Create a label. 302 * 303 * @param {jQuery} element 304 * @returns {String} 305 */ 306 optionLabel: function(element){ 307 return $(element).attr('label') || $(element).text(); 308 }, 309 /** 310 * Create a class. 311 * 312 * @param {jQuery} element 313 * @returns {String} 314 */ 315 optionClass: function(element) { 316 return $(element).attr('class') || ''; 317 }, 318 /** 319 * Triggered on change of the multiselect. 320 * 321 * Not triggered when selecting/deselecting options manually. 322 * 323 * @param {jQuery} option 324 * @param {Boolean} checked 325 */ 326 onChange : function(option, checked) { 327 328 }, 329 /** 330 * Triggered when the dropdown is shown. 331 * 332 * @param {jQuery} event 333 */ 334 onDropdownShow: function(event) { 335 336 }, 337 /** 338 * Triggered when the dropdown is hidden. 339 * 340 * @param {jQuery} event 341 */ 342 onDropdownHide: function(event) { 343 344 }, 345 /** 346 * Triggered after the dropdown is shown.
347 * 348 * @param {jQuery} event 349 */ 350 onDropdownShown: function(event) { 351 352 }, 353 /** 354 * Triggered after the dropdown is hidden. 355 * 356 * @param {jQuery} event 357 */ 358 onDropdownHidden: function(event) { 359 360 }, 361 /** 362 * Triggered on select all. 363 */ 364 onSelectAll: function() { 365 366 }, 367 /** 368 * Triggered on deselect all. 369 */ 370 onDeselectAll: function() { 371 372 }, 373 /** 374 * Triggered after initializing. 375 * 376 * @param {jQuery} $select 377 * @param {jQuery} $container 378 */ 379 onInitialized: function($select, $container) { 380 381 }, 382 /** 383 * Triggered on filtering. 384 * 385 * @param {jQuery} $filter 386 */ 387 onFiltering: function($filter) { 388 389 }, 390 enableHTML: false, 391 buttonClass: 'btn btn-default', 392 inheritClass: false, 393 buttonWidth: 'auto', 394 buttonContainer: '<div class="btn-group" />', 395 dropRight: false, 396 dropUp: false, 397 selectedClass: 'active', 398 // Maximum height of the dropdown menu. 399 // If maximum height is exceeded a scrollbar will be displayed. 400 maxHeight: false, 401 includeSelectAllOption: false, 402 includeSelectAllIfMoreThan: 0, 403 selectAllText: ' Select all', 404 selectAllValue: 'multiselect-all', 405 selectAllName: false, 406 selectAllNumber: true, 407 selectAllJustVisible: true, 408 enableFiltering: false, 409 enableCaseInsensitiveFiltering: false, 410 enableFullValueFiltering: false, 411 enableClickableOptGroups: false, 412 enableCollapsibleOptGroups: false, 413 filterPlaceholder: 'Search', 414 // possible options: 'text', 'value', 'both' 415 filterBehavior: 'text', 416 includeFilterClearBtn: true, 417 preventInputChangeEvent: false, 418 nonSelectedText: 'None selected', 419 nSelectedText: 'selected', 420 allSelectedText: 'All selected', 421 numberDisplayed: 3, 422 disableIfEmpty: false, 423 disabledText: '', 424 delimiterText: ', ', 425 templates: { 426 button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>', 427 ul: '<ul class="multiselect-container dropdown-menu"></ul>', 428 filter: '<li class="multiselect-item multiselect-filter"><div class="input-group"><span class="input-group-addon"><i class="glyphicon glyphicon-search"></i></span><input class="form-control multiselect-search" type="text"></div></li>', 429 filterClearBtn: '<span class="input-group-btn"><button class="btn btn-default multiselect-clear-filter" type="button"><i class="glyphicon glyphicon-remove-circle"></i></button></span>', 430 li: '<li><a tabindex="0"><label></label></a></li>', 431 divider: '<li class="multiselect-item divider"></li>', 432 liGroup: '<li class="multiselect-item multiselect-group"><label></label></li>' 433 } 434 }, 435 436 constructor: Multiselect, 437 438 /** 439 * Builds the container of the multiselect. 440 */ 441 buildContainer: function() { 442 this.$container = $(this.options.buttonContainer); 443 this.$container.on('show.bs.dropdown', this.options.onDropdownShow); 444 this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); 445 this.$container.on('shown.bs.dropdown', this.options.onDropdownShown); 446 this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden); 447 }, 448 449 /** 450 * Builds the button of the multiselect. 451 */ 452 buildButton: function() { 453 this.$button = $(this.options.templates.button).addClass(this.options.buttonClass); 454 if (this.$select.attr('class') && this.options.inheritClass) { 455 this.$button.addClass(this.$select.attr('class')); 456 } 457 // Adopt active state. 458 if (this.$select.prop('disabled')) { 459 this.disable(); 460 } 461 else { 462 this.enable(); 463 } 464 465 // Manually add button width if set. 466 if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') { 467 this.$button.css({ 468 'width' : '100%', //this.options.buttonWidth, 469 'overflow' : 'hidden', 470 'text-overflow' : 'ellipsis' 471 }); 472 this.$container.css({ 473 'width': this.options.buttonWidth 474 }); 475 } 476 477 // Keep the tab index from the select. 478 var tabindex = this.$select.attr('tabindex'); 479 if (tabindex) { 480 this.$button.attr('tabindex', tabindex); 481 } 482 483 this.$container.prepend(this.$button); 484 }, 485 486 /** 487 * Builds the ul representing the dropdown menu. 488 */ 489 buildDropdown: function() { 490 491 // Build ul. 492 this.$ul = $(this.options.templates.ul); 493 494 if (this.options.dropRight) { 495 this.$ul.addClass('pull-right'); 496 } 497 498 // Set max height of dropdown menu to activate auto scrollbar. 499 if (this.options.maxHeight) { 500 // TODO: Add a class for this option to move the css declarations. 501 this.$ul.css({ 502 'max-height': this.options.maxHeight + 'px', 503 'overflow-y': 'auto', 504 'overflow-x': 'hidden' 505 }); 506 } 507 508 if (this.options.dropUp) { 509 510 var height = Math.min(this.options.maxHeight, $('option[data-role!="divider"]', this.$select).length*26 + $('option[data-role="divider"]', this.$select).length*19 + (this.options.includeSelectAllOption ? 26 : 0) + (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering ? 44 : 0)); 511 var moveCalc = height + 34; 512 513 this.$ul.css({ 514 'max-height': height + 'px', 515 'overflow-y': 'auto', 516 'overflow-x': 'hidden', 517 'margin-top': "-" + moveCalc + 'px' 518 }); 519 } 520 521 this.$container.append(this.$ul); 522 }, 523 524 /** 525 * Build the dropdown options and binds all necessary events. 526 * 527 * Uses createDivider and createOptionValue to create the necessary options. 528 */ 529 buildDropdownOptions: function() { 530 531 this.$select.children().each($.proxy(function(index, element) { 532 533 var $element = $(element);
vendor: 4,140 bytes, lines 534-639
534 // Support optgroups and options without a group simultaneously. 535 var tag = $element.prop('tagName') 536 .toLowerCase(); 537 538 if ($element.prop('value') === this.options.selectAllValue) { 539 return; 540 } 541 542 if (tag === 'optgroup') { 543 this.createOptgroup(element); 544 } 545 else if (tag === 'option') { 546 547 if ($element.data('role') === 'divider') { 548 this.createDivider(); 549 } 550 else { 551 this.createOptionValue(element); 552 } 553 554 } 555 556 // Other illegal tags will be ignored. 557 }, this)); 558 559 // Bind the change event on the dropdown elements. 560 $('li:not(.multiselect-group) input', this.$ul).on('change', $.proxy(function(event) { 561 var $target = $(event.target); 562 563 var checked = $target.prop('checked') || false; 564 var isSelectAllOption = $target.val() === this.options.selectAllValue; 565 566 // Apply or unapply the configured selected class. 567 if (this.options.selectedClass) { 568 if (checked) { 569 $target.closest('li') 570 .addClass(this.options.selectedClass); 571 } 572 else { 573 $target.closest('li') 574 .removeClass(this.options.selectedClass); 575 } 576 } 577 578 // Get the corresponding option. 579 var value = $target.val(); 580 var $option = this.getOptionByValue(value); 581 582 var $optionsNotThis = $('option', this.$select).not($option); 583 var $checkboxesNotThis = $('input', this.$container).not($target); 584 585 if (isSelectAllOption) { 586 587 if (checked) { 588 this.selectAll(this.options.selectAllJustVisible); 589 } 590 else { 591 this.deselectAll(this.options.selectAllJustVisible); 592 } 593 } 594 else { 595 if (checked) { 596 $option.prop('selected', true); 597 598 if (this.options.multiple) { 599 // Simply select additional option. 600 $option.prop('selected', true); 601 } 602 else { 603 // Unselect all other options and corresponding checkboxes. 604 if (this.options.selectedClass) { 605 $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass); 606 } 607 608 $($checkboxesNotThis).prop('checked', false); 609 $optionsNotThis.prop('selected', false); 610 611 // It's a single selection, so close. 612 this.$button.click(); 613 } 614 615 if (this.options.selectedClass === "active") { 616 $optionsNotThis.closest("a").css("outline", ""); 617 } 618 } 619 else { 620 // Unselect option. 621 $option.prop('selected', false); 622 } 623 624 // To prevent select all from firing onChange: #575 625 this.options.onChange($option, checked); 626 627 // Do not update select all or optgroups on select all change! 628 this.updateSelectAll(); 629 630 if (this.options.enableClickableOptGroups && this.options.multiple) { 631 this.updateOptGroups(); 632 } 633 } 634 635 this.$select.change(); 636 this.updateButtonText(); 637 638 if(this.options.preventInputChangeEvent) { 639 return false;
640 } 641 }, this)); 642 643 $('li a', this.$ul).on('mousedown', function(e) { 644 if (e.shiftKey) { 645 // Prevent selecting text by Shift+click 646 return false; 647 } 648 }); 649 650 $('li a', this.$ul).on('touchstart click', $.proxy(function(event) { 651 event.stopPropagation(); 652 653 var $target = $(event.target); 654 655 if (event.shiftKey && this.options.multiple) { 656 if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) 657 event.preventDefault(); 658 $target = $target.find("input"); 659 $target.prop("checked", !$target.prop("checked")); 660 } 661 var checked = $target.prop('checked') || false; 662 663 if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range 664 var from = $target.closest("li").index(); 665 var to = this.lastToggledInput.closest("li").index(); 666 667 if (from > to) { // Swap the indices 668 var tmp = to; 669 to = from; 670 from = tmp; 671 } 672 673 // Make sure we grab all elements since slice excludes the last index 674 ++to; 675 676 // Change the checkboxes and underlying options 677 var range = this.$ul.find("li").slice(from, to).find("input"); 678 679 range.prop('checked', checked); 680 681 if (this.options.selectedClass) { 682 range.closest('li') 683 .toggleClass(this.options.selectedClass, checked); 684 } 685 686 for (var i = 0, j = range.length; i < j; i++) { 687 var $checkbox = $(range[i]); 688 689 var $option = this.getOptionByValue($checkbox.val()); 690 691 $option.prop('selected', checked); 692 } 693 } 694 695 // Trigger the select "change" event 696 $target.trigger("change"); 697 } 698 699 // Remembers last clicked option 700 if($target.is("input") && !$target.closest("li").is(".multiselect-item")){ 701 this.lastToggledInput = $target; 702 } 703 704 $target.blur(); 705 }, this)); 706 707 // Keyboard support. 708 this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) { 709 if ($('input[type="text"]', this.$container).is(':focus')) { 710 return; 711 } 712 713 if (event.keyCode === 9 && this.$container.hasClass('open')) { 714 this.$button.click(); 715 } 716 else { 717 var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible"); 718 719 if (!$items.length) { 720 return; 721 } 722 723 var index = $items.index($items.filter(':focus')); 724 725 // Navigation up. 726 if (event.keyCode === 38 && index > 0) { 727 index--; 728 } 729 // Navigate down. 730 else if (event.keyCode === 40 && index < $items.length - 1) { 731 index++; 732 } 733 else if (!~index) { 734 index = 0; 735 } 736 737 var $current = $items.eq(index); 738 $current.focus(); 739 740 if (event.keyCode === 32 || event.keyCode === 13) { 741 var $checkbox = $current.find('input'); 742 743 $checkbox.prop("checked", !$checkbox.prop("checked")); 744 $checkbox.change(); 745 } 746 747 event.stopPropagation(); 748 event.preventDefault(); 749 } 750 }, this)); 751 752 if (this.options.enableClickableOptGroups && this.options.multiple) { 753 $("li.multiselect-group input", this.$ul).on("change", $.proxy(function(event) { 754 event.stopPropagation(); 755 756 var $target = $(event.target); 757 var checked = $target.prop('checked') || false; 758 759 var $li = $(event.target).closest('li'); 760 var $group = $li.nextUntil("li.multiselect-group") 761 .not('.multiselect-filter-hidden') 762 .not('.disabled'); 763
764 var $inputs = $group.find("input"); 765 766 var values = []; 767 var $options = []; 768 769 $.each($inputs, $.proxy(function(index, input) { 770 var value = $(input).val(); 771 var $option = this.getOptionByValue(value); 772 773 if (checked) { 774 $(input).prop('checked', true); 775 $(input).closest('li') 776 .addClass(this.options.selectedClass); 777 778 $option.prop('selected', true); 779 } 780 else { 781 $(input).prop('checked', false); 782 $(input).closest('li') 783 .removeClass(this.options.selectedClass); 784 785 $option.prop('selected', false); 786 } 787 788 $options.push(this.getOptionByValue(value)); 789 }, this)) 790 791 // Cannot use select or deselect here because it would call updateOptGroups again. 792 793 this.options.onChange($options, checked); 794 795 this.updateButtonText(); 796 this.updateSelectAll(); 797 }, this)); 798 } 799 800 if (this.options.enableCollapsibleOptGroups && this.options.multiple) { 801 $("li.multiselect-group .caret-container", this.$ul).on("click", $.proxy(function(event) { 802 var $li = $(event.target).closest('li'); 803 var $inputs = $li.nextUntil("li.multiselect-group") 804 .not('.multiselect-filter-hidden'); 805 806 var visible = true; 807 $inputs.each(function() { 808 visible = visible && $(this).is(':visible'); 809 }); 810 811 if (visible) { 812 $inputs.hide() 813 .addClass('multiselect-collapsible-hidden'); 814 } 815 else { 816 $inputs.show() 817 .removeClass('multiselect-collapsible-hidden'); 818 } 819 }, this)); 820 821 $("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); 822 $("li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px'); 823 $("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px'); 824 } 825 }, 826 827 /** 828 * Create an option using the given select option. 829 * 830 * @param {jQuery} element 831 */ 832 createOptionValue: function(element) { 833 var $element = $(element); 834 if ($element.is(':selected')) { 835 $element.prop('selected', true); 836 } 837 838 // Support the label attribute on options. 839 var label = this.options.optionLabel(element); 840 var classes = this.options.optionClass(element); 841 var value = $element.val(); 842 var inputType = this.options.multiple ? "checkbox" : "radio"; 843 844 var $li = $(this.options.templates.li); 845 var $label = $('label', $li); 846 $label.addClass(inputType);
847 $li.addClass(classes); 848 849 if (this.options.enableHTML) { 850 $label.html(" " + label); 851 } 852 else { 853 $label.text(" " + label); 854 } 855 856 var $checkbox = $('<input/>').attr('type', inputType); 857 858 var name = this.options.checkboxName($element); 859 if (name) { 860 $checkbox.attr('name', name); 861 } 862 863 $label.prepend($checkbox); 864 865 var selected = $element.prop('selected') || false; 866 $checkbox.val(value); 867 868 if (value === this.options.selectAllValue) { 869 $li.addClass("multiselect-item multiselect-all"); 870 $checkbox.parent().parent() 871 .addClass('multiselect-all'); 872 } 873 874 $label.attr('title', $element.attr('title')); 875 876 this.$ul.append($li); 877 878 if ($element.is(':disabled')) { 879 $checkbox.attr('disabled', 'disabled') 880 .prop('disabled', true) 881 .closest('a') 882 .attr("tabindex", "-1") 883 .closest('li') 884 .addClass('disabled'); 885 } 886 887 $checkbox.prop('checked', selected); 888 889 if (selected && this.options.selectedClass) { 890 $checkbox.closest('li') 891 .addClass(this.options.selectedClass); 892 } 893 }, 894 895 /** 896 * Creates a divider using the given select option. 897 * 898 * @param {jQuery} element 899 */ 900 createDivider: function(element) { 901 var $divider = $(this.options.templates.divider); 902 this.$ul.append($divider); 903 }, 904 905 /** 906 * Creates an optgroup. 907 * 908 * @param {jQuery} group 909 */ 910 createOptgroup: function(group) { 911 var label = $(group).attr("label"); 912 var value = $(group).attr("value"); 913 var $li = $('<li class="multiselect-item multiselect-group"><a href="javascript:void(0);"><label><b></b></label></a></li>'); 914 915 var classes = this.options.optionClass(group);
916 $li.addClass(classes); 917 918 if (this.options.enableHTML) { 919 $('label b', $li).html(" " + label); 920 } 921 else { 922 $('label b', $li).text(" " + label); 923 } 924 925 if (this.options.enableCollapsibleOptGroups && this.options.multiple) { 926 $('a', $li).append('<span class="caret-container"><b class="caret"></b></span>'); 927 } 928 929 if (this.options.enableClickableOptGroups && this.options.multiple) { 930 $('a label', $li).prepend('<input type="checkbox" value="' + value + '"/>'); 931 } 932 933 if ($(group).is(':disabled')) { 934 $li.addClass('disabled'); 935 } 936 937 this.$ul.append($li); 938 939 $("option", group).each($.proxy(function($, group) { 940 this.createOptionValue(group); 941 }, this)) 942 }, 943 944 /** 945 * Build the select all. 946 * 947 * Checks if a select all has already been created. 948 */ 949 buildSelectAll: function() { 950 if (typeof this.options.selectAllValue === 'number') { 951 this.options.selectAllValue = this.options.selectAllValue.toString(); 952 } 953 954 var alreadyHasSelectAll = this.hasSelectAll(); 955 956 if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple 957 && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) { 958 959 // Check whether to add a divider after the select all. 960 if (this.options.includeSelectAllDivider) { 961 this.$ul.prepend($(this.options.templates.divider)); 962 } 963 964 var $li = $(this.options.templates.li); 965 $('label', $li).addClass("checkbox"); 966 967 if (this.options.enableHTML) { 968 $('label', $li).html(" " + this.options.selectAllText); 969 } 970 else { 971 $('label', $li).text(" " + this.options.selectAllText); 972 } 973 974 if (this.options.selectAllName) { 975 $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />'); 976 } 977 else { 978 $('label', $li).prepend('<input type="checkbox" />'); 979 } 980 981 var $checkbox = $('input', $li); 982 $checkbox.val(this.options.selectAllValue); 983 984 $li.addClass("multiselect-item multiselect-all"); 985 $checkbox.parent().parent() 986 .addClass('multiselect-all'); 987 988 this.$ul.prepend($li); 989 990 $checkbox.prop('checked', false); 991 } 992 }, 993 994 /** 995 * Builds the filter. 996 */ 997 buildFilter: function() { 998 999 // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. 1000 if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { 1001 var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering); 1002 1003 if (this.$select.find('option').length >= enableFilterLength) { 1004 1005 this.$filter = $(this.options.templates.filter); 1006 $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); 1007 1008 // Adds optional filter clear button 1009 if(this.options.includeFilterClearBtn) { 1010 var clearBtn = $(this.options.templates.filterClearBtn); 1011 clearBtn.on('click', $.proxy(function(event){ 1012 clearTimeout(this.searchTimeout); 1013 1014 this.$filter.find('.multiselect-search').val(''); 1015 $('li', this.$ul).show().removeClass('multiselect-filter-hidden'); 1016 1017 this.updateSelectAll(); 1018 1019 if (this.options.enableClickableOptGroups && this.options.multiple) { 1020 this.updateOptGroups(); 1021 } 1022 1023 }, this)); 1024 this.$filter.find('.input-group').append(clearBtn); 1025 } 1026 1027 this.$ul.prepend(this.$filter); 1028 1029 this.$filter.val(this.query).on('click', function(event) { 1030 event.stopPropagation(); 1031 }).on('input keydown', $.proxy(function(event) { 1032 // Cancel enter key default behaviour 1033 if (event.which === 13) { 1034 event.preventDefault(); 1035 } 1036 1037 // This is useful to catch "keydown" events after the browser has updated the control. 1038 clearTimeout(this.searchTimeout); 1039 1040 this.searchTimeout = this.asyncFunction($.proxy(function() { 1041 1042 if (this.query !== event.target.value) { 1043 this.query = event.target.value; 1044 1045 var currentGroup, currentGroupVisible; 1046 $.each($('li', this.$ul), $.proxy(function(index, element) { 1047 var value = $('input', element).length > 0 ? $('input', element).val() : ""; 1048 var text = $('label', element).text(); 1049 1050 var filterCandidate = ''; 1051 if ((this.options.filterBehavior === 'text')) { 1052 filterCandidate = text; 1053 } 1054 else if ((this.options.filterBehavior === 'value')) { 1055 filterCandidate = value; 1056 } 1057 else if (this.options.filterBehavior === 'both') { 1058 filterCandidate = text + '\n' + value; 1059 } 1060 1061 if (value !== this.options.selectAllValue && text) { 1062 1063 // By default lets assume that element is not 1064 // interesting for this search. 1065 var showElement = false;
1066 1067 if (this.options.enableCaseInsensitiveFiltering) { 1068 filterCandidate = filterCandidate.toLowerCase(); 1069 this.query = this.query.toLowerCase(); 1070 } 1071 1072 if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') { 1073 var valueToMatch = filterCandidate.trim().substring(0, this.query.length); 1074 if (this.query.indexOf(valueToMatch) > -1) { 1075 showElement = true; 1076 } 1077 } 1078 else if (filterCandidate.indexOf(this.query) > -1) { 1079 showElement = true; 1080 } 1081 1082 // Toggle current element (group or group item) according to showElement boolean. 1083 $(element).toggle(showElement) 1084 .toggleClass('multiselect-filter-hidden', !showElement); 1085 1086 // Differentiate groups and group items. 1087 if ($(element).hasClass('multiselect-group')) { 1088 // Remember group status. 1089 currentGroup = element; 1090 currentGroupVisible = showElement; 1091 } 1092 else { 1093 // Show group name when at least one of its items is visible. 1094 if (showElement) { 1095 $(currentGroup).show() 1096 .removeClass('multiselect-filter-hidden'); 1097 } 1098 1099 // Show all group items when group name satisfies filter. 1100 if (!showElement && currentGroupVisible) { 1101 $(element).show() 1102 .removeClass('multiselect-filter-hidden'); 1103 } 1104 } 1105 } 1106 }, this)); 1107 } 1108 1109 this.updateSelectAll(); 1110 1111 if (this.options.enableClickableOptGroups && this.options.multiple) { 1112 this.updateOptGroups(); 1113 } 1114 1115 this.options.onFiltering(event.target); 1116 1117 }, this), 300, this); 1118 }, this)); 1119 } 1120 } 1121 }, 1122 1123 /** 1124 * Unbinds the whole plugin. 1125 */ 1126 destroy: function() { 1127 this.$container.remove(); 1128 this.$select.show(); 1129 this.$select.data('multiselect', null); 1130 }, 1131 1132 /** 1133 * Refreshs the multiselect based on the selected options of the select. 1134 */ 1135 refresh: function () { 1136 var inputs = $.map($('li input', this.$ul), $); 1137 1138 $('option', this.$select).each($.proxy(function (index, element) { 1139 var $elem = $(element); 1140 var value = $elem.val(); 1141 var $input; 1142 for (var i = inputs.length; 0 < i--; /**/) { 1143 if (value !== ($input = inputs[i]).val()) 1144 continue; // wrong li 1145 1146 if ($elem.is(':selected')) { 1147 $input.prop('checked', true); 1148 1149 if (this.options.selectedClass) { 1150 $input.closest('li') 1151 .addClass(this.options.selectedClass); 1152 } 1153 } 1154 else { 1155 $input.prop('checked', false); 1156 1157 if (this.options.selectedClass) { 1158 $input.closest('li') 1159 .removeClass(this.options.selectedClass); 1160 } 1161 } 1162 1163 if ($elem.is(":disabled")) { 1164 $input.attr('disabled', 'disabled') 1165 .prop('disabled', true) 1166 .closest('li') 1167 .addClass('disabled'); 1168 } 1169 else { 1170 $input.prop('disabled', false) 1171 .closest('li') 1172 .removeClass('disabled'); 1173 } 1174 break; // assumes unique values 1175 } 1176 }, this)); 1177 1178 this.updateButtonText(); 1179 this.updateSelectAll(); 1180 1181 if (this.options.enableClickableOptGroups && this.options.multiple) { 1182 this.updateOptGroups(); 1183 } 1184 }, 1185 1186 /** 1187 * Select all options of the given values. 1188 * 1189 * If triggerOnChange is set to true, the on change event is triggered if 1190 * and only if one value is passed. 1191 * 1192 * @param {Array} selectValues 1193 * @param {Boolean} triggerOnChange 1194 */ 1195 select: function(selectValues, triggerOnChange) { 1196 if(!$.isArray(selectValues)) { 1197 selectValues = [selectValues]; 1198 } 1199 1200 for (var i = 0; i < selectValues.length; i++) { 1201 var value = selectValues[i]; 1202 1203 if (value === null || value === undefined) { 1204 continue; 1205 } 1206 1207 var $option = this.getOptionByValue(value); 1208 var $checkbox = this.getInputByValue(value); 1209 1210 if($option === undefined || $checkbox === undefined) { 1211 continue; 1212 } 1213 1214 if (!this.options.multiple) { 1215 this.deselectAll(false); 1216 } 1217 1218 if (this.options.selectedClass) { 1219 $checkbox.closest('li') 1220 .addClass(this.options.selectedClass); 1221 } 1222 1223 $checkbox.prop('checked', true); 1224 $option.prop('selected', true); 1225 1226 if (triggerOnChange) { 1227 this.options.onChange($option, true); 1228 } 1229 } 1230 1231 this.updateButtonText(); 1232 this.updateSelectAll(); 1233 1234 if (this.options.enableClickableOptGroups && this.options.multiple) { 1235 this.updateOptGroups(); 1236 } 1237 }, 1238 1239 /** 1240 * Clears all selected items. 1241 */ 1242 clearSelection: function () { 1243 this.deselectAll(false); 1244 this.updateButtonText(); 1245 this.updateSelectAll(); 1246 1247 if (this.options.enableClickableOptGroups && this.options.multiple) { 1248 this.updateOptGroups(); 1249 } 1250 }, 1251 1252 /** 1253 * Deselects all options of the given values. 1254 * 1255 * If triggerOnChange is set to true, the on change event is triggered, if 1256 * and only if one value is passed. 1257 * 1258 * @param {Array} deselectValues 1259 * @param {Boolean} triggerOnChange 1260 */ 1261 deselect: function(deselectValues, triggerOnChange) { 1262 if(!$.isArray(deselectValues)) { 1263 deselectValues = [deselectValues]; 1264 } 1265 1266 for (var i = 0; i < deselectValues.length; i++) { 1267 var value = deselectValues[i]; 1268 1269 if (value === null || value === undefined) { 1270 continue; 1271 } 1272 1273 var $option = this.getOptionByValue(value); 1274 var $checkbox = this.getInputByValue(value); 1275 1276 if($option === undefined || $checkbox === undefined) { 1277 continue; 1278 } 1279 1280 if (this.options.selectedClass) { 1281 $checkbox.closest('li') 1282 .removeClass(this.options.selectedClass); 1283 } 1284 1285 $checkbox.prop('checked', false); 1286 $option.prop('selected', false); 1287 1288 if (triggerOnChange) { 1289 this.options.onChange($option, false); 1290 } 1291 } 1292 1293 this.updateButtonText(); 1294 this.updateSelectAll(); 1295 1296 if (this.options.enableClickableOptGroups && this.options.multiple) { 1297 this.updateOptGroups(); 1298 } 1299 }, 1300 1301 /** 1302 * Selects all enabled & visible options. 1303 * 1304 * If justVisible is true or not specified, only visible options are selected. 1305 * 1306 * @param {Boolean} justVisible 1307 * @param {Boolean} triggerOnSelectAll 1308 */ 1309 selectAll: function (justVisible, triggerOnSelectAll) { 1310 1311 var justVisible = typeof justVisible === 'undefined' ? true : justVisible; 1312 var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); 1313 var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); 1314 1315 if(justVisible) { 1316 $('input:enabled' , visibleLis).prop('checked', true); 1317 visibleLis.addClass(this.options.selectedClass); 1318 1319 $('input:enabled' , visibleLis).each($.proxy(function(index, element) { 1320 var value = $(element).val(); 1321 var option = this.getOptionByValue(value); 1322 $(option).prop('selected', true); 1323 }, this)); 1324 } 1325 else { 1326 $('input:enabled' , allLis).prop('checked', true); 1327 allLis.addClass(this.options.selectedClass); 1328 1329 $('input:enabled' , allLis).each($.proxy(function(index, element) { 1330 var value = $(element).val(); 1331 var option = this.getOptionByValue(value); 1332 $(option).prop('selected', true); 1333 }, this)); 1334 } 1335 1336 $('li input[value="' + this.options.selectAllValue + '"]').prop('checked', true); 1337 1338 if (this.options.enableClickableOptGroups && this.options.multiple) { 1339 this.updateOptGroups(); 1340 } 1341 1342 if (triggerOnSelectAll) { 1343 this.options.onSelectAll(); 1344 } 1345 }, 1346 1347 /** 1348 * Deselects all options. 1349 * 1350 * If justVisible is true or not specified, only visible options are deselected. 1351 * 1352 * @param {Boolean} justVisible 1353 */ 1354 deselectAll: function (justVisible, triggerOnDeselectAll) { 1355 1356 var justVisible = typeof justVisible === 'undefined' ? true : justVisible; 1357 var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); 1358 var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); 1359 1360 if(justVisible) { 1361 $('input[type="checkbox"]:enabled' , visibleLis).prop('checked', false); 1362 visibleLis.removeClass(this.options.selectedClass); 1363 1364 $('input[type="checkbox"]:enabled' , visibleLis).each($.proxy(function(index, element) { 1365 var value = $(element).val(); 1366 var option = this.getOptionByValue(value); 1367 $(option).prop('selected', false); 1368 }, this)); 1369 } 1370 else { 1371 $('input[type="checkbox"]:enabled' , allLis).prop('checked', false); 1372 allLis.removeClass(this.options.selectedClass); 1373 1374 $('input[type="checkbox"]:enabled' , allLis).each($.proxy(function(index, element) { 1375 var value = $(element).val(); 1376 var option = this.getOptionByValue(value); 1377 $(option).prop('selected', false); 1378 }, this)); 1379 } 1380 1381 $('li input[value="' + this.options.selectAllValue + '"]').prop('checked', false); 1382 1383 if (this.options.enableClickableOptGroups && this.options.multiple) { 1384 this.updateOptGroups(); 1385 } 1386 1387 if (triggerOnDeselectAll) { 1388 this.options.onDeselectAll(); 1389 } 1390 }, 1391 1392 /** 1393 * Rebuild the plugin. 1394 * 1395 * Rebuilds the dropdown, the filter and the select all option. 1396 */ 1397 rebuild: function() { 1398 this.$ul.html(''); 1399 1400 // Important to distinguish between radios and checkboxes. 1401 this.options.multiple = this.$select.attr('multiple') === "multiple"; 1402 1403 this.buildSelectAll(); 1404 this.buildDropdownOptions(); 1405 this.buildFilter(); 1406 1407 this.updateButtonText(); 1408 this.updateSelectAll(true); 1409 1410 if (this.options.enableClickableOptGroups && this.options.multiple) { 1411 this.updateOptGroups(); 1412 } 1413 1414 if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { 1415 this.disable(); 1416 } 1417 else { 1418 this.enable(); 1419 } 1420 1421 if (this.options.dropRight) { 1422 this.$ul.addClass('pull-right'); 1423 } 1424 }, 1425 1426 /** 1427 * The provided data will be used to build the dropdown. 1428 */ 1429 dataprovider: function(dataprovider) { 1430 1431 var groupCounter = 0; 1432 var $select = this.$select.empty(); 1433 1434 $.each(dataprovider, function (index, option) { 1435 var $tag; 1436 1437 if ($.isArray(option.children)) { // create optiongroup tag 1438 groupCounter++; 1439 1440 $tag = $('<optgroup/>').attr({ 1441 label: option.label || 'Group ' + groupCounter, 1442 disabled: !!option.disabled 1443 }); 1444
1445 forEach(option.children, function(subOption) { // add children option tags 1446 var attributes = { 1447 value: subOption.value, 1448 label: subOption.label || subOption.value, 1449 title: subOption.title, 1450 selected: !!subOption.selected, 1451 disabled: !!subOption.disabled 1452 }; 1453 1454 //Loop through attributes object and add key-value for each attribute 1455 for (var key in subOption.attributes) { 1456 attributes['data-' + key] = subOption.attributes[key]; 1457 } 1458 //Append original attributes + new data attributes to option 1459 $tag.append($('<option/>').attr(attributes)); 1460 }); 1461 } 1462 else { 1463 1464 var attributes = { 1465 'value': option.value, 1466 'label': option.label || option.value, 1467 'title': option.title, 1468 'class': option.class, 1469 'selected': !!option.selected, 1470 'disabled': !!option.disabled 1471 }; 1472 //Loop through attributes object and add key-value for each attribute 1473 for (var key in option.attributes) { 1474 attributes['data-' + key] = option.attributes[key]; 1475 } 1476 //Append original attributes + new data attributes to option 1477 $tag = $('<option/>').attr(attributes); 1478 1479 $tag.text(option.label || option.value); 1480 } 1481 1482 $select.append($tag); 1483 }); 1484 1485 this.rebuild(); 1486 }, 1487 1488 /** 1489 * Enable the multiselect. 1490 */ 1491 enable: function() { 1492 this.$select.prop('disabled', false); 1493 this.$button.prop('disabled', false) 1494 .removeClass('disabled'); 1495 }, 1496 1497 /** 1498 * Disable the multiselect. 1499 */ 1500 disable: function() { 1501 this.$select.prop('disabled', true); 1502 this.$button.prop('disabled', true) 1503 .addClass('disabled'); 1504 }, 1505 1506 /** 1507 * Set the options. 1508 * 1509 * @param {Array} options 1510 */ 1511 setOptions: function(options) { 1512 this.options = this.mergeOptions(options); 1513 }, 1514 1515 /** 1516 * Merges the given options with the default options. 1517 * 1518 * @param {Array} options 1519 * @returns {Array} 1520 */ 1521 mergeOptions: function(options) { 1522 return $.extend(true, {}, this.defaults, this.options, options); 1523 }, 1524 1525 /** 1526 * Checks whether a select all checkbox is present. 1527 * 1528 * @returns {Boolean} 1529 */ 1530 hasSelectAll: function() { 1531 return $('li.multiselect-all', this.$ul).length > 0; 1532 }, 1533 1534 /** 1535 * Update opt groups. 1536 */ 1537 updateOptGroups: function() { 1538 var $groups = $('li.multiselect-group', this.$ul) 1539 1540 $groups.each(function() { 1541 var $options = $(this).nextUntil('li.multiselect-group') 1542 .not('.multiselect-filter-hidden') 1543 .not('.disabled'); 1544 1545 var checked = true; 1546 $options.each(function() { 1547 var $input = $('input', this); 1548 1549 if (!$input.prop('checked')) { 1550 checked = false; 1551 } 1552 }); 1553 1554 $('input', this).prop('checked', checked); 1555 }); 1556 }, 1557 1558 /** 1559 * Updates the select all checkbox based on the currently displayed and selected checkboxes. 1560 */ 1561 updateSelectAll: function(notTriggerOnSelectAll) { 1562 if (this.hasSelectAll()) { 1563 var allBoxes = $("li:not(.multiselect-item):not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled", this.$ul); 1564 var allBoxesLength = allBoxes.length; 1565 var checkedBoxesLength = allBoxes.filter(":checked").length; 1566 var selectAllLi = $("li.multiselect-all", this.$ul); 1567 var selectAllInput = selectAllLi.find("input"); 1568 1569 if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) { 1570 selectAllInput.prop("checked", true); 1571 selectAllLi.addClass(this.options.selectedClass); 1572 this.options.onSelectAll(); 1573 } 1574 else { 1575 selectAllInput.prop("checked", false); 1576 selectAllLi.removeClass(this.options.selectedClass); 1577 if (checkedBoxesLength === 0) { 1578 if (!notTriggerOnSelectAll) { 1579 this.options.onDeselectAll(); 1580 } 1581 } 1582 } 1583 } 1584 }, 1585 1586 /** 1587 * Update the button text and its title based on the currently selected options. 1588 */ 1589 updateButtonText: function() {
1590 var options = this.getSelected(); 1591 1592 // First update the displayed button text. 1593 if (this.options.enableHTML) { 1594 $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select)); 1595 } 1596 else { 1597 $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select)); 1598 } 1599 1600 // Now update the title attribute of the button. 1601 $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select)); 1602 }, 1603 1604 /** 1605 * Get all selected options. 1606 * 1607 * @returns {jQUery} 1608 */ 1609 getSelected: function() { 1610 return $('option', this.$select).filter(":selected"); 1611 }, 1612 1613 /** 1614 * Gets a select option by its value. 1615 * 1616 * @param {String} value 1617 * @returns {jQuery} 1618 */ 1619 getOptionByValue: function (value) { 1620 1621 var options = $('option', this.$select); 1622 var valueToCompare = value.toString(); 1623 1624 for (var i = 0; i < options.length; i = i + 1) { 1625 var option = options[i]; 1626 if (option.value === valueToCompare) { 1627 return $(option); 1628 } 1629 } 1630 }, 1631 1632 /** 1633 * Get the input (radio/checkbox) by its value. 1634 * 1635 * @param {String} value 1636 * @returns {jQuery} 1637 */ 1638 getInputByValue: function (value) { 1639 1640 var checkboxes = $('li input', this.$ul); 1641 var valueToCompare = value.toString(); 1642 1643 for (var i = 0; i < checkboxes.length; i = i + 1) { 1644 var checkbox = checkboxes[i]; 1645 if (checkbox.value === valueToCompare) { 1646 return $(checkbox); 1647 } 1648 } 1649 }, 1650 1651 /** 1652 * Used for knockout integration. 1653 */ 1654 updateOriginalOptions: function() { 1655 this.originalOptions = this.$select.clone()[0].options; 1656 }, 1657 1658 asyncFunction: function(callback, timeout, self) { 1659 var args = Array.prototype.slice.call(arguments, 3); 1660 return setTimeout(function() { 1661 callback.apply(self || window, args); 1662 }, timeout); 1663 }, 1664 1665 setAllSelectedText: function(allSelectedText) { 1666 this.options.allSelectedText = allSelectedText; 1667 this.updateButtonText(); 1668 } 1669 }; 1670 1671 $.fn.multiselect = function(option, parameter, extraOptions) { 1672 return this.each(function() { 1673 var data = $(this).data('multiselect'); 1674 var options = typeof option === 'object' && option; 1675 1676 // Initialize the multiselect. 1677 if (!data) { 1678 data = new Multiselect(this, options); 1679 $(this).data('multiselect', data); 1680 } 1681 1682 // Call multiselect method. 1683 if (typeof option === 'string') { 1684 data[option](parameter, extraOptions); 1685 1686 if (option === 'destroy') { 1687 $(this).data('multiselect', false); 1688 } 1689 } 1690 }); 1691 }; 1692 1693 $.fn.multiselect.Constructor = Multiselect; 1694 1695 $(function() { 1696 $("select[data-role=multiselect]").multiselect(); 1697 }); 1698 1699}(window.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.