vendor: 9,906 bytes, lines 1-223
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.onDropdownShow = $.proxy(this.options.onDropdownShow, this); 196 this.options.onDropdownHide = $.proxy(this.options.onDropdownHide, this); 197 this.options.onDropdownShown = $.proxy(this.options.onDropdownShown, this); 198 this.options.onDropdownHidden = $.proxy(this.options.onDropdownHidden, this); 199 this.options.onInitialized = $.proxy(this.options.onInitialized, this); 200 201 // Build select all if enabled. 202 this.buildContainer(); 203 this.buildButton(); 204 this.buildDropdown(); 205 this.buildSelectAll(); 206 this.buildDropdownOptions(); 207 this.buildFilter(); 208 209 this.updateButtonText(); 210 this.updateSelectAll(true); 211 212 if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { 213 this.disable(); 214 } 215 216 this.$select.hide().after(this.$container); 217 this.options.onInitialized(this.$select, this.$container); 218 } 219 220 Multiselect.prototype = { 221 222 defaults: { 223 /**
224 * Default text function will either print 'None selected' in case no 225 * option is selected or a list of the selected options up to a length 226 * of 3 selected options. 227 * 228 * @param {jQuery} options 229 * @param {jQuery} select 230 * @returns {String} 231 */ 232 buttonText: function(options, select) { 233 if (this.disabledText.length > 0 234 && (this.disableIfEmpty || select.prop('disabled')) 235 && options.length == 0) { 236 237 return this.disabledText; 238 } 239 else if (options.length === 0) { 240 return this.nonSelectedText; 241 } 242 else if (this.allSelectedText 243 && options.length === $('option', $(select)).length 244 && $('option', $(select)).length !== 1 245 && this.multiple) { 246 247 if (this.selectAllNumber) { 248 return this.allSelectedText + ' (' + options.length + ')'; 249 } 250 else { 251 return this.allSelectedText; 252 } 253 } 254 else if (options.length > this.numberDisplayed) { 255 return options.length + ' ' + this.nSelectedText; 256 } 257 else { 258 var selected = ''; 259 var delimiter = this.delimiterText; 260 261 options.each(function() { 262 var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); 263 selected += label + delimiter; 264 }); 265 266 return selected.substr(0, selected.length - 2); 267 } 268 }, 269 /** 270 * Updates the title of the button similar to the buttonText function. 271 * 272 * @param {jQuery} options 273 * @param {jQuery} select 274 * @returns {@exp;selected@call;substr} 275 */ 276 buttonTitle: function(options, select) { 277 if (options.length === 0) { 278 return this.nonSelectedText; 279 } 280 else { 281 var selected = ''; 282 var delimiter = this.delimiterText; 283 284 options.each(function () { 285 var label = ($(this).attr('label') !== undefined) ? $(this).attr('label') : $(this).text(); 286 selected += label + delimiter; 287 }); 288 return selected.substr(0, selected.length - 2); 289 } 290 }, 291 /** 292 * Create a label. 293 * 294 * @param {jQuery} element 295 * @returns {String} 296 */ 297 optionLabel: function(element){ 298 return $(element).attr('label') || $(element).text(); 299 }, 300 /** 301 * Create a class. 302 * 303 * @param {jQuery} element 304 * @returns {String} 305 */ 306 optionClass: function(element) { 307 return $(element).attr('class') || ''; 308 }, 309 /** 310 * Triggered on change of the multiselect. 311 * 312 * Not triggered when selecting/deselecting options manually. 313 * 314 * @param {jQuery} option 315 * @param {Boolean} checked 316 */ 317 onChange : function(option, checked) { 318 319 }, 320 /** 321 * Triggered when the dropdown is shown. 322 * 323 * @param {jQuery} event 324 */ 325 onDropdownShow: function(event) { 326 327 }, 328 /** 329 * Triggered when the dropdown is hidden. 330 * 331 * @param {jQuery} event 332 */ 333 onDropdownHide: function(event) { 334 335 }, 336 /** 337 * Triggered after the dropdown is shown. 338 * 339 * @param {jQuery} event 340 */ 341 onDropdownShown: function(event) { 342 343 }, 344 /** 345 * Triggered after the dropdown is hidden. 346 * 347 * @param {jQuery} event 348 */ 349 onDropdownHidden: function(event) { 350 351 }, 352 /** 353 * Triggered on select all. 354 */ 355 onSelectAll: function(checked) { 356 357 }, 358 /** 359 * Triggered after initializing. 360 * 361 * @param {jQuery} $select 362 * @param {jQuery} $container 363 */ 364 onInitialized: function($select, $container) { 365 366 },
367 enableHTML: false, 368 buttonClass: 'btn btn-default', 369 inheritClass: false, 370 buttonWidth: 'auto', 371 buttonContainer: '<div class="btn-group" />', 372 dropRight: false, 373 dropUp: false, 374 selectedClass: 'active', 375 // Maximum height of the dropdown menu. 376 // If maximum height is exceeded a scrollbar will be displayed. 377 maxHeight: false, 378 checkboxName: false, 379 includeSelectAllOption: false, 380 includeSelectAllIfMoreThan: 0, 381 selectAllText: ' Select all', 382 selectAllValue: 'multiselect-all', 383 selectAllName: false, 384 selectAllNumber: true, 385 selectAllJustVisible: true, 386 enableFiltering: false, 387 enableCaseInsensitiveFiltering: false, 388 enableFullValueFiltering: false, 389 enableClickableOptGroups: false, 390 enableCollapsibelOptGroups: false, 391 filterPlaceholder: 'Search', 392 // possible options: 'text', 'value', 'both' 393 filterBehavior: 'text', 394 includeFilterClearBtn: true, 395 preventInputChangeEvent: false, 396 nonSelectedText: 'None selected', 397 nSelectedText: 'selected', 398 allSelectedText: 'All selected', 399 numberDisplayed: 3, 400 disableIfEmpty: false, 401 disabledText: '', 402 delimiterText: ', ', 403 templates: { 404 button: '<button type="button" class="multiselect dropdown-toggle" data-toggle="dropdown"><span class="multiselect-selected-text"></span> <b class="caret"></b></button>', 405 ul: '<ul class="multiselect-container dropdown-menu"></ul>', 406 filter: '<li class="multiselect-item 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>', 407 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>', 408 li: '<li><a tabindex="0"><label></label></a></li>', 409 divider: '<li class="multiselect-item divider"></li>', 410 liGroup: '<li class="multiselect-item multiselect-group"><label></label></li>' 411 } 412 }, 413 414 constructor: Multiselect, 415 416 /** 417 * Builds the container of the multiselect. 418 */ 419 buildContainer: function() { 420 this.$container = $(this.options.buttonContainer); 421 this.$container.on('show.bs.dropdown', this.options.onDropdownShow); 422 this.$container.on('hide.bs.dropdown', this.options.onDropdownHide); 423 this.$container.on('shown.bs.dropdown', this.options.onDropdownShown); 424 this.$container.on('hidden.bs.dropdown', this.options.onDropdownHidden); 425 }, 426 427 /** 428 * Builds the button of the multiselect. 429 */ 430 buildButton: function() { 431 this.$button = $(this.options.templates.button).addClass(this.options.buttonClass); 432 if (this.$select.attr('class') && this.options.inheritClass) { 433 this.$button.addClass(this.$select.attr('class')); 434 } 435 // Adopt active state. 436 if (this.$select.prop('disabled')) { 437 this.disable(); 438 } 439 else { 440 this.enable(); 441 } 442 443 // Manually add button width if set. 444 if (this.options.buttonWidth && this.options.buttonWidth !== 'auto') { 445 this.$button.css({ 446 'width' : this.options.buttonWidth, 447 'overflow' : 'hidden', 448 'text-overflow' : 'ellipsis' 449 }); 450 this.$container.css({ 451 'width': this.options.buttonWidth 452 }); 453 } 454 455 // Keep the tab index from the select. 456 var tabindex = this.$select.attr('tabindex'); 457 if (tabindex) { 458 this.$button.attr('tabindex', tabindex); 459 } 460 461 this.$container.prepend(this.$button); 462 }, 463 464 /** 465 * Builds the ul representing the dropdown menu. 466 */ 467 buildDropdown: function() { 468 469 // Build ul. 470 this.$ul = $(this.options.templates.ul); 471 472 if (this.options.dropRight) { 473 this.$ul.addClass('pull-right'); 474 } 475 476 // Set max height of dropdown menu to activate auto scrollbar. 477 if (this.options.maxHeight) { 478 // TODO: Add a class for this option to move the css declarations. 479 this.$ul.css({ 480 'max-height': this.options.maxHeight + 'px', 481 'overflow-y': 'auto', 482 'overflow-x': 'hidden' 483 }); 484 } 485 486 if (this.options.dropUp) { 487 488 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)); 489 var moveCalc = height + 34; 490 491 this.$ul.css({ 492 'max-height': height + 'px', 493 'overflow-y': 'auto', 494 'overflow-x': 'hidden', 495 'margin-top': "-" + moveCalc + 'px' 496 }); 497 } 498 499 this.$container.append(this.$ul); 500 }, 501 502 /** 503 * Build the dropdown options and binds all nessecary events. 504 * 505 * Uses createDivider and createOptionValue to create the necessary options. 506 */ 507 buildDropdownOptions: function() { 508 509 this.$select.children().each($.proxy(function(index, element) { 510 511 var $element = $(element);
512 // Support optgroups and options without a group simultaneously. 513 var tag = $element.prop('tagName') 514 .toLowerCase(); 515 516 if ($element.prop('value') === this.options.selectAllValue) { 517 return; 518 } 519 520 if (tag === 'optgroup') { 521 this.createOptgroup(element); 522 } 523 else if (tag === 'option') { 524 525 if ($element.data('role') === 'divider') { 526 this.createDivider(); 527 } 528 else { 529 this.createOptionValue(element); 530 } 531 532 } 533 534 // Other illegal tags will be ignored. 535 }, this)); 536 537 // Bind the change event on the dropdown elements. 538 $('li input', this.$ul).on('change', $.proxy(function(event) { 539 var $target = $(event.target); 540 541 var checked = $target.prop('checked') || false; 542 var isSelectAllOption = $target.val() === this.options.selectAllValue; 543 544 // Apply or unapply the configured selected class. 545 if (this.options.selectedClass) { 546 if (checked) { 547 $target.closest('li') 548 .addClass(this.options.selectedClass); 549 } 550 else { 551 $target.closest('li') 552 .removeClass(this.options.selectedClass); 553 } 554 } 555 556 // Get the corresponding option. 557 var value = $target.val(); 558 var $option = this.getOptionByValue(value); 559 560 var $optionsNotThis = $('option', this.$select).not($option); 561 var $checkboxesNotThis = $('input', this.$container).not($target); 562 563 if (isSelectAllOption) { 564 if (checked) { 565 this.selectAll(this.options.selectAllJustVisible); 566 } 567 else { 568 this.deselectAll(this.options.selectAllJustVisible); 569 } 570 } 571 else { 572 if (checked) { 573 $option.prop('selected', true); 574 575 if (this.options.multiple) { 576 // Simply select additional option. 577 $option.prop('selected', true); 578 } 579 else { 580 // Unselect all other options and corresponding checkboxes. 581 if (this.options.selectedClass) { 582 $($checkboxesNotThis).closest('li').removeClass(this.options.selectedClass); 583 } 584 585 $($checkboxesNotThis).prop('checked', false); 586 $optionsNotThis.prop('selected', false); 587 588 // It's a single selection, so close. 589 this.$button.click(); 590 } 591 592 if (this.options.selectedClass === "active") { 593 $optionsNotThis.closest("a").css("outline", ""); 594 } 595 } 596 else { 597 // Unselect option. 598 $option.prop('selected', false); 599 } 600 601 // To prevent select all from firing onChange: #575 602 this.options.onChange($option, checked); 603 } 604 605 this.$select.change(); 606 607 this.updateButtonText(); 608 this.updateSelectAll(); 609 610 if(this.options.preventInputChangeEvent) { 611 return false; 612 } 613 }, this)); 614 615 $('li a', this.$ul).on('mousedown', function(e) { 616 if (e.shiftKey) { 617 // Prevent selecting text by Shift+click 618 return false; 619 } 620 }); 621 622 $('li a', this.$ul).on('touchstart click', $.proxy(function(event) { 623 event.stopPropagation(); 624 625 var $target = $(event.target); 626 627 if (event.shiftKey && this.options.multiple) { 628 if($target.is("label")){ // Handles checkbox selection manually (see https://github.com/davidstutz/bootstrap-multiselect/issues/431) 629 event.preventDefault(); 630 $target = $target.find("input"); 631 $target.prop("checked", !$target.prop("checked")); 632 } 633 var checked = $target.prop('checked') || false; 634 635 if (this.lastToggledInput !== null && this.lastToggledInput !== $target) { // Make sure we actually have a range 636 var from = $target.closest("li").index(); 637 var to = this.lastToggledInput.closest("li").index(); 638 639 if (from > to) { // Swap the indices 640 var tmp = to; 641 to = from; 642 from = tmp; 643 } 644 645 // Make sure we grab all elements since slice excludes the last index 646 ++to; 647 648 // Change the checkboxes and underlying options 649 var range = this.$ul.find("li").slice(from, to).find("input"); 650 651 range.prop('checked', checked); 652 653 if (this.options.selectedClass) { 654 range.closest('li') 655 .toggleClass(this.options.selectedClass, checked); 656 } 657 658 for (var i = 0, j = range.length; i < j; i++) { 659 var $checkbox = $(range[i]); 660 661 var $option = this.getOptionByValue($checkbox.val()); 662 663 $option.prop('selected', checked); 664 } 665 } 666 667 // Trigger the select "change" event 668 $target.trigger("change"); 669 } 670 671 // Remembers last clicked option 672 if($target.is("input") && !$target.closest("li").is(".multiselect-item")){ 673 this.lastToggledInput = $target; 674 } 675 676 $target.blur(); 677 }, this)); 678 679 // Keyboard support. 680 this.$container.off('keydown.multiselect').on('keydown.multiselect', $.proxy(function(event) { 681 if ($('input[type="text"]', this.$container).is(':focus')) { 682 return; 683 } 684 685 if (event.keyCode === 9 && this.$container.hasClass('open')) { 686 this.$button.click(); 687 } 688 else { 689 var $items = $(this.$container).find("li:not(.divider):not(.disabled) a").filter(":visible"); 690 691 if (!$items.length) { 692 return; 693 } 694 695 var index = $items.index($items.filter(':focus')); 696 697 // Navigation up. 698 if (event.keyCode === 38 && index > 0) { 699 index--; 700 } 701 // Navigate down. 702 else if (event.keyCode === 40 && index < $items.length - 1) { 703 index++; 704 } 705 else if (!~index) { 706 index = 0; 707 } 708 709 var $current = $items.eq(index); 710 $current.focus(); 711 712 if (event.keyCode === 32 || event.keyCode === 13) { 713 var $checkbox = $current.find('input'); 714 715 $checkbox.prop("checked", !$checkbox.prop("checked")); 716 $checkbox.change(); 717 } 718 719 event.stopPropagation(); 720 event.preventDefault(); 721 } 722 }, this)); 723 724 if(this.options.enableClickableOptGroups && this.options.multiple) { 725 $('li.multiselect-group', this.$ul).on('click', $.proxy(function(event) { 726 event.stopPropagation(); 727 console.log('test'); 728 var group = $(event.target).parent(); 729 730 // Search all option in optgroup 731 var $options = group.nextUntil('li.multiselect-group'); 732 var $visibleOptions = $options.filter(":visible:not(.disabled)"); 733 734 // check or uncheck items 735 var allChecked = true; 736 var optionInputs = $visibleOptions.find('input'); 737 var values = []; 738 739 optionInputs.each(function() { 740 allChecked = allChecked && $(this).prop('checked'); 741 values.push($(this).val()); 742 }); 743 744 if (!allChecked) { 745 this.select(values, false); 746 } 747 else { 748 this.deselect(values, false); 749 } 750 751 this.options.onChange(optionInputs, !allChecked); 752 }, this)); 753 } 754 755 if (this.options.enableCollapsibleOptGroups && this.options.multiple) { 756 $("li.multiselect-group input", this.$ul).off(); 757 $("li.multiselect-group", this.$ul).siblings().not("li.multiselect-group, li.multiselect-all", this.$ul).each( function () { 758 $(this).toggleClass('hidden', true); 759 }); 760 761 $("li.multiselect-group", this.$ul).on("click", $.proxy(function(group) { 762 group.stopPropagation(); 763 }, this)); 764 765 $("li.multiselect-group > a > b", this.$ul).on("click", $.proxy(function(t) { 766 t.stopPropagation(); 767 var n = $(t.target).closest('li'); 768 var r = n.nextUntil("li.multiselect-group"); 769 var i = true; 770 771 r.each(function() { 772 i = i && $(this).hasClass('hidden'); 773 }); 774 775 r.toggleClass('hidden', !i); 776 }, this)); 777 778 $("li.multiselect-group > a > input", this.$ul).on("change", $.proxy(function(t) { 779 t.stopPropagation(); 780 var n = $(t.target).closest('li'); 781 var r = n.nextUntil("li.multiselect-group", ':not(.disabled)'); 782 var s = r.find("input"); 783 784 var i = true; 785 s.each(function() { 786 i = i && $(this).prop("checked"); 787 }); 788 789 s.prop("checked", !i).trigger("change"); 790 }, this)); 791 792 // Set the initial selection state of the groups. 793 $('li.multiselect-group', this.$ul).each(function() { 794 var r = $(this).nextUntil("li.multiselect-group", ':not(.disabled)'); 795 var s = r.find("input"); 796 797 var i = true; 798 s.each(function() { 799 i = i && $(this).prop("checked"); 800 }); 801 802 $(this).find('input').prop("checked", i); 803 }); 804 805 // Update the group checkbox based on new selections among the 806 // corresponding children. 807 $("li input", this.$ul).on("change", $.proxy(function(t) { 808 t.stopPropagation(); 809 var n = $(t.target).closest('li'); 810 var r1 = n.prevUntil("li.multiselect-group", ':not(.disabled)'); 811 var r2 = n.nextUntil("li.multiselect-group", ':not(.disabled)'); 812 var s1 = r1.find("input"); 813 var s2 = r2.find("input"); 814 815 var i = $(t.target).prop('checked'); 816 s1.each(function() { 817 i = i && $(this).prop("checked"); 818 }); 819 820 s2.each(function() { 821 i = i && $(this).prop("checked"); 822 }); 823 824 n.prevAll('.multiselect-group').find('input').prop('checked', i); 825 }, this)); 826 827 $("li.multiselect-all", this.$ul).css('background', '#f3f3f3').css('border-bottom', '1px solid #eaeaea'); 828 $("li.multiselect-group > a, li.multiselect-all > a > label.checkbox", this.$ul).css('padding', '3px 20px 3px 35px'); 829 $("li.multiselect-group > a > input", this.$ul).css('margin', '4px 0px 5px -20px'); 830 } 831 }, 832 833 /** 834 * Create an option using the given select option. 835 * 836 * @param {jQuery} element 837 */ 838 createOptionValue: function(element) { 839 var $element = $(element); 840 if ($element.is(':selected')) { 841 $element.prop('selected', true); 842 } 843
844 // Support the label attribute on options. 845 var label = this.options.optionLabel(element); 846 var classes = this.options.optionClass(element); 847 var value = $element.val(); 848 var inputType = this.options.multiple ? "checkbox" : "radio"; 849 850 var $li = $(this.options.templates.li); 851 var $label = $('label', $li); 852 $label.addClass(inputType); 853 $li.addClass(classes); 854 855 if (this.options.enableHTML) { 856 $label.html(" " + label); 857 } 858 else { 859 $label.text(" " + label); 860 } 861 862 var $checkbox = $('<input/>').attr('type', inputType); 863 864 if (this.options.checkboxName) { 865 $checkbox.attr('name', this.options.checkboxName); 866 } 867 $label.prepend($checkbox); 868 869 var selected = $element.prop('selected') || false; 870 $checkbox.val(value); 871 872 if (value === this.options.selectAllValue) { 873 $li.addClass("multiselect-item multiselect-all"); 874 $checkbox.parent().parent() 875 .addClass('multiselect-all'); 876 } 877 878 $label.attr('title', $element.attr('title')); 879 880 this.$ul.append($li); 881 882 if ($element.is(':disabled')) { 883 $checkbox.attr('disabled', 'disabled') 884 .prop('disabled', true) 885 .closest('a') 886 .attr("tabindex", "-1") 887 .closest('li') 888 .addClass('disabled'); 889 } 890 891 $checkbox.prop('checked', selected); 892 893 if (selected && this.options.selectedClass) { 894 $checkbox.closest('li') 895 .addClass(this.options.selectedClass); 896 } 897 }, 898 899 /** 900 * Creates a divider using the given select option. 901 * 902 * @param {jQuery} element 903 */ 904 createDivider: function(element) { 905 var $divider = $(this.options.templates.divider); 906 this.$ul.append($divider); 907 }, 908 909 /** 910 * Creates an optgroup. 911 * 912 * @param {jQuery} group 913 */ 914 createOptgroup: function(group) { 915 if (this.options.enableCollapsibleOptGroups && this.options.multiple) { 916 var label = $(group).attr("label"); 917 var value = $(group).attr("value"); 918 var r = $('<li class="multiselect-item multiselect-group"><a href="javascript:void(0);"><input type="checkbox" value="' + value + '"/><b> ' + label + '<b class="caret"></b></b></a></li>'); 919 920 if (this.options.enableClickableOptGroups) { 921 r.addClass("multiselect-group-clickable") 922 } 923 this.$ul.append(r); 924 if ($(group).is(":disabled")) { 925 r.addClass("disabled") 926 } 927 $("option", group).each($.proxy(function($, group) { 928 this.createOptionValue(group) 929 }, this)) 930 } 931 else { 932 var groupName = $(group).prop('label'); 933 934 // Add a header for the group. 935 var $li = $(this.options.templates.liGroup); 936 937 if (this.options.enableHTML) { 938 $('label', $li).html(groupName); 939 } 940 else { 941 $('label', $li).text(groupName); 942 } 943 944 if (this.options.enableClickableOptGroups) { 945 $li.addClass('multiselect-group-clickable'); 946 } 947 948 this.$ul.append($li); 949 950 if ($(group).is(':disabled')) { 951 $li.addClass('disabled'); 952 } 953 954 // Add the options of the group. 955 $('option', group).each($.proxy(function(index, element) { 956 this.createOptionValue(element); 957 }, this)); 958 } 959 }, 960 961 /** 962 * Build the select all. 963 * 964 * Checks if a select all has already been created. 965 */ 966 buildSelectAll: function() { 967 if (typeof this.options.selectAllValue === 'number') { 968 this.options.selectAllValue = this.options.selectAllValue.toString(); 969 } 970 971 var alreadyHasSelectAll = this.hasSelectAll(); 972 973 if (!alreadyHasSelectAll && this.options.includeSelectAllOption && this.options.multiple 974 && $('option', this.$select).length > this.options.includeSelectAllIfMoreThan) { 975 976 // Check whether to add a divider after the select all. 977 if (this.options.includeSelectAllDivider) { 978 this.$ul.prepend($(this.options.templates.divider)); 979 } 980 981 var $li = $(this.options.templates.li); 982 $('label', $li).addClass("checkbox"); 983 984 if (this.options.enableHTML) { 985 $('label', $li).html(" " + this.options.selectAllText); 986 } 987 else { 988 $('label', $li).text(" " + this.options.selectAllText); 989 } 990 991 if (this.options.selectAllName) { 992 $('label', $li).prepend('<input type="checkbox" name="' + this.options.selectAllName + '" />'); 993 } 994 else { 995 $('label', $li).prepend('<input type="checkbox" />'); 996 } 997 998 var $checkbox = $('input', $li); 999 $checkbox.val(this.options.selectAllValue); 1000 1001 $li.addClass("multiselect-item multiselect-all"); 1002 $checkbox.parent().parent() 1003 .addClass('multiselect-all'); 1004 1005 this.$ul.prepend($li); 1006 1007 $checkbox.prop('checked', false); 1008 } 1009 }, 1010 1011 /** 1012 * Builds the filter. 1013 */ 1014 buildFilter: function() { 1015 1016 // Build filter if filtering OR case insensitive filtering is enabled and the number of options exceeds (or equals) enableFilterLength. 1017 if (this.options.enableFiltering || this.options.enableCaseInsensitiveFiltering) { 1018 var enableFilterLength = Math.max(this.options.enableFiltering, this.options.enableCaseInsensitiveFiltering); 1019 1020 if (this.$select.find('option').length >= enableFilterLength) { 1021 1022 this.$filter = $(this.options.templates.filter); 1023 $('input', this.$filter).attr('placeholder', this.options.filterPlaceholder); 1024 1025 // Adds optional filter clear button 1026 if(this.options.includeFilterClearBtn){ 1027 var clearBtn = $(this.options.templates.filterClearBtn); 1028 clearBtn.on('click', $.proxy(function(event){ 1029 clearTimeout(this.searchTimeout); 1030 this.$filter.find('.multiselect-search').val(''); 1031 $('li', this.$ul).show().removeClass("filter-hidden"); 1032 this.updateSelectAll(); 1033 }, this)); 1034 this.$filter.find('.input-group').append(clearBtn); 1035 } 1036 1037 this.$ul.prepend(this.$filter); 1038 1039 this.$filter.val(this.query).on('click', function(event) { 1040 event.stopPropagation(); 1041 }).on('input keydown', $.proxy(function(event) { 1042 // Cancel enter key default behaviour 1043 if (event.which === 13) { 1044 event.preventDefault(); 1045 } 1046 1047 // This is useful to catch "keydown" events after the browser has updated the control. 1048 clearTimeout(this.searchTimeout); 1049 1050 this.searchTimeout = this.asyncFunction($.proxy(function() { 1051 1052 if (this.query !== event.target.value) { 1053 this.query = event.target.value; 1054 1055 var currentGroup, currentGroupVisible; 1056 $.each($('li', this.$ul), $.proxy(function(index, element) { 1057 var value = $('input', element).length > 0 ? $('input', element).val() : ""; 1058 var text = $('label', element).text(); 1059 1060 var filterCandidate = ''; 1061 if ((this.options.filterBehavior === 'text')) { 1062 filterCandidate = text; 1063 } 1064 else if ((this.options.filterBehavior === 'value')) { 1065 filterCandidate = value; 1066 } 1067 else if (this.options.filterBehavior === 'both') { 1068 filterCandidate = text + '\n' + value; 1069 } 1070 1071 if (value !== this.options.selectAllValue && text) { 1072 1073 // By default lets assume that element is not 1074 // interesting for this search. 1075 var showElement = false; 1076 1077 if (this.options.enableCaseInsensitiveFiltering) { 1078 filterCandidate = filterCandidate.toLowerCase(); 1079 this.query = this.query.toLowerCase(); 1080 } 1081 1082 if (this.options.enableFullValueFiltering && this.options.filterBehavior !== 'both') { 1083 var valueToMatch = filterCandidate.trim().substring(0, this.query.length); 1084 if (this.query.indexOf(valueToMatch) > -1) { 1085 showElement = true; 1086 } 1087 } 1088 else if (filterCandidate.indexOf(this.query) > -1) { 1089 showElement = true; 1090 } 1091 1092 // Toggle current element (group or group item) according to showElement boolean. 1093 $(element).toggle(showElement).toggleClass('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().removeClass('filter-hidden'); 1105 } 1106 1107 // Show all group items when group name satisfies filter. 1108 if (!showElement && currentGroupVisible) { 1109 $(element).show().removeClass('filter-hidden'); 1110 } 1111 } 1112 } 1113 }, this)); 1114 } 1115 1116 this.updateSelectAll(); 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 1182 /** 1183 * Select all options of the given values. 1184 * 1185 * If triggerOnChange is set to true, the on change event is triggered if 1186 * and only if one value is passed. 1187 * 1188 * @param {Array} selectValues 1189 * @param {Boolean} triggerOnChange 1190 */ 1191 select: function(selectValues, triggerOnChange) { 1192 if(!$.isArray(selectValues)) { 1193 selectValues = [selectValues]; 1194 } 1195 1196 for (var i = 0; i < selectValues.length; i++) { 1197 var value = selectValues[i]; 1198 1199 if (value === null || value === undefined) { 1200 continue; 1201 } 1202 1203 var $option = this.getOptionByValue(value); 1204 var $checkbox = this.getInputByValue(value); 1205 1206 if($option === undefined || $checkbox === undefined) { 1207 continue; 1208 } 1209 1210 if (!this.options.multiple) { 1211 this.deselectAll(false); 1212 } 1213 1214 if (this.options.selectedClass) { 1215 $checkbox.closest('li') 1216 .addClass(this.options.selectedClass); 1217 } 1218 1219 $checkbox.prop('checked', true); 1220 $option.prop('selected', true); 1221 1222 if (triggerOnChange) { 1223 this.options.onChange($option, true); 1224 } 1225 } 1226 1227 this.updateButtonText(); 1228 this.updateSelectAll(); 1229 }, 1230 1231 /** 1232 * Clears all selected items. 1233 */ 1234 clearSelection: function () { 1235 this.deselectAll(false); 1236 this.updateButtonText(); 1237 this.updateSelectAll(); 1238 }, 1239 1240 /** 1241 * Deselects all options of the given values. 1242 * 1243 * If triggerOnChange is set to true, the on change event is triggered, if 1244 * and only if one value is passed. 1245 * 1246 * @param {Array} deselectValues 1247 * @param {Boolean} triggerOnChange 1248 */ 1249 deselect: function(deselectValues, triggerOnChange) { 1250 if(!$.isArray(deselectValues)) { 1251 deselectValues = [deselectValues]; 1252 } 1253 1254 for (var i = 0; i < deselectValues.length; i++) { 1255 var value = deselectValues[i]; 1256 1257 if (value === null || value === undefined) { 1258 continue; 1259 } 1260 1261 var $option = this.getOptionByValue(value); 1262 var $checkbox = this.getInputByValue(value); 1263 1264 if($option === undefined || $checkbox === undefined) { 1265 continue; 1266 } 1267 1268 if (this.options.selectedClass) { 1269 $checkbox.closest('li') 1270 .removeClass(this.options.selectedClass); 1271 } 1272 1273 $checkbox.prop('checked', false); 1274 $option.prop('selected', false); 1275 1276 if (triggerOnChange) { 1277 this.options.onChange($option, false); 1278 } 1279 } 1280 1281 this.updateButtonText(); 1282 this.updateSelectAll(); 1283 }, 1284 1285 /** 1286 * Selects all enabled & visible options. 1287 * 1288 * If justVisible is true or not specified, only visible options are selected. 1289 * 1290 * @param {Boolean} justVisible 1291 * @param {Boolean} triggerOnSelectAll 1292 */ 1293 selectAll: function (justVisible, triggerOnSelectAll) { 1294 justVisible = (this.options.enableCollapsibleOptGroups && this.options.multiple) ? false : justVisible; 1295 1296 var justVisible = typeof justVisible === 'undefined' ? true : justVisible; 1297 var allCheckboxes = $("li input[type='checkbox']:enabled", this.$ul); 1298 var visibleCheckboxes = allCheckboxes.filter(":visible"); 1299 var allCheckboxesCount = allCheckboxes.length; 1300 var visibleCheckboxesCount = visibleCheckboxes.length; 1301 1302 if(justVisible) { 1303 visibleCheckboxes.prop('checked', true); 1304 $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").addClass(this.options.selectedClass); 1305 } 1306 else { 1307 allCheckboxes.prop('checked', true); 1308 $("li:not(.divider):not(.disabled)", this.$ul).addClass(this.options.selectedClass); 1309 } 1310 1311 if (allCheckboxesCount === visibleCheckboxesCount || justVisible === false) { 1312 $("option:not([data-role='divider']):enabled", this.$select).prop('selected', true); 1313 } 1314 else { 1315 var values = visibleCheckboxes.map(function() { 1316 return $(this).val(); 1317 }).get(); 1318 1319 $("option:enabled", this.$select).filter(function(index) { 1320 return $.inArray($(this).val(), values) !== -1; 1321 }).prop('selected', true); 1322 } 1323 1324 if (triggerOnSelectAll) { 1325 this.options.onSelectAll(); 1326 } 1327 }, 1328 1329 /** 1330 * Deselects all options. 1331 * 1332 * If justVisible is true or not specified, only visible options are deselected. 1333 * 1334 * @param {Boolean} justVisible 1335 */ 1336 deselectAll: function (justVisible) { 1337 justVisible = (this.options.enableCollapsibleOptGroups && this.options.multiple) ? false : justVisible; 1338 justVisible = typeof justVisible === 'undefined' ? true : justVisible; 1339 1340 if(justVisible) { 1341 var visibleCheckboxes = $("li input[type='checkbox']:not(:disabled)", this.$ul).filter(":visible"); 1342 visibleCheckboxes.prop('checked', false); 1343 1344 var values = visibleCheckboxes.map(function() { 1345 return $(this).val(); 1346 }).get(); 1347 1348 $("option:enabled", this.$select).filter(function(index) { 1349 return $.inArray($(this).val(), values) !== -1; 1350 }).prop('selected', false); 1351 1352 if (this.options.selectedClass) { 1353 $("li:not(.divider):not(.disabled)", this.$ul).filter(":visible").removeClass(this.options.selectedClass); 1354 } 1355 } 1356 else { 1357 $("li input[type='checkbox']:enabled", this.$ul).prop('checked', false); 1358 $("option:enabled", this.$select).prop('selected', false); 1359 1360 if (this.options.selectedClass) { 1361 $("li:not(.divider):not(.disabled)", this.$ul).removeClass(this.options.selectedClass); 1362 } 1363 } 1364 }, 1365 1366 /** 1367 * Rebuild the plugin. 1368 * 1369 * Rebuilds the dropdown, the filter and the select all option. 1370 */ 1371 rebuild: function() { 1372 this.$ul.html(''); 1373 1374 // Important to distinguish between radios and checkboxes. 1375 this.options.multiple = this.$select.attr('multiple') === "multiple"; 1376 1377 this.buildSelectAll(); 1378 this.buildDropdownOptions(); 1379 this.buildFilter(); 1380 1381 this.updateButtonText(); 1382 this.updateSelectAll(true); 1383 1384 if (this.options.disableIfEmpty && $('option', this.$select).length <= 0) { 1385 this.disable(); 1386 } 1387 else { 1388 this.enable(); 1389 } 1390 1391 if (this.options.dropRight) { 1392 this.$ul.addClass('pull-right'); 1393 } 1394 }, 1395 1396 /** 1397 * The provided data will be used to build the dropdown. 1398 */ 1399 dataprovider: function(dataprovider) { 1400 1401 var groupCounter = 0; 1402 var $select = this.$select.empty(); 1403 1404 $.each(dataprovider, function (index, option) { 1405 var $tag; 1406 1407 if ($.isArray(option.children)) { // create optiongroup tag 1408 groupCounter++; 1409 1410 $tag = $('<optgroup/>').attr({ 1411 label: option.label || 'Group ' + groupCounter, 1412 disabled: !!option.disabled 1413 }); 1414
1415 forEach(option.children, function(subOption) { // add children option tags 1416 $tag.append($('<option/>').attr({ 1417 value: subOption.value, 1418 label: subOption.label || subOption.value, 1419 title: subOption.title, 1420 selected: !!subOption.selected, 1421 disabled: !!subOption.disabled 1422 })); 1423 }); 1424 } 1425 else { 1426 $tag = $('<option/>').attr({ 1427 value: option.value, 1428 label: option.label || option.value, 1429 title: option.title, 1430 class: option.class, 1431 selected: !!option.selected, 1432 disabled: !!option.disabled 1433 }); 1434 $tag.text(option.label || option.value); 1435 } 1436 1437 $select.append($tag); 1438 }); 1439 1440 this.rebuild(); 1441 }, 1442 1443 /** 1444 * Enable the multiselect. 1445 */ 1446 enable: function() { 1447 this.$select.prop('disabled', false); 1448 this.$button.prop('disabled', false) 1449 .removeClass('disabled'); 1450 }, 1451 1452 /** 1453 * Disable the multiselect. 1454 */ 1455 disable: function() { 1456 this.$select.prop('disabled', true); 1457 this.$button.prop('disabled', true) 1458 .addClass('disabled'); 1459 }, 1460 1461 /** 1462 * Set the options. 1463 * 1464 * @param {Array} options 1465 */ 1466 setOptions: function(options) { 1467 this.options = this.mergeOptions(options); 1468 }, 1469 1470 /** 1471 * Merges the given options with the default options. 1472 * 1473 * @param {Array} options 1474 * @returns {Array} 1475 */ 1476 mergeOptions: function(options) { 1477 return $.extend(true, {}, this.defaults, this.options, options); 1478 }, 1479 1480 /** 1481 * Checks whether a select all checkbox is present. 1482 * 1483 * @returns {Boolean} 1484 */ 1485 hasSelectAll: function() { 1486 return $('li.multiselect-all', this.$ul).length > 0; 1487 }, 1488 1489 /** 1490 * Updates the select all checkbox based on the currently displayed and selected checkboxes. 1491 */ 1492 updateSelectAll: function(notTriggerOnSelectAll) { 1493 if (this.hasSelectAll()) { 1494 var allBoxes = $("li:not(.multiselect-item):not(.filter-hidden) input:enabled", this.$ul); 1495 var allBoxesLength = allBoxes.length; 1496 var checkedBoxesLength = allBoxes.filter(":checked").length; 1497 var selectAllLi = $("li.multiselect-all", this.$ul); 1498 var selectAllInput = selectAllLi.find("input"); 1499 1500 if (checkedBoxesLength > 0 && checkedBoxesLength === allBoxesLength) { 1501 selectAllInput.prop("checked", true); 1502 selectAllLi.addClass(this.options.selectedClass); 1503 this.options.onSelectAll(true); 1504 } 1505 else { 1506 selectAllInput.prop("checked", false); 1507 selectAllLi.removeClass(this.options.selectedClass); 1508 if (checkedBoxesLength === 0) { 1509 if (!notTriggerOnSelectAll) { 1510 this.options.onSelectAll(false); 1511 } 1512 } 1513 } 1514 } 1515 }, 1516 1517 /** 1518 * Update the button text and its title based on the currently selected options. 1519 */ 1520 updateButtonText: function() { 1521 var options = this.getSelected(); 1522 1523 // First update the displayed button text. 1524 if (this.options.enableHTML) { 1525 $('.multiselect .multiselect-selected-text', this.$container).html(this.options.buttonText(options, this.$select)); 1526 } 1527 else { 1528 $('.multiselect .multiselect-selected-text', this.$container).text(this.options.buttonText(options, this.$select)); 1529 } 1530 1531 // Now update the title attribute of the button. 1532 $('.multiselect', this.$container).attr('title', this.options.buttonTitle(options, this.$select)); 1533 }, 1534 1535 /** 1536 * Get all selected options. 1537 * 1538 * @returns {jQUery} 1539 */ 1540 getSelected: function() { 1541 return $('option', this.$select).filter(":selected"); 1542 }, 1543 1544 /** 1545 * Gets a select option by its value. 1546 * 1547 * @param {String} value 1548 * @returns {jQuery} 1549 */ 1550 getOptionByValue: function (value) { 1551 1552 var options = $('option', this.$select); 1553 var valueToCompare = value.toString(); 1554 1555 for (var i = 0; i < options.length; i = i + 1) { 1556 var option = options[i]; 1557 if (option.value === valueToCompare) { 1558 return $(option); 1559 } 1560 } 1561 }, 1562 1563 /** 1564 * Get the input (radio/checkbox) by its value. 1565 * 1566 * @param {String} value 1567 * @returns {jQuery} 1568 */ 1569 getInputByValue: function (value) { 1570 1571 var checkboxes = $('li input', this.$ul); 1572 var valueToCompare = value.toString(); 1573 1574 for (var i = 0; i < checkboxes.length; i = i + 1) { 1575 var checkbox = checkboxes[i];
1576 if (checkbox.value === valueToCompare) { 1577 return $(checkbox); 1578 } 1579 } 1580 }, 1581 1582 /** 1583 * Used for knockout integration. 1584 */ 1585 updateOriginalOptions: function() { 1586 this.originalOptions = this.$select.clone()[0].options; 1587 }, 1588 1589 asyncFunction: function(callback, timeout, self) { 1590 var args = Array.prototype.slice.call(arguments, 3); 1591 return setTimeout(function() { 1592 callback.apply(self || window, args); 1593 }, timeout); 1594 }, 1595 1596 setAllSelectedText: function(allSelectedText) { 1597 this.options.allSelectedText = allSelectedText; 1598 this.updateButtonText(); 1599 } 1600 }; 1601 1602 $.fn.multiselect = function(option, parameter, extraOptions) { 1603 return this.each(function() { 1604 var data = $(this).data('multiselect'); 1605 var options = typeof option === 'object' && option; 1606 1607 // Initialize the multiselect. 1608 if (!data) { 1609 data = new Multiselect(this, options); 1610 $(this).data('multiselect', data); 1611 } 1612 1613 // Call multiselect method. 1614 if (typeof option === 'string') { 1615 data[option](parameter, extraOptions); 1616 1617 if (option === 'destroy') { 1618 $(this).data('multiselect', false); 1619 } 1620 } 1621 }); 1622 }; 1623 1624 $.fn.multiselect.Constructor = Multiselect; 1625 1626 $(function() { 1627 $("select[data-role=multiselect]").multiselect(); 1628 }); 1629 1630}(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.