vendor: 10,116 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 this.options = this.mergeOptions($.extend({}, options, this.$select.data())); 179 180 // Placeholder via data attributes 181 if (this.$select.attr("data-placeholder")) { 182 this.options.nonSelectedText = this.$select.data("placeholder"); 183 } 184 185 // Initialization. 186 // We have to clone to create a new reference. 187 this.originalOptions = this.$select.clone()[0].options; 188 this.query = ''; 189 this.searchTimeout = null; 190 this.lastToggledInput = null; 191 192 this.options.multiple = this.$select.attr('multiple') === "multiple"; 193 this.options.onChange = $.proxy(this.options.onChange, this); 194 this.options.onSelectAll = $.proxy(this.options.onSelectAll, this); 195 this.options.onDeselectAll = $.proxy(this.options.onDeselectAll, this); 196 this.options.onDropdownShow = $.proxy(this.options.onDropdownShow, this); 197 this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this); 198 this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this); 199 this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this); 200 this.options.onInitialized = $.proxy(this.options.onInitialized, this); 201 this.options.onFiltering = $.proxy(this.options.onFiltering, this); 202 203 // Build select all if enabled. 204 this.buildContainer(); 205 this.buildButton(); 206 this.buildDropdown(); 207 this.buildSelectAll(); 208 this.buildDropdownOptions(); 209 this.buildFilter(); 210 211 this.updateButtonText(); 212 this.updateSelectAll(true); 213 214 if (this.options.enableClickableOptGroups && this.options.multiple) { 215 this.updateOptGroups(); 216 } 217 218 this.options.wasDisabled = this.$select.prop('disabled'); 219 if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { 220 this.disable(); 221 } 222 223 this.$select.wrap('<span class="multiselect-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,152 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, true); 589 } 590 else { 591 this.deselectAll(this.options.selectAllJustVisible, true); 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 if (this.options.selectedClass) { 770 if (checked) { 771 $li.addClass(this.options.selectedClass); 772 } 773 else { 774 $li.removeClass(this.options.selectedClass); 775 } 776 } 777 778 $.each($inputs, $.proxy(function(index, input) { 779 var value = $(input).val(); 780 var $option = this.getOptionByValue(value); 781 782 if (checked) { 783 $(input).prop('checked', true); 784 $(input).closest('li') 785 .addClass(this.options.selectedClass); 786 787 $option.prop('selected', true); 788 } 789 else { 790 $(input).prop('checked', false); 791 $(input).closest('li') 792 .removeClass(this.options.selectedClass); 793 794 $option.prop('selected', false); 795 } 796 797 $options.push(this.getOptionByValue(value)); 798 }, this)) 799 800 // Cannot use select or deselect here because it would call updateOptGroups again. 801 802 this.options.onChange($options, checked); 803 804 this.updateButtonText(); 805 this.updateSelectAll(); 806 }, this)); 807 } 808 809 if (this.options.enableCollapsibleOptGroups && this.options.multiple) { 810 $("li.multiselect-group .caret-container", this.$ul).on("click", $.proxy(function(event) { 811 var $li = $(event.target).closest('li'); 812 var $inputs = $li.nextUntil("li.multiselect-group") 813 .not('.multiselect-filter-hidden'); 814 815 var visible = true; 816 $inputs.each(function() { 817 visible = visible && $(this).is(':visible'); 818 }); 819 820 if (visible) { 821 $inputs.hide() 822 .addClass('multiselect-collapsible-hidden'); 823 } 824 else { 825 $inputs.show() 826 .removeClass('multiselect-collapsible-hidden'); 827 } 828 }, this)); 829 830 $("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); 831 $("li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px'); 832 $("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px'); 833 } 834 }, 835 836 /** 837 * Create an option using the given select option. 838 * 839 * @param {jQuery} element 840 */ 841 createOptionValue: function(element) { 842 var $element = $(element); 843 if ($element.is(':selected')) { 844 $element.prop('selected', true); 845 } 846 847 // Support the label attribute on options. 848 var label = this.options.optionLabel(element); 849 var classes = this.options.optionClass(element); 850 var value = $element.val(); 851 var inputType = this.options.multiple ? "checkbox" : "radio"; 852 853 var $li = $(this.options.templates.li); 854 var $label = $('label', $li); 855 $label.addClass(inputType);
856 $li.addClass(classes); 857 858 if (this.options.enableHTML) { 859 $label.html(" " + label); 860 } 861 else { 862 $label.text(" " + label); 863 } 864 865 var $checkbox = $('<input/>').attr('type', inputType); 866 867 var name = this.options.checkboxName($element); 868 if (name) { 869 $checkbox.attr('name', name); 870 } 871 872 $label.prepend($checkbox); 873 874 var selected = $element.prop('selected') || false; 875 $checkbox.val(value); 876 877 if (value === this.options.selectAllValue) { 878 $li.addClass("multiselect-item multiselect-all"); 879 $checkbox.parent().parent() 880 .addClass('multiselect-all'); 881 } 882 883 $label.attr('title', $element.attr('title')); 884 885 this.$ul.append($li); 886 887 if ($element.is(':disabled')) { 888 $checkbox.attr('disabled', 'disabled') 889 .prop('disabled', true) 890 .closest('a') 891 .attr("tabindex", "-1") 892 .closest('li') 893 .addClass('disabled'); 894 } 895 896 $checkbox.prop('checked', selected); 897 898 if (selected && this.options.selectedClass) { 899 $checkbox.closest('li') 900 .addClass(this.options.selectedClass); 901 } 902 }, 903 904 /** 905 * Creates a divider using the given select option. 906 * 907 * @param {jQuery} element 908 */ 909 createDivider: function(element) { 910 var $divider = $(this.options.templates.divider); 911 this.$ul.append($divider); 912 }, 913 914 /** 915 * Creates an optgroup. 916 * 917 * @param {jQuery} group 918 */ 919 createOptgroup: function(group) { 920 var label = $(group).attr("label"); 921 var value = $(group).attr("value"); 922 var $li = $('<li class="multiselect-item multiselect-group"><a href="javascript:void(0);"><label><b></b></label></a></li>'); 923 924 var classes = this.options.optionClass(group);
925 $li.addClass(classes); 926 927 if (this.options.enableHTML) { 928 $('label b', $li).html(" " + label); 929 } 930 else { 931 $('label b', $li).text(" " + label); 932 } 933 934 if (this.options.enableCollapsibleOptGroups && this.options.multiple) { 935 $('a', $li).append('<span class="caret-container"><b class="caret"></b></span>'); 936 } 937 938 if (this.options.enableClickableOptGroups && this.options.multiple) { 939 $('a label', $li).prepend('<input type="checkbox" value="' + value + '"/>'); 940 } 941 942 if ($(group).is(':disabled')) { 943 $li.addClass('disabled'); 944 } 945 946 this.$ul.append($li); 947 948 $("option", group).each($.proxy(function($, group) { 949 this.createOptionValue(group); 950 }, this)) 951 }, 952 953 /** 954 * Build the select all. 955 * 956 * Checks if a select all has already been created. 957 */ 958 buildSelectAll: function() { 959 if (typeof this.options.selectAllValue === 'number') { 960 this.options.selectAllValue = this.options.selectAllValue.toString(); 961 } 962 963 var alreadyHasSelectAll = this.hasSelectAll(); 964 965 if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple 966 && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) { 967 968 // Check whether to add a divider after the select all. 969 if (this.options.includeSelectAllDivider) { 970 this.$ul.prepend($(this.options.templates.divider)); 971 } 972 973 var $li = $(this.options.templates.li); 974 $('label', $li).addClass("checkbox"); 975 976 if (this.options.enableHTML) { 977 $('label', $li).html(" " + this.options.selectAllText); 978 } 979 else { 980 $('label', $li).text(" " + this.options.selectAllText); 981 } 982 983 if (this.options.selectAllName) { 984 $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />'); 985 } 986 else { 987 $('label', $li).prepend('<input type="checkbox" />'); 988 } 989 990 var $checkbox = $('input', $li); 991 $checkbox.val(this.options.selectAllValue); 992 993 $li.addClass("multiselect-item multiselect-all"); 994 $checkbox.parent().parent() 995 .addClass('multiselect-all'); 996 997 this.$ul.prepend($li); 998 999 $checkbox.prop('checked', false); 1000 } 1001 }, 1002 1003 /** 1004 * Builds the filter. 1005 */ 1006 buildFilter: function() { 1007 1008 // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. 1009 if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { 1010 var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering); 1011 1012 if (this.$select.find('option').length >= enableFilterLength) { 1013 1014 this.$filter = $(this.options.templates.filter); 1015 $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); 1016 1017 // Adds optional filter clear button 1018 if(this.options.includeFilterClearBtn) { 1019 var clearBtn = $(this.options.templates.filterClearBtn); 1020 clearBtn.on('click', $.proxy(function(event){ 1021 clearTimeout(this.searchTimeout); 1022 1023 this.$filter.find('.multiselect-search').val(''); 1024 $('li', this.$ul).show().removeClass('multiselect-filter-hidden'); 1025 1026 this.updateSelectAll(); 1027 1028 if (this.options.enableClickableOptGroups && this.options.multiple) { 1029 this.updateOptGroups(); 1030 } 1031 1032 }, this)); 1033 this.$filter.find('.input-group').append(clearBtn); 1034 } 1035 1036 this.$ul.prepend(this.$filter); 1037 1038 this.$filter.val(this.query).on('click', function(event) { 1039 event.stopPropagation(); 1040 }).on('input keydown', $.proxy(function(event) { 1041 // Cancel enter key default behaviour 1042 if (event.which === 13) { 1043 event.preventDefault(); 1044 } 1045 1046 // This is useful to catch "keydown" events after the browser has updated the control. 1047 clearTimeout(this.searchTimeout); 1048 1049 this.searchTimeout = this.asyncFunction($.proxy(function() { 1050 1051 if (this.query !== event.target.value) { 1052 this.query = event.target.value; 1053 1054 var currentGroup, currentGroupVisible; 1055 $.each($('li', this.$ul), $.proxy(function(index, element) { 1056 var value = $('input', element).length > 0 ? $('input', element).val() : ""; 1057 var text = $('label', element).text(); 1058 1059 var filterCandidate = ''; 1060 if ((this.options.filterBehavior === 'text')) { 1061 filterCandidate = text; 1062 } 1063 else if ((this.options.filterBehavior === 'value')) { 1064 filterCandidate = value; 1065 } 1066 else if (this.options.filterBehavior === 'both') { 1067 filterCandidate = text + '\n' + value; 1068 } 1069 1070 if (value !== this.options.selectAllValue && text) { 1071 1072 // By default lets assume that element is not 1073 // interesting for this search. 1074 var showElement = false;
1075 1076 if (this.options.enableCaseInsensitiveFiltering) { 1077 filterCandidate = filterCandidate.toLowerCase(); 1078 this.query = this.query.toLowerCase(); 1079 } 1080 1081 if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') { 1082 var valueToMatch = filterCandidate.trim().substring(0, this.query.length); 1083 if (this.query.indexOf(valueToMatch) > -1) { 1084 showElement = true; 1085 } 1086 } 1087 else if (filterCandidate.indexOf(this.query) > -1) { 1088 showElement = true; 1089 } 1090 1091 // Toggle current element (group or group item) according to showElement boolean. 1092 $(element).toggle(showElement) 1093 .toggleClass('multiselect-filter-hidden', !showElement); 1094 1095 // Differentiate groups and group items. 1096 if ($(element).hasClass('multiselect-group')) { 1097 // Remember group status. 1098 currentGroup = element; 1099 currentGroupVisible = showElement; 1100 } 1101 else { 1102 // Show group name when at least one of its items is visible. 1103 if (showElement) { 1104 $(currentGroup).show() 1105 .removeClass('multiselect-filter-hidden'); 1106 } 1107 1108 // Show all group items when group name satisfies filter. 1109 if (!showElement && currentGroupVisible) { 1110 $(element).show() 1111 .removeClass('multiselect-filter-hidden'); 1112 } 1113 } 1114 } 1115 }, this)); 1116 } 1117 1118 this.updateSelectAll(); 1119 1120 if (this.options.enableClickableOptGroups && this.options.multiple) { 1121 this.updateOptGroups(); 1122 } 1123 1124 this.options.onFiltering(event.target); 1125 1126 }, this), 300, this); 1127 }, this)); 1128 } 1129 } 1130 }, 1131 1132 /** 1133 * Unbinds the whole plugin. 1134 */ 1135 destroy: function() { 1136 this.$container.remove(); 1137 this.$select.show(); 1138 1139 // reset original state 1140 this.$select.prop('disabled', this.options.wasDisabled); 1141 1142 this.$select.data('multiselect', null); 1143 }, 1144 1145 /** 1146 * Refreshs the multiselect based on the selected options of the select. 1147 */ 1148 refresh: function () { 1149 var inputs = $.map($('li input', this.$ul), $); 1150 1151 $('option', this.$select).each($.proxy(function (index, element) { 1152 var $elem = $(element); 1153 var value = $elem.val(); 1154 var $input; 1155 for (var i = inputs.length; 0 < i--; /**/) { 1156 if (value !== ($input = inputs[i]).val()) 1157 continue; // wrong li 1158 1159 if ($elem.is(':selected')) { 1160 $input.prop('checked', true); 1161 1162 if (this.options.selectedClass) { 1163 $input.closest('li') 1164 .addClass(this.options.selectedClass); 1165 } 1166 } 1167 else { 1168 $input.prop('checked', false); 1169 1170 if (this.options.selectedClass) { 1171 $input.closest('li') 1172 .removeClass(this.options.selectedClass); 1173 } 1174 } 1175 1176 if ($elem.is(":disabled")) { 1177 $input.attr('disabled', 'disabled') 1178 .prop('disabled', true) 1179 .closest('li') 1180 .addClass('disabled'); 1181 } 1182 else { 1183 $input.prop('disabled', false) 1184 .closest('li') 1185 .removeClass('disabled'); 1186 } 1187 break; // assumes unique values 1188 } 1189 }, this)); 1190 1191 this.updateButtonText(); 1192 this.updateSelectAll(); 1193 1194 if (this.options.enableClickableOptGroups && this.options.multiple) { 1195 this.updateOptGroups(); 1196 } 1197 }, 1198 1199 /** 1200 * Select all options of the given values. 1201 * 1202 * If triggerOnChange is set to true, the on change event is triggered if 1203 * and only if one value is passed. 1204 * 1205 * @param {Array} selectValues 1206 * @param {Boolean} triggerOnChange 1207 */ 1208 select: function(selectValues, triggerOnChange) { 1209 if(!$.isArray(selectValues)) { 1210 selectValues = [selectValues]; 1211 } 1212 1213 for (var i = 0; i < selectValues.length; i++) { 1214 var value = selectValues[i]; 1215 1216 if (value === null || value === undefined) { 1217 continue; 1218 } 1219 1220 var $option = this.getOptionByValue(value); 1221 var $checkbox = this.getInputByValue(value); 1222 1223 if($option === undefined || $checkbox === undefined) { 1224 continue; 1225 } 1226 1227 if (!this.options.multiple) { 1228 this.deselectAll(false); 1229 } 1230 1231 if (this.options.selectedClass) { 1232 $checkbox.closest('li') 1233 .addClass(this.options.selectedClass); 1234 } 1235 1236 $checkbox.prop('checked', true); 1237 $option.prop('selected', true); 1238 1239 if (triggerOnChange) { 1240 this.options.onChange($option, true); 1241 } 1242 } 1243 1244 this.updateButtonText(); 1245 this.updateSelectAll(); 1246 1247 if (this.options.enableClickableOptGroups && this.options.multiple) { 1248 this.updateOptGroups(); 1249 } 1250 }, 1251 1252 /** 1253 * Clears all selected items. 1254 */ 1255 clearSelection: function () { 1256 this.deselectAll(false); 1257 this.updateButtonText(); 1258 this.updateSelectAll(); 1259 1260 if (this.options.enableClickableOptGroups && this.options.multiple) { 1261 this.updateOptGroups(); 1262 } 1263 }, 1264 1265 /** 1266 * Deselects all options of the given values. 1267 * 1268 * If triggerOnChange is set to true, the on change event is triggered, if 1269 * and only if one value is passed. 1270 * 1271 * @param {Array} deselectValues 1272 * @param {Boolean} triggerOnChange 1273 */ 1274 deselect: function(deselectValues, triggerOnChange) { 1275 if(!$.isArray(deselectValues)) { 1276 deselectValues = [deselectValues]; 1277 } 1278 1279 for (var i = 0; i < deselectValues.length; i++) { 1280 var value = deselectValues[i]; 1281 1282 if (value === null || value === undefined) { 1283 continue; 1284 } 1285 1286 var $option = this.getOptionByValue(value); 1287 var $checkbox = this.getInputByValue(value); 1288 1289 if($option === undefined || $checkbox === undefined) { 1290 continue; 1291 } 1292 1293 if (this.options.selectedClass) { 1294 $checkbox.closest('li') 1295 .removeClass(this.options.selectedClass); 1296 } 1297 1298 $checkbox.prop('checked', false); 1299 $option.prop('selected', false); 1300 1301 if (triggerOnChange) { 1302 this.options.onChange($option, false); 1303 } 1304 } 1305 1306 this.updateButtonText(); 1307 this.updateSelectAll(); 1308 1309 if (this.options.enableClickableOptGroups && this.options.multiple) { 1310 this.updateOptGroups(); 1311 } 1312 }, 1313 1314 /** 1315 * Selects all enabled & visible options. 1316 * 1317 * If justVisible is true or not specified, only visible options are selected. 1318 * 1319 * @param {Boolean} justVisible 1320 * @param {Boolean} triggerOnSelectAll 1321 */ 1322 selectAll: function (justVisible, triggerOnSelectAll) { 1323 1324 var justVisible = typeof justVisible === 'undefined' ? true : justVisible; 1325 var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); 1326 var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); 1327 1328 if(justVisible) { 1329 $('input:enabled' , visibleLis).prop('checked', true); 1330 visibleLis.addClass(this.options.selectedClass); 1331 1332 $('input:enabled' , visibleLis).each($.proxy(function(index, element) { 1333 var value = $(element).val(); 1334 var option = this.getOptionByValue(value); 1335 $(option).prop('selected', true); 1336 }, this)); 1337 } 1338 else { 1339 $('input:enabled' , allLis).prop('checked', true); 1340 allLis.addClass(this.options.selectedClass); 1341 1342 $('input:enabled' , allLis).each($.proxy(function(index, element) { 1343 var value = $(element).val(); 1344 var option = this.getOptionByValue(value); 1345 $(option).prop('selected', true); 1346 }, this)); 1347 } 1348 1349 $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', true); 1350 1351 if (this.options.enableClickableOptGroups && this.options.multiple) { 1352 this.updateOptGroups(); 1353 } 1354 1355 if (triggerOnSelectAll) { 1356 this.options.onSelectAll(); 1357 } 1358 }, 1359 1360 /** 1361 * Deselects all options. 1362 * 1363 * If justVisible is true or not specified, only visible options are deselected. 1364 * 1365 * @param {Boolean} justVisible 1366 */ 1367 deselectAll: function (justVisible, triggerOnDeselectAll) { 1368 1369 var justVisible = typeof justVisible === 'undefined' ? true : justVisible; 1370 var allLis = $("li:not(.divider):not(.disabled):not(.multiselect-group)", this.$ul); 1371 var visibleLis = $("li:not(.divider):not(.disabled):not(.multiselect-group):not(.multiselect-filter-hidden):not(.multiselect-collapisble-hidden)", this.$ul).filter(':visible'); 1372 1373 if(justVisible) { 1374 $('input[type="checkbox"]:enabled' , visibleLis).prop('checked', false); 1375 visibleLis.removeClass(this.options.selectedClass); 1376 1377 $('input[type="checkbox"]:enabled' , visibleLis).each($.proxy(function(index, element) { 1378 var value = $(element).val(); 1379 var option = this.getOptionByValue(value); 1380 $(option).prop('selected', false); 1381 }, this)); 1382 } 1383 else { 1384 $('input[type="checkbox"]:enabled' , allLis).prop('checked', false); 1385 allLis.removeClass(this.options.selectedClass); 1386 1387 $('input[type="checkbox"]:enabled' , allLis).each($.proxy(function(index, element) { 1388 var value = $(element).val(); 1389 var option = this.getOptionByValue(value); 1390 $(option).prop('selected', false); 1391 }, this)); 1392 } 1393 1394 $('li input[value="' + this.options.selectAllValue + '"]', this.$ul).prop('checked', false); 1395 1396 if (this.options.enableClickableOptGroups && this.options.multiple) { 1397 this.updateOptGroups(); 1398 } 1399 1400 if (triggerOnDeselectAll) { 1401 this.options.onDeselectAll(); 1402 } 1403 }, 1404 1405 /** 1406 * Rebuild the plugin. 1407 * 1408 * Rebuilds the dropdown, the filter and the select all option. 1409 */ 1410 rebuild: function() { 1411 this.$ul.html(''); 1412 1413 // Important to distinguish between radios and checkboxes. 1414 this.options.multiple = this.$select.attr('multiple') === "multiple"; 1415 1416 this.buildSelectAll(); 1417 this.buildDropdownOptions(); 1418 this.buildFilter(); 1419 1420 this.updateButtonText(); 1421 this.updateSelectAll(true); 1422 1423 if (this.options.enableClickableOptGroups && this.options.multiple) { 1424 this.updateOptGroups(); 1425 } 1426 1427 if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { 1428 this.disable(); 1429 } 1430 else { 1431 this.enable(); 1432 } 1433 1434 if (this.options.dropRight) { 1435 this.$ul.addClass('pull-right'); 1436 } 1437 }, 1438 1439 /** 1440 * The provided data will be used to build the dropdown. 1441 */ 1442 dataprovider: function(dataprovider) { 1443 1444 var groupCounter = 0; 1445 var $select = this.$select.empty(); 1446 1447 $.each(dataprovider, function (index, option) { 1448 var $tag; 1449 1450 if ($.isArray(option.children)) { // create optiongroup tag 1451 groupCounter++; 1452 1453 $tag = $('<optgroup/>').attr({ 1454 label: option.label || 'Group ' + groupCounter, 1455 disabled: !!option.disabled 1456 }); 1457
vendor: 5,379 bytes, lines 1458-1606
1458 forEach(option.children, function(subOption) { // add children option tags 1459 var attributes = { 1460 value: subOption.value, 1461 label: subOption.label || subOption.value, 1462 title: subOption.title, 1463 selected: !!subOption.selected, 1464 disabled: !!subOption.disabled 1465 }; 1466 1467 //Loop through attributes object and add key-value for each attribute 1468 for (var key in subOption.attributes) { 1469 attributes['data-' + key] = subOption.attributes[key]; 1470 } 1471 //Append original attributes + new data attributes to option 1472 $tag.append($('<option/>').attr(attributes)); 1473 }); 1474 } 1475 else { 1476 1477 var attributes = { 1478 'value': option.value, 1479 'label': option.label || option.value, 1480 'title': option.title, 1481 'class': option.class, 1482 'selected': !!option.selected, 1483 'disabled': !!option.disabled 1484 }; 1485 //Loop through attributes object and add key-value for each attribute 1486 for (var key in option.attributes) { 1487 attributes['data-' + key] = option.attributes[key]; 1488 } 1489 //Append original attributes + new data attributes to option 1490 $tag = $('<option/>').attr(attributes); 1491 1492 $tag.text(option.label || option.value); 1493 } 1494 1495 $select.append($tag); 1496 }); 1497 1498 this.rebuild(); 1499 }, 1500 1501 /** 1502 * Enable the multiselect. 1503 */ 1504 enable: function() { 1505 this.$select.prop('disabled', false); 1506 this.$button.prop('disabled', false) 1507 .removeClass('disabled'); 1508 }, 1509 1510 /** 1511 * Disable the multiselect. 1512 */ 1513 disable: function() { 1514 this.$select.prop('disabled', true); 1515 this.$button.prop('disabled', true) 1516 .addClass('disabled'); 1517 }, 1518 1519 /** 1520 * Set the options. 1521 * 1522 * @param {Array} options 1523 */ 1524 setOptions: function(options) { 1525 this.options = this.mergeOptions(options); 1526 }, 1527 1528 /** 1529 * Merges the given options with the default options. 1530 * 1531 * @param {Array} options 1532 * @returns {Array} 1533 */ 1534 mergeOptions: function(options) { 1535 return $.extend(true, {}, this.defaults, this.options, options); 1536 }, 1537 1538 /** 1539 * Checks whether a select all checkbox is present. 1540 * 1541 * @returns {Boolean} 1542 */ 1543 hasSelectAll: function() { 1544 return $('li.multiselect-all', this.$ul).length > 0; 1545 }, 1546 1547 /** 1548 * Update opt groups. 1549 */ 1550 updateOptGroups: function() { 1551 var $groups = $('li.multiselect-group', this.$ul) 1552 var selectedClass = this.options.selectedClass; 1553 1554 $groups.each(function() { 1555 var $options = $(this).nextUntil('li.multiselect-group') 1556 .not('.multiselect-filter-hidden') 1557 .not('.disabled'); 1558 1559 var checked = true; 1560 $options.each(function() { 1561 var $input = $('input', this); 1562 1563 if (!$input.prop('checked')) { 1564 checked = false; 1565 } 1566 }); 1567 1568 if (selectedClass) { 1569 if (checked) { 1570 $(this).addClass(selectedClass); 1571 } 1572 else { 1573 $(this).removeClass(selectedClass); 1574 } 1575 } 1576 1577 $('input', this).prop('checked', checked); 1578 }); 1579 }, 1580 1581 /** 1582 * Updates the select all checkbox based on the currently displayed and selected checkboxes. 1583 */ 1584 updateSelectAll: function(notTriggerOnSelectAll) { 1585 if (this.hasSelectAll()) { 1586 var allBoxes = $("li:not(.multiselect-item):not(.multiselect-filter-hidden):not(.multiselect-group):not(.disabled) input:enabled", this.$ul); 1587 var allBoxesLength = allBoxes.length; 1588 var checkedBoxesLength = allBoxes.filter(":checked").length; 1589 var selectAllLi = $("li.multiselect-all", this.$ul); 1590 var selectAllInput = selectAllLi.find("input"); 1591 1592 if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) { 1593 selectAllInput.prop("checked", true); 1594 selectAllLi.addClass(this.options.selectedClass); 1595 } 1596 else { 1597 selectAllInput.prop("checked", false); 1598 selectAllLi.removeClass(this.options.selectedClass); 1599 } 1600 } 1601 }, 1602 1603 /** 1604 * Update the button text and its title based on the currently selected options. 1605 */ 1606 updateButtonText: function() {
1607 var options = this.getSelected(); 1608 1609 // First update the displayed button text. 1610 if (this.options.enableHTML) { 1611 $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select)); 1612 } 1613 else { 1614 $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select)); 1615 } 1616 1617 // Now update the title attribute of the button. 1618 $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select)); 1619 }, 1620 1621 /** 1622 * Get all selected options. 1623 * 1624 * @returns {jQUery} 1625 */ 1626 getSelected: function() { 1627 return $('option', this.$select).filter(":selected"); 1628 }, 1629 1630 /** 1631 * Gets a select option by its value. 1632 * 1633 * @param {String} value 1634 * @returns {jQuery} 1635 */ 1636 getOptionByValue: function (value) { 1637 1638 var options = $('option', this.$select); 1639 var valueToCompare = value.toString(); 1640 1641 for (var i = 0; i < options.length; i = i + 1) { 1642 var option = options[i]; 1643 if (option.value === valueToCompare) { 1644 return $(option); 1645 } 1646 } 1647 }, 1648 1649 /** 1650 * Get the input (radio/checkbox) by its value. 1651 * 1652 * @param {String} value 1653 * @returns {jQuery} 1654 */ 1655 getInputByValue: function (value) { 1656 1657 var checkboxes = $('li input:not(.multiselect-search)', this.$ul); 1658 var valueToCompare = value.toString(); 1659 1660 for (var i = 0; i < checkboxes.length; i = i + 1) { 1661 var checkbox = checkboxes[i]; 1662 if (checkbox.value === valueToCompare) { 1663 return $(checkbox); 1664 } 1665 } 1666 }, 1667 1668 /** 1669 * Used for knockout integration. 1670 */ 1671 updateOriginalOptions: function() { 1672 this.originalOptions = this.$select.clone()[0].options; 1673 }, 1674 1675 asyncFunction: function(callback, timeout, self) { 1676 var args = Array.prototype.slice.call(arguments, 3); 1677 return setTimeout(function() { 1678 callback.apply(self || window, args); 1679 }, timeout); 1680 }, 1681 1682 setAllSelectedText: function(allSelectedText) { 1683 this.options.allSelectedText = allSelectedText; 1684 this.updateButtonText(); 1685 } 1686 }; 1687 1688 $.fn.multiselect = function(option, parameter, extraOptions) { 1689 return this.each(function() { 1690 var data = $(this).data('multiselect'); 1691 var options = typeof option === 'object' && option; 1692 1693 // Initialize the multiselect. 1694 if (!data) { 1695 data = new Multiselect(this, options); 1696 $(this).data('multiselect', data); 1697 } 1698 1699 // Call multiselect method. 1700 if (typeof option === 'string') { 1701 data[option](parameter, extraOptions); 1702 1703 if (option === 'destroy') { 1704 $(this).data('multiselect', false); 1705 } 1706 } 1707 }); 1708 }; 1709 1710 $.fn.multiselect.Constructor = Multiselect; 1711 1712 $(function() { 1713 $("select[data-role=multiselect]").multiselect(); 1714 }); 1715 1716}(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.