vendor: 5,059 bytes, lines 1-123
1/*! 2 * Bootstrap-select v1.12.2 (http://silviomoreto.github.io/bootstrap-select) 3 * 4 * Copyright 2013-2017 bootstrap-select 5 * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) 6 */ 7 8/* 9 10 11 Creative Tim Modifications 12 13 Line: 379-380 - we changed glyphicons with material-icons. 14 Line: 593 - we put tickIcon inside of span tag. 15 16 17*/ 18 19 20(function(root, factory) { 21 if (typeof define === 'function' && define.amd) { 22 // AMD. Register as an anonymous module unless amdModuleId is set 23 define(["jquery"], function(a0) { 24 return (factory(a0)); 25 }); 26 } else if (typeof module === 'object' && module.exports) { 27 // Node. Does not work with strict CommonJS, but 28 // only CommonJS-like environments that support module.exports, 29 // like Node. 30 module.exports = factory(require("jquery")); 31 } else { 32 factory(root["jQuery"]); 33 } 34}(this, function(jQuery) { 35 36 (function($) { 37 'use strict'; 38 39 //<editor-fold desc="Shims"> 40 if (!String.prototype.includes) { 41 (function() { 42 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` 43 var toString = {}.toString; 44 var defineProperty = (function() { 45 // IE 8 only supports `Object.defineProperty` on DOM elements 46 try { 47 var object = {}; 48 var $defineProperty = Object.defineProperty; 49 var result = $defineProperty(object, object, object) && $defineProperty; 50 } catch (error) {} 51 return result; 52 }()); 53 var indexOf = ''.indexOf; 54 var includes = function(search) { 55 if (this == null) { 56 throw new TypeError(); 57 } 58 var string = String(this); 59 if (search && toString.call(search) == '[object RegExp]') { 60 throw new TypeError(); 61 } 62 var stringLength = string.length; 63 var searchString = String(search); 64 var searchLength = searchString.length; 65 var position = arguments.length > 1 ? arguments[1] : undefined; 66 // `ToInteger` 67 var pos = position ? Number(position) : 0; 68 if (pos != pos) { // better `isNaN` 69 pos = 0; 70 } 71 var start = Math.min(Math.max(pos, 0), stringLength); 72 // Avoid the `indexOf` call if no match is possible 73 if (searchLength + start > stringLength) { 74 return false; 75 } 76 return indexOf.call(string, searchString, pos) != -1; 77 }; 78 if (defineProperty) { 79 defineProperty(String.prototype, 'includes', { 80 'value': includes, 81 'configurable': true, 82 'writable': true 83 }); 84 } else { 85 String.prototype.includes = includes; 86 } 87 }()); 88 } 89 90 if (!String.prototype.startsWith) { 91 (function() { 92 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` 93 var defineProperty = (function() { 94 // IE 8 only supports `Object.defineProperty` on DOM elements 95 try { 96 var object = {}; 97 var $defineProperty = Object.defineProperty; 98 var result = $defineProperty(object, object, object) && $defineProperty; 99 } catch (error) {} 100 return result; 101 }()); 102 var toString = {}.toString; 103 var startsWith = function(search) { 104 if (this == null) { 105 throw new TypeError(); 106 } 107 var string = String(this); 108 if (search && toString.call(search) == '[object RegExp]') { 109 throw new TypeError(); 110 } 111 var stringLength = string.length; 112 var searchString = String(search); 113 var searchLength = searchString.length; 114 var position = arguments.length > 1 ? arguments[1] : undefined; 115 // `ToInteger` 116 var pos = position ? Number(position) : 0; 117 if (pos != pos) { // better `isNaN` 118 pos = 0; 119 } 120 var start = Math.min(Math.max(pos, 0), stringLength); 121 // Avoid the `indexOf` call if no match is possible 122 if (searchLength + start > stringLength) { 123 return false;
124 } 125 var index = -1; 126 while (++index < searchLength) { 127 if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { 128 return false; 129 } 130 } 131 return true; 132 }; 133 if (defineProperty) { 134 defineProperty(String.prototype, 'startsWith', { 135 'value': startsWith, 136 'configurable': true, 137 'writable': true 138 }); 139 } else { 140 String.prototype.startsWith = startsWith; 141 } 142 }()); 143 } 144 145 if (!Object.keys) { 146 Object.keys = function( 147 o, // object 148 k, // key 149 r // result array 150 ) { 151 // initialize object and result 152 r = []; 153 // iterate over object keys 154 for (k in o) 155 // fill result array with non-prototypical keys 156 r.hasOwnProperty.call(o, k) && r.push(k); 157 // return result 158 return r; 159 }; 160 } 161 162 // set data-selected on select element if the value has been programmatically selected 163 // prior to initialization of bootstrap-select 164 // * consider removing or replacing an alternative method * 165 var valHooks = { 166 useDefault: false, 167 _set: $.valHooks.select.set 168 }; 169 170 $.valHooks.select.set = function(elem, value) { 171 if (value && !valHooks.useDefault) $(elem).data('selected', true); 172 173 return valHooks._set.apply(this, arguments); 174 }; 175 176 var changed_arguments = null; 177 $.fn.triggerNative = function(eventName) { 178 var el = this[0], 179 event; 180 181 if (el.dispatchEvent) { // for modern browsers & IE9+ 182 if (typeof Event === 'function') { 183 // For modern browsers 184 event = new Event(eventName, { 185 bubbles: true 186 }); 187 } else { 188 // For IE since it doesn't support Event constructor 189 event = document.createEvent('Event'); 190 event.initEvent(eventName, true, false); 191 } 192 193 el.dispatchEvent(event); 194 } else if (el.fireEvent) { // for IE8 195 event = document.createEventObject(); 196 event.eventType = eventName; 197 el.fireEvent('on' + eventName, event); 198 } else { 199 // fall back to jQuery.trigger 200 this.trigger(eventName); 201 } 202 }; 203 //</editor-fold> 204 205 // Case insensitive contains search 206 $.expr.pseudos.icontains = function(obj, index, meta) { 207 var $obj = $(obj); 208 var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase(); 209 return haystack.includes(meta[3].toUpperCase()); 210 }; 211 212 // Case insensitive begins search 213 $.expr.pseudos.ibegins = function(obj, index, meta) { 214 var $obj = $(obj); 215 var haystack = ($obj.data('tokens') || $obj.text()).toString().toUpperCase(); 216 return haystack.startsWith(meta[3].toUpperCase()); 217 }; 218 219 // Case and accent insensitive contains search 220 $.expr.pseudos.aicontains = function(obj, index, meta) { 221 var $obj = $(obj); 222 var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase(); 223 return haystack.includes(meta[3].toUpperCase()); 224 }; 225 226 // Case and accent insensitive begins search 227 $.expr.pseudos.aibegins = function(obj, index, meta) { 228 var $obj = $(obj); 229 var haystack = ($obj.data('tokens') || $obj.data('normalizedText') || $obj.text()).toString().toUpperCase(); 230 return haystack.startsWith(meta[3].toUpperCase()); 231 }; 232 233 /** 234 * Remove all diatrics from the given text. 235 * @access private 236 * @param {String} text 237 * @returns {String} 238 */ 239 function normalizeToBase(text) { 240 var rExps = [{ 241 re: /[\xC0-\xC6]/g, 242 ch: "A" 243 }, 244 { 245 re: /[\xE0-\xE6]/g, 246 ch: "a" 247 }, 248 { 249 re: /[\xC8-\xCB]/g, 250 ch: "E" 251 }, 252 { 253 re: /[\xE8-\xEB]/g, 254 ch: "e" 255 }, 256 { 257 re: /[\xCC-\xCF]/g, 258 ch: "I" 259 }, 260 { 261 re: /[\xEC-\xEF]/g, 262 ch: "i" 263 }, 264 { 265 re: /[\xD2-\xD6]/g, 266 ch: "O" 267 }, 268 {
vendor: 4,559 bytes, lines 269-394
269 re: /[\xF2-\xF6]/g, 270 ch: "o" 271 }, 272 { 273 re: /[\xD9-\xDC]/g, 274 ch: "U" 275 }, 276 { 277 re: /[\xF9-\xFC]/g, 278 ch: "u" 279 }, 280 { 281 re: /[\xC7-\xE7]/g, 282 ch: "c" 283 }, 284 { 285 re: /[\xD1]/g, 286 ch: "N" 287 }, 288 { 289 re: /[\xF1]/g, 290 ch: "n" 291 } 292 ]; 293 $.each(rExps, function() { 294 text = text ? text.replace(this.re, this.ch) : ''; 295 }); 296 return text; 297 } 298 299 300 // List of HTML entities for escaping. 301 var escapeMap = { 302 '&': '&', 303 '<': '<', 304 '>': '>', 305 '"': '"', 306 "'": ''', 307 '`': '`' 308 }; 309 310 var unescapeMap = { 311 '&': '&', 312 '<': '<', 313 '>': '>', 314 '"': '"', 315 ''': "'", 316 '`': '`' 317 }; 318 319 // Functions for escaping and unescaping strings to/from HTML interpolation. 320 var createEscaper = function(map) { 321 var escaper = function(match) { 322 return map[match]; 323 }; 324 // Regexes for identifying a key that needs to be escaped. 325 var source = '(?:' + Object.keys(map).join('|') + ')'; 326 var testRegexp = RegExp(source); 327 var replaceRegexp = RegExp(source, 'g'); 328 return function(string) { 329 string = string == null ? '' : '' + string; 330 return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; 331 }; 332 }; 333 334 var htmlEscape = createEscaper(escapeMap); 335 var htmlUnescape = createEscaper(unescapeMap); 336 337 var Selectpicker = function(element, options) { 338 // bootstrap-select has been initialized - revert valHooks.select.set back to its original function 339 if (!valHooks.useDefault) { 340 $.valHooks.select.set = valHooks._set; 341 valHooks.useDefault = true; 342 } 343 344 this.$element = $(element); 345 this.$newElement = null; 346 this.$button = null; 347 this.$menu = null; 348 this.$lis = null; 349 this.options = options; 350 351 // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a 352 // data-attribute) 353 if (this.options.title === null) { 354 this.options.title = this.$element.attr('title'); 355 } 356 357 // Format window padding 358 var winPad = this.options.windowPadding; 359 if (typeof winPad === 'number') { 360 this.options.windowPadding = [winPad, winPad, winPad, winPad]; 361 } 362 363 //Expose public methods 364 this.val = Selectpicker.prototype.val; 365 this.render = Selectpicker.prototype.render; 366 this.refresh = Selectpicker.prototype.refresh; 367 this.setStyle = Selectpicker.prototype.setStyle; 368 this.selectAll = Selectpicker.prototype.selectAll; 369 this.deselectAll = Selectpicker.prototype.deselectAll; 370 this.destroy = Selectpicker.prototype.destroy; 371 this.remove = Selectpicker.prototype.remove; 372 this.show = Selectpicker.prototype.show; 373 this.hide = Selectpicker.prototype.hide; 374 375 this.init(); 376 }; 377 378 Selectpicker.VERSION = '1.12.2'; 379 380 // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. 381 Selectpicker.DEFAULTS = { 382 noneSelectedText: 'Nothing selected', 383 noneResultsText: 'No results matched {0}', 384 countSelectedText: function(numSelected, numTotal) { 385 return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; 386 }, 387 maxOptionsText: function(numAll, numGroup) { 388 return [ 389 (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', 390 (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' 391 ]; 392 }, 393 selectAllText: 'Select All', 394 deselectAllText: 'Deselect All',
395 doneButton: false, 396 doneButtonText: 'Close', 397 multipleSeparator: ', ', 398 styleBase: 'btn', 399 style: 'btn-default', 400 size: 'auto', 401 title: null, 402 selectedTextFormat: 'values', 403 width: false, 404 container: false, 405 hideDisabled: false, 406 showSubtext: false, 407 showIcon: true, 408 showContent: true, 409 dropupAuto: true, 410 header: false, 411 liveSearch: false, 412 liveSearchPlaceholder: null, 413 liveSearchNormalize: false, 414 liveSearchStyle: 'contains', 415 actionsBox: false, 416 iconBase: 'material-icons', 417 tickIcon: 'done', 418 showTick: false, 419 template: { 420 caret: '<span class="caret"></span>' 421 }, 422 maxOptions: false, 423 mobile: false, 424 selectOnTab: false, 425 dropdownAlignRight: false, 426 windowPadding: 0 427 }; 428 429 Selectpicker.prototype = { 430 431 constructor: Selectpicker, 432 433 init: function() { 434 var that = this, 435 id = this.$element.attr('id'); 436 437 this.$element.addClass('bs-select-hidden'); 438 439 // store originalIndex (key) and newIndex (value) in this.liObj for fast accessibility 440 // allows us to do this.$lis.eq(that.liObj[index]) instead of this.$lis.filter('[data-original-index="' + index + '"]') 441 this.liObj = {}; 442 this.multiple = this.$element.prop('multiple'); 443 this.autofocus = this.$element.prop('autofocus'); 444 this.$newElement = this.createView(); 445 this.$element 446 .after(this.$newElement) 447 .appendTo(this.$newElement); 448 this.$button = this.$newElement.children('button'); 449 this.$menu = this.$newElement.children('.dropdown-menu'); 450 this.$menuInner = this.$menu.children('.inner'); 451 this.$searchbox = this.$menu.find('input'); 452 453 this.$element.removeClass('bs-select-hidden'); 454 455 if (this.options.dropdownAlignRight === true) this.$menu.addClass('dropdown-menu-right'); 456 457 if (typeof id !== 'undefined') { 458 this.$button.attr('data-id', id); 459 $('label[for="' + id + '"]').click(function(e) { 460 e.preventDefault(); 461 that.$button.focus(); 462 }); 463 } 464 465 this.checkDisabled(); 466 this.clickListener(); 467 if (this.options.liveSearch) this.liveSearchListener(); 468 this.render(); 469 this.setStyle(); 470 this.setWidth(); 471 if (this.options.container) this.selectPosition(); 472 this.$menu.data('this', this); 473 this.$newElement.data('this', this); 474 if (this.options.mobile) this.mobile(); 475 476 this.$newElement.on({ 477 'hide.bs.dropdown': function(e) { 478 that.$menuInner.attr('aria-expanded', false); 479 that.$element.trigger('hide.bs.select', e); 480 }, 481 'hidden.bs.dropdown': function(e) { 482 that.$element.trigger('hidden.bs.select', e); 483 }, 484 'show.bs.dropdown': function(e) { 485 that.$menuInner.attr('aria-expanded', true); 486 that.$element.trigger('show.bs.select', e); 487 }, 488 'shown.bs.dropdown': function(e) { 489 that.$element.trigger('shown.bs.select', e); 490 } 491 }); 492 493 if (that.$element[0].hasAttribute('required')) { 494 this.$element.on('invalid', function() { 495 that.$button 496 .addClass('bs-invalid') 497 .focus(); 498 499 that.$element.on({
500 'focus.bs.select': function() { 501 that.$button.focus(); 502 that.$element.off('focus.bs.select'); 503 }, 504 'shown.bs.select': function() { 505 that.$element 506 .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened 507 .off('shown.bs.select'); 508 }, 509 'rendered.bs.select': function() { 510 // if select is no longer invalid, remove the bs-invalid class 511 if (this.validity.valid) that.$button.removeClass('bs-invalid'); 512 that.$element.off('rendered.bs.select'); 513 } 514 }); 515 }); 516 } 517 518 setTimeout(function() { 519 that.$element.trigger('loaded.bs.select'); 520 }); 521 }, 522 523 createDropdown: function() { 524 // Options 525 // If we are multiple or showTick option is set, then add the show-tick class 526 var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', 527 inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '', 528 autofocus = this.autofocus ? ' autofocus' : ''; 529 // Elements 530 var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">×</button>' + this.options.header + '</div>' : ''; 531 var searchbox = this.options.liveSearch ? 532 '<div class="bs-searchbox">' + 533 '<input type="text" class="form-control" autocomplete="off"' + 534 (null === this.options.liveSearchPlaceholder ? '' : ' placeholder="' + htmlEscape(this.options.liveSearchPlaceholder) + '"') + ' role="textbox" aria-label="Search">' + 535 '</div>' : 536 ''; 537 var actionsbox = this.multiple && this.options.actionsBox ? 538 '<div class="bs-actionsbox">' + 539 '<div class="btn-group btn-group-sm btn-block">' + 540 '<button type="button" class="actions-btn bs-select-all btn btn-default">' + 541 this.options.selectAllText + 542 '</button>' + 543 '<button type="button" class="actions-btn bs-deselect-all btn btn-default">' + 544 this.options.deselectAllText + 545 '</button>' + 546 '</div>' + 547 '</div>' : 548 ''; 549 var donebutton = this.multiple && this.options.doneButton ? 550 '<div class="bs-donebutton">' + 551 '<div class="btn-group btn-block">' + 552 '<button type="button" class="btn btn-sm btn-default">' + 553 this.options.doneButtonText + 554 '</button>' + 555 '</div>' + 556 '</div>' : 557 ''; 558 var drop = 559 '<div class="btn-group bootstrap-select' + showTick + inputGroup + '">' + 560 '<button type="button" class="' + this.options.styleBase + ' dropdown-toggle" data-toggle="dropdown"' + autofocus + ' role="button">' + 561 '<span class="filter-option pull-left"></span> ' + 562 '<span class="bs-caret">' + 563 this.options.template.caret + 564 '</span>' + 565 '</button>' + 566 '<div class="dropdown-menu open" role="combobox">' + 567 header + 568 searchbox + 569 actionsbox + 570 '<ul class="dropdown-menu inner" role="listbox" aria-expanded="false">' + 571 '</ul>' + 572 donebutton + 573 '</div>' + 574 '</div>'; 575 576 return $(drop); 577 }, 578 579 createView: function() { 580 var $drop = this.createDropdown(), 581 li = this.createLi(); 582 583 $drop.find('ul')[0].innerHTML = li; 584 return $drop; 585 }, 586 587 reloadLi: function() { 588 // rebuild 589 var li = this.createLi(); 590 this.$menuInner[0].innerHTML = li; 591 }, 592 593 createLi: function() { 594 var that = this, 595 _li = [], 596 optID = 0, 597 titleOption = document.createElement('option'), 598 liIndex = -1; // increment liIndex whenever a new <li> element is created to ensure liObj is correct 599 600 // Helper functions 601 /** 602 * @param content 603 * @param [index] 604 * @param [classes] 605 * @param [optgroup] 606 * @returns {string} 607 */ 608 var generateLI = function(content, index, classes, optgroup) { 609 return '<li' + 610 ((typeof classes !== 'undefined' & '' !== classes) ? ' class="' + classes + '"' : '') + 611 ((typeof index !== 'undefined' & null !== index) ? ' data-original-index="' + index + '"' : '') + 612 ((typeof optgroup !== 'undefined' & null !== optgroup) ? 'data-optgroup="' + optgroup + '"' : '') + 613 '>' + content + '</li>'; 614 }; 615 616 /** 617 * @param text 618 * @param [classes] 619 * @param [inline] 620 * @param [tokens] 621 * @returns {string} 622 */ 623 var generateA = function(text, classes, inline, tokens) { 624 return '<a tabindex="0"' + 625 (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') + 626 (inline ? ' style="' + inline + '"' : '') + 627 (that.options.liveSearchNormalize ? ' data-normalized-text="' + normalizeToBase(htmlEscape($(text).html())) + '"' : '') + 628 (typeof tokens !== 'undefined' || tokens !== null ? ' data-tokens="' + tokens + '"' : '') + 629 ' role="option">
629' + text + 630 '<span class="' + that.options.iconBase + ' check-mark"> ' + that.options.tickIcon + ' </span>' + 631 '</a>'; 632 }; 633 634 if (this.options.title && !this.multiple) { 635 // this option doesn't create a new <li> element, but does add a new option, so liIndex is decreased 636 // since liObj is recalculated on every refresh, liIndex needs to be decreased even if the titleOption is already appended 637 liIndex--; 638 639 if (!this.$element.find('.bs-title-option').length) { 640 // Use native JS to prepend option (faster) 641 var element = this.$element[0]; 642 titleOption.className = 'bs-title-option'; 643 titleOption.innerHTML = this.options.title; 644 titleOption.value = ''; 645 element.insertBefore(titleOption, element.firstChild); 646 // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. 647 // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, 648 // if so, the select will have the data-selected attribute 649 var $opt = $(element.options[element.selectedIndex]); 650 if ($opt.attr('selected') === undefined && this.$element.data('selected') === undefined) { 651 titleOption.selected = true; 652 } 653 } 654 } 655 656 this.$element.find('option').each(function(index) { 657 var $this = $(this); 658 659 liIndex++; 660 661 if ($this.hasClass('bs-title-option')) return; 662 663 // Get the class and text for the option 664 var optionClass = this.className || '', 665 inline = this.style.cssText, 666 text = $this.data('content') ? $this.data('content') : $this.html(), 667 tokens = $this.data('tokens') ? $this.data('tokens') : null, 668 subtext = typeof $this.data('subtext') !== 'undefined' ? '<small class="text-muted">' + $this.data('subtext') + '</small>' : '', 669 icon = typeof $this.data('icon') !== 'undefined' ? '<span class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></span> ' : '', 670 $parent = $this.parent(), 671 isOptgroup = $parent[0].tagName === 'OPTGROUP', 672 isOptgroupDisabled = isOptgroup && $parent[0].disabled, 673 isDisabled = this.disabled || isOptgroupDisabled; 674 675 if (icon !== '' && isDisabled) { 676 icon = '<span>' + icon + '</span>'; 677 } 678 679 if (that.options.hideDisabled && (isDisabled && !isOptgroup || isOptgroupDisabled)) { 680 liIndex--; 681 return; 682 } 683 684 if (!$this.data('content')) { 685 // Prepend any icon and append any subtext to the main text. 686 text = icon + '<span class="text">' + text + subtext + '</span>'; 687 } 688 689 if (isOptgroup && $this.data('divider') !== true) { 690 if (that.options.hideDisabled && isDisabled) { 691 if ($parent.data('allOptionsDisabled') === undefined) { 692 var $options = $parent.children(); 693 $parent.data('allOptionsDisabled', $options.filter(':disabled').length === $options.length); 694 } 695 696 if ($parent.data('allOptionsDisabled')) { 697 liIndex--; 698 return; 699 } 700 } 701 702 var optGroupClass = ' ' + $parent[0].className || ''; 703 704 if ($this.index() === 0) { // Is it the first option of the optgroup? 705 optID += 1; 706 707 // Get the opt group label 708 var label = $parent[0].label, 709 labelSubtext = typeof $parent.data('subtext') !== 'undefined' ? '<small class="text-muted">' + $parent.data('subtext') + '</small>' : '', 710 labelIcon = $parent.data('icon') ? '<span class="' + that.options.iconBase + ' ' + $parent.data('icon') + '"></span> ' : ''; 711 712 label = labelIcon + '<span class="text">' + htmlEscape(label) + labelSubtext + '</span>'; 713 714 if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? 715 liIndex++; 716 _li.push(generateLI('', null, 'divi
716der', optID + 'div')); 717 } 718 liIndex++; 719 _li.push(generateLI(label, null, 'dropdown-header' + optGroupClass, optID)); 720 } 721 722 if (that.options.hideDisabled && isDisabled) { 723 liIndex--; 724 return; 725 } 726 727 _li.push(generateLI(generateA(text, 'opt ' + optionClass + optGroupClass, inline, tokens), index, '', optID)); 728 } else if ($this.data('divider') === true) { 729 _li.push(generateLI('', index, 'divider')); 730 } else if ($this.data('hidden') === true) { 731 _li.push(generateLI(generateA(text, optionClass, inline, tokens), index, 'hidden is-hidden')); 732 } else { 733 var showDivider = this.previousElementSibling && this.previousElementSibling.tagName === 'OPTGROUP'; 734 735 // if previous element is not an optgroup and hideDisabled is true 736 if (!showDivider && that.options.hideDisabled) { 737 // get previous elements 738 var $prev = $(this).prevAll(); 739 740 for (var i = 0; i < $prev.length; i++) { 741 // find the first element in the previous elements that is an optgroup 742 if ($prev[i].tagName === 'OPTGROUP') { 743 var optGroupDistance = 0; 744 745 // loop through the options in between the current option and the optgroup 746 // and check if they are hidden or disabled 747 for (var d = 0; d < i; d++) { 748 var prevOption = $prev[d]; 749 if (prevOption.disabled || $(prevOption).data('hidden') === true) optGroupDistance++; 750 } 751 752 // if all of the options between the current option and the optgroup are hidden or disabled, show the divider 753 if (optGroupDistance === i) showDivider = true; 754 755 break; 756 } 757 } 758 } 759 760 if (showDivider) { 761 liIndex++; 762 _li.push(generateLI('', null, 'divider', optID + 'div')); 763 } 764 _li.push(generateLI(generateA(text, optionClass, inline, tokens), index)); 765 } 766 767 that.liObj[index] = liIndex; 768 }); 769 770 //If we are not multiple, we don't have a selected item, and we don't have a title, select the first element so something is set in the button 771 if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) { 772 this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); 773 } 774 775 return _li.join(''); 776 }, 777 778 findLis: function() { 779 if (this.$lis == null) this.$lis = this.$menu.find('li'); 780 return this.$lis; 781 }, 782 783 /** 784 * @param [updateLi] defaults to true 785 */ 786 render: function(updateLi) { 787 var that = this, 788 notDisabled; 789 790 //Update the LI to match the SELECT 791 if (updateLi !== false) { 792 this.$element.find('option').each(function(index) { 793 var $lis = that.findLis().eq(that.liObj[index]); 794 795 that.setDisabled(index, this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled, $lis); 796 that.setSelected(index, this.selected, $lis); 797 }); 798 } 799 800 this.togglePlaceholder(); 801 802 this.tabIndex(); 803 804 var selectedItems = this.$element.find('option').map(function() { 805 if (this.selected) { 806 if (that.options.hideDisabled && (this.disabled || this.parentNode.tagName === 'OPTGROUP' && this.parentNode.disabled)) return; 807 808 var $this = $(this), 809 icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : '', 810 subtext; 811 812 if (that.options.showSubtext && $this.data('subtext') && !that.multiple) { 813 subtext = ' <small class="text-muted">' + $this.data('subtext') + '</small>'; 814 } else { 815 subtext = ''; 816 } 817 if (typeof $this.attr('title') !== 'undefined') { 818 return $this.attr('title'); 819 } else if ($this.data('content') && that.options.showContent) { 820 return $this.data('content').toString(); 821 } else { 822 return icon + $this.html() + subtext; 823 } 824 } 825 }).toArray(); 826 827 //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled 828 //Convert all the values into a comma delimited string 829 var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); 830 831 //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. 832 if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) { 833 var max = this.options.selectedTextFormat.split('>'); 834 if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) { 835 notDisabled = this.options.hideDisabled ? ', [disabled]' : ''; 836 var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, 837 tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText; 838 title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString()); 839 } 840 } 841 842 if (this.options.title == undefined) { 843 this.options.title = this.$element.attr('title'); 844 } 845 846 if (this.options.selectedTextFormat == 'static') { 847 title = this.options.title; 848 } 849 850 //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text 851 if (!title) { 852 title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText; 853 } 854 855 //strip all HTML tags and trim the result, then unescape any escaped tags 856 this.$button.attr('title', htmlUnescape($.trim(title.replace(/<[^>]*>?/g, '')))); 857 this.$button.children('.filter-option').html(title); 858 859 this.$element.trigger('rendered.bs.select'); 860 }, 861 862 /** 863 * @param [style] 864 * @param [status] 865 */ 866 setStyle: function(style, status) { 867 if (this.$element.attr('class')) { 868 this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); 869 } 870 871 var buttonClass = style ? style : this.options.style; 872 873 if (status == 'add') { 874 this.$button.addClass(buttonClass); 875 } else if (status == 'remove') { 876 this.$button.removeClass(buttonClass); 877 } else { 878 this.$button.removeClass(this.options.style); 879 this.$button.addClass(buttonClass); 880 } 881 }, 882 883 liHeight: function(refresh) { 884 if (!refresh && (this.options.size === false || this.sizeInfo)) return; 885 886 var newElement = document.createElement('div'), 887 menu = document.createElement('div'), 888 menuInner = document.createElement('ul'), 889 divider = document.createElement('li'), 890 li = document.createElement('li'), 891 a = document.createElement('a'), 892 text = document.createElement('span'), 893 header = this.options.header && this.$menu.find('.popover-title').length > 0 ? this.$menu.find('.popover-title')[0].cloneNode(true) : null, 894 search = this.options.liveSearch ? document.createElement('div') : null, 895 actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, 896 doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null; 897 898 text.className = 'text'; 899 newElement.className = this.$menu[0].parentNode.className + ' open'; 900 menu.className = 'dropdown-menu open'; 901 menuInner.className = 'dropdown-menu inner'; 902 divider.className = 'divider'; 903 904 text.appendChild(document.createTextNode('Inner text')); 905 a.appendChild(text); 906 li.appendChild(a); 907 menuInner.appendChild(li); 908 menuInner.appendChild(divider); 909 if (header) menu.appendChild(header); 910 if (search) { 911 var input = document.createElement('input'); 912 search.className = 'bs-searchbox'; 913 input.className = 'form-control'; 914 search.appendChild(input); 915 menu.appendChild(search); 916 } 917 if (actions) menu.appendChild(actions); 918 menu.appendChild(menuInner); 919 if (doneButton) menu.appendChild(doneButton); 920 newElement.appendChild(menu); 921 922 document.body.appendChild(newElement); 923 924 var liHeight = a.offsetHeight, 925 headerHeight = header ? header.offsetHeight : 0, 926 searchHeight = search ? search.offsetHeight : 0, 927 actionsHeight = actions ? actions.offsetHeight : 0, 928 doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, 929 dividerHeight = $(divider).outerHeight(true), 930 // fall back to jQuery if getComputedStyle is not supported 931 menuStyle = typeof getComputedStyle === 'function' ? getComputedStyle(menu) : false, 932 $menu = menuStyle ? null : $(menu), 933 menuPadding = { 934 vert: parseInt(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + 935 parseInt(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + 936 parseInt(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + 937 parseInt(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), 938 horiz: parseInt(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + 939 parseInt(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + 940 parseInt(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + 941 parseInt(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) 942 }, 943 menuExtras = { 944 vert: menuPadding.vert + 945 parseInt(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + 946 parseInt(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, 947 horiz: menuPadding.horiz + 948 parseInt(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + 949 parseInt(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 950 } 951 952 document.body.removeChild(newElement); 953 954 this.sizeInfo = { 955 liHeight: liHeight, 956 headerHeight: headerHeight, 957 searchHeight: searchHeight, 958 actionsHeight: actionsHeight, 959 doneButtonHeight: doneButtonHeight,
960 dividerHeight: dividerHeight, 961 menuPadding: menuPadding, 962 menuExtras: menuExtras 963 }; 964 }, 965 966 setSize: function() { 967 this.findLis(); 968 this.liHeight(); 969 970 if (this.options.header) this.$menu.css('padding-top', 0); 971 if (this.options.size === false) return; 972 973 var that = this, 974 $menu = this.$menu, 975 $menuInner = this.$menuInner, 976 $window = $(window), 977 selectHeight = this.$newElement[0].offsetHeight, 978 selectWidth = this.$newElement[0].offsetWidth, 979 liHeight = this.sizeInfo['liHeight'], 980 headerHeight = this.sizeInfo['headerHeight'], 981 searchHeight = this.sizeInfo['searchHeight'], 982 actionsHeight = this.sizeInfo['actionsHeight'], 983 doneButtonHeight = this.sizeInfo['doneButtonHeight'], 984 divHeight = this.sizeInfo['dividerHeight'], 985 menuPadding = this.sizeInfo['menuPadding'], 986 menuExtras = this.sizeInfo['menuExtras'], 987 notDisabled = this.options.hideDisabled ? '.disabled' : '', 988 menuHeight, 989 menuWidth, 990 getHeight, 991 getWidth, 992 selectOffsetTop, 993 selectOffsetBot, 994 selectOffsetLeft, 995 selectOffsetRight, 996 getPos = function() { 997 var pos = that.$newElement.offset(), 998 $container = $(that.options.container), 999 containerPos; 1000 1001 if (that.options.container && !$container.is('body')) { 1002 containerPos = $container.offset(); 1003 containerPos.top += parseInt($container.css('borderTopWidth')); 1004 containerPos.left += parseInt($container.css('borderLeftWidth')); 1005 } else { 1006 containerPos = { 1007 top: 0, 1008 left: 0 1009 }; 1010 } 1011 1012 var winPad = that.options.windowPadding; 1013 selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); 1014 selectOffsetBot = $window.height() - selectOffsetTop - selectHeight - containerPos.top - winPad[2]; 1015 selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); 1016 selectOffsetRight = $window.width() - selectOffsetLeft - selectWidth - containerPos.left - winPad[1]; 1017 selectOffsetTop -= winPad[0]; 1018 selectOffsetLeft -= winPad[3]; 1019 }; 1020 1021 getPos(); 1022 1023 if (this.options.size === 'auto') { 1024 var getSize = function() { 1025 var minHeight, 1026 hasClass = function(className, include) { 1027 return function(element) { 1028 if (include) { 1029 return (element.classList ? element.classList.contains(className) : $(element).hasClass(className)); 1030 } else { 1031 return !(element.classList ? element.classList.contains(className) : $(element).hasClass(className)); 1032 } 1033 }; 1034 }, 1035 lis = that.$menuInner[0].getElementsByTagName('li'), 1036 lisVisible = Array.prototype.filter ? Array.prototype.filter.call(lis, hasClass('hidden', false)) : that.$lis.not('.hidden'), 1037 optGroup = Array.prototype.filter ? Array.prototype.filter.call(lisVisible, hasClass('dropdown-header', true)) : lisVisible.filter('.dropdown-header'); 1038 1039 getPos(); 1040 menuHeight = selectOffsetBot - menuExtras.vert; 1041 menuWidth = selectOffsetRight - menuExtras.horiz; 1042 1043 if (that.options.container) { 1044 if (!$menu.data('height')) $menu.data('height', $menu.height()); 1045 getHeight = $menu.data('height'); 1046 1047 if (!$menu.data('width')) $menu.data('width', $menu.width()); 1048 getWidth = $menu.data('width'); 1049 } else { 1050 getHeight = $menu.height(); 1051 getWidth = $menu.width(); 1052 } 1053 1054 if (that.options.dropupAuto) { 1055 that.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight); 1056 } 1057 1058 if (that.$newElement.hasClass('dropup')) { 1059 menuHeight = selectOffsetTop - menuExtras.vert; 1060 } 1061 1062 if (that.options.dropdownAlignRight === 'auto') { 1063 $menu.toggleClass('dropdown-menu-right', selectOffsetLeft > selectOffsetRight && (menuWidth - menuExtras.horiz) < (getWidth - selectWidth)); 1064 } 1065 1066 if ((lisVisible.length + optGroup.length) > 3) { 1067 minHeight = liHeight * 3 + menuExtras.vert - 2; 1068 } else { 1069 minHeight = 0; 1070 } 1071 1072 $menu.css({ 1073 'max-height': menuHeight + 'px', 1074 'overflow': 'hidden', 1075 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px' 1076 }); 1077 $menuInner.css({ 1078 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert + 'px', 1079 'overflow-y': 'auto', 1080 'min-height': Math.max(minHeight - menuPadding.vert, 0) + 'px' 1081 }); 1082 }; 1083 getSize(); 1084 this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); 1085 $window.off('resize.getSize scroll.getSize').on('resize.getSize scroll.getSize', getSize); 1086 } else if (this.options.size && this.options.size != 'auto' && this.$lis.not(notDisabled).length > this.options.size) { 1087 var optIndex = this.$lis.not('.divider').not(notDisabled).children().slice(0, this.options.size).last().parent().index(), 1088 divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; 1089 menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; 1090 1091 if (that.options.container) { 1092 if (!$menu.data('height')) $menu.data('height', $menu.height()); 1093 getHeight = $menu.data('height'); 1094 } else { 1095 getHeight = $menu.height(); 1096 } 1097 1098 if (that.options.dropupAuto) { 1099 //noinspection JSUnusedAssignment 1100 this.$newElement.toggleClass('dropup', selectOffsetTop > selectOffsetBot && (menuHeight - menuExtras.vert) < getHeight); 1101 } 1102 $menu.css({ 1103 'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight + 'px', 1104 'overflow': 'hidden', 1105 'min-height': '' 1106 }); 1107 $menuInner.css({ 1108 'max-height': menuHeight - menuPadding.vert + 'px', 1109 'overflow-y': 'auto', 1110 'min-height': '' 1111 }); 1112 } 1113 }, 1114 1115 setWidth: function() { 1116 if (this.options.width === 'auto') { 1117 this.$menu.css('min-width', '0'); 1118 1119 // Get correct width if element is hidden 1120 var $selectClone = this.$menu.parent().clone().appendTo('body'), 1121 $selectClone2 = this.options.container ? this.$newElement.clone().appendTo('body') : $selectClone, 1122 ulWidth = $selectClone.children('.dropdown-menu').outerWidth(), 1123 btnWidth = $selectClone2.css('width', 'auto').children('button').outerWidth(); 1124 1125 $selectClone.remove(); 1126 $selectClone2.remove(); 1127 1128 // Set width to whatever's larger, button title or longest option 1129 this.$newElement.css('width', Math.max(ulWidth, btnWidth) + 'px'); 1130 } else if (this.options.width === 'fit') { 1131 // Remove inline min-width so width can be changed from 'auto' 1132 this.$menu.css('min-width', ''); 1133 this.$newElement.css('width', '').addClass('fit-width'); 1134 } else if (this.options.width) { 1135 // Remove inline min-width so width can be changed from 'auto' 1136 this.$menu.css('min-width', ''); 1137 this.$newElement.css('width', this.options.width); 1138 } else { 1139 // Remove inline min-width/width so width can be changed 1140 this.$menu.css('min-width', ''); 1141 this.$newElement.css('width', ''); 1142 } 1143 // Remove fit-width class if width is changed programmatically 1144 if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { 1145 this.$newElement.removeClass('fit-width'); 1146 } 1147 }, 1148 1149 selectPosition: function() {
1150 this.$bsContainer = $('<div class="bs-container" />'); 1151 1152 var that = this, 1153 $container = $(this.options.container), 1154 pos, 1155 containerPos, 1156 actualHeight, 1157 getPlacement = function($element) { 1158 that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); 1159 pos = $element.offset(); 1160 1161 if (!$container.is('body')) { 1162 containerPos = $container.offset(); 1163 containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); 1164 containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); 1165 } else { 1166 containerPos = { 1167 top: 0, 1168 left: 0 1169 }; 1170 } 1171 1172 actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; 1173 1174 that.$bsContainer.css({ 1175 'top': pos.top - containerPos.top + actualHeight, 1176 'left': pos.left - containerPos.left, 1177 'width': $element[0].offsetWidth 1178 }); 1179 }; 1180 1181 this.$button.on('click', function() { 1182 var $this = $(this); 1183 1184 if (that.isDisabled()) { 1185 return; 1186 } 1187 1188 getPlacement(that.$newElement); 1189 1190 that.$bsContainer 1191 .appendTo(that.options.container) 1192 .toggleClass('open', !$this.hasClass('open')) 1193 .append(that.$menu); 1194 }); 1195 1196 $(window).on('resize scroll', function() { 1197 getPlacement(that.$newElement); 1198 }); 1199 1200 this.$element.on('hide.bs.select', function() { 1201 that.$menu.data('height', that.$menu.height()); 1202 that.$bsContainer.detach(); 1203 }); 1204 }, 1205 1206 /** 1207 * @param {number} index - the index of the option that is being changed 1208 * @param {boolean} selected - true if the option is being selected, false if being deselected 1209 * @param {JQuery} $lis - the 'li' element that is being modified 1210 */ 1211 setSelected: function(index, selected, $lis) { 1212 if (!$lis) { 1213 this.togglePlaceholder(); // check if setSelected is being called by changing the value of the select 1214 $lis = this.findLis().eq(this.liObj[index]); 1215 } 1216 1217 $lis.toggleClass('selected', selected).find('a').attr('aria-selected', selected); 1218 }, 1219 1220 /** 1221 * @param {number} index - the index of the option that is being disabled 1222 * @param {boolean} disabled - true if the option is being disabled, false if being enabled 1223 * @param {JQuery} $lis - the 'li' element that is being modified 1224 */ 1225 setDisabled: function(index, disabled, $lis) { 1226 if (!$lis) { 1227 $lis = this.findLis().eq(this.liObj[index]); 1228 } 1229 1230 if (disabled) { 1231 $lis.addClass('disabled').children('a').attr('href', '#').attr('tabindex', -1).attr('aria-disabled', true); 1232 } else { 1233 $lis.removeClass('disabled').children('a').removeAttr('href').attr('tabindex', 0).attr('aria-disabled', false); 1234 } 1235 }, 1236 1237 isDisabled: function() { 1238 return this.$element[0].disabled; 1239 }, 1240 1241 checkDisabled: function() { 1242 var that = this; 1243 1244 if (this.isDisabled()) { 1245 this.$newElement.addClass('disabled'); 1246 this.$button.addClass('disabled').attr('tabindex', -1).attr('aria-disabled', true); 1247 } else { 1248 if (this.$button.hasClass('disabled')) { 1249 this.$newElement.removeClass('disabled'); 1250 this.$button.removeClass('disabled').attr('aria-disabled', false); 1251 } 1252 1253 if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { 1254 this.$button.removeAttr('tabindex'); 1255 } 1256 } 1257 1258 this.$button.click(function() { 1259 return !that.isDisabled(); 1260 }); 1261 }, 1262 1263 togglePlaceholder: function() { 1264 var value = this.$element.val(); 1265 this.$button.toggleClass('bs-placeholder', value === null || value === '' || (value.constructor === Array && value.length === 0)); 1266 }, 1267 1268 tabIndex: function() { 1269 if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && 1270 (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { 1271 this.$element.data('tabindex', this.$element.attr('tabindex')); 1272 this.$button.attr('tabindex', this.$element.data('tabindex')); 1273 } 1274 1275 this.$element.attr('tabindex', -98); 1276 }, 1277 1278 clickListener: function() { 1279 var that = this, 1280 $document = $(document); 1281 1282 $document.data('spaceSelect', false); 1283 1284 this.$button.on('keyup', function(e) { 1285 if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { 1286 e.preventDefault(); 1287 $document.data('spaceSelect', false); 1288 } 1289 }); 1290 1291 this.$button.on('click', function() { 1292 that.setSize(); 1293 }); 1294 1295 this.$element.on('shown.bs.select', function() { 1296 if (!that.options.liveSearch && !that.multiple) { 1297 that.$menuInner.find('.selected a').focus(); 1298 } else if (!that.multiple) { 1299 var selectedIndex = that.liObj[that.$element[0].selectedIndex]; 1300 1301 if (typeof selectedIndex !== 'number' || that.options.size === false) return; 1302 1303 // scroll to selected option 1304 var offset = that.$lis.eq(selectedIndex)[0].offsetTop - that.$menuInner[0].offsetTop; 1305 offset = offset - that.$menuInner[0].offsetHeight / 2 + that.sizeInfo.liHeight / 2; 1306 that.$menuInner[0].scrollTop = offset; 1307 } 1308 }); 1309 1310 this.$menuInner.on('click', 'li a', function(e) { 1311 var $this = $(this), 1312 clickedIndex = $this.parent().data('originalIndex'),
1313 prevValue = that.$element.val(), 1314 prevIndex = that.$element.prop('selectedIndex'), 1315 triggerChange = true; 1316 1317 // Don't close on multi choice menu 1318 if (that.multiple && that.options.maxOptions !== 1) { 1319 e.stopPropagation(); 1320 } 1321 1322 e.preventDefault(); 1323 1324 //Don't run if we have been disabled 1325 if (!that.isDisabled() && !$this.parent().hasClass('disabled')) { 1326 var $options = that.$element.find('option'), 1327 $option = $options.eq(clickedIndex), 1328 state = $option.prop('selected'), 1329 $optgroup = $option.parent('optgroup'), 1330 maxOptions = that.options.maxOptions, 1331 maxOptionsGrp = $optgroup.data('maxOptions') || false; 1332 1333 if (!that.multiple) { // Deselect all others if not multi select box 1334 $options.prop('selected', false); 1335 $option.prop('selected', true); 1336 that.$menuInner.find('.selected').removeClass('selected').find('a').attr('aria-selected', false); 1337 that.setSelected(clickedIndex, true); 1338 } else { // Toggle the one we have chosen if we are multi select. 1339 $option.prop('selected', !state); 1340 that.setSelected(clickedIndex, !state); 1341 $this.blur(); 1342 1343 if (maxOptions !== false || maxOptionsGrp !== false) { 1344 var maxReached = maxOptions < $options.filter(':selected').length, 1345 maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; 1346 1347 if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { 1348 if (maxOptions && maxOptions == 1) { 1349 $options.prop('selected', false); 1350 $option.prop('selected', true); 1351 that.$menuInner.find('.selected').removeClass('selected'); 1352 that.setSelected(clickedIndex, true); 1353 } else if (maxOptionsGrp && maxOptionsGrp == 1) { 1354 $optgroup.find('option:selected').prop('selected', false); 1355 $option.prop('selected', true); 1356 var optgroupID = $this.parent().data('optgroup'); 1357 that.$menuInner.find('[data-optgroup="' + optgroupID + '"]').removeClass('selected'); 1358 that.setSelected(clickedIndex, true); 1359 } else { 1360 var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, 1361 maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, 1362 maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), 1363 maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), 1364 $notify = $('<div class="notify"></div>'); 1365 // If {var} is set in array, replace it 1366 /** @deprecated */ 1367 if (maxOptionsArr[2]) { 1368 maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); 1369 maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); 1370 } 1371 1372 $option.prop('selected', false); 1373 1374 that.$menu.append($notify); 1375 1376 if (maxOptions && maxReached) { 1377 $notify.append($('<div>' + maxTxt + '</div>')); 1378 triggerChange = false;
1379 that.$element.trigger('maxReached.bs.select'); 1380 } 1381 1382 if (maxOptionsGrp && maxReachedGrp) { 1383 $notify.append($('<div>' + maxTxtGrp + '</div>')); 1384 triggerChange = false; 1385 that.$element.trigger('maxReachedGrp.bs.select'); 1386 } 1387 1388 setTimeout(function() { 1389 that.setSelected(clickedIndex, false); 1390 }, 10); 1391 1392 $notify.delay(750).fadeOut(300, function() { 1393 $(this).remove(); 1394 }); 1395 } 1396 } 1397 } 1398 } 1399 1400 if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { 1401 that.$button.focus(); 1402 } else if (that.options.liveSearch) { 1403 that.$searchbox.focus(); 1404 } 1405 1406 // Trigger select 'change' 1407 if (triggerChange) { 1408 if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { 1409 // $option.prop('selected') is current option state (selected/unselected). state is previous option state. 1410 changed_arguments = [clickedIndex, $option.prop('selected'), state]; 1411 that.$element 1412 .triggerNative('change'); 1413 } 1414 } 1415 } 1416 }); 1417 1418 this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function(e) { 1419 if (e.currentTarget == this) { 1420 e.preventDefault(); 1421 e.stopPropagation(); 1422 if (that.options.liveSearch && !$(e.target).hasClass('close')) { 1423 that.$searchbox.focus(); 1424 } else { 1425 that.$button.focus(); 1426 } 1427 } 1428 }); 1429 1430 this.$menuInner.on('click', '.divider, .dropdown-header', function(e) { 1431 e.preventDefault(); 1432 e.stopPropagation(); 1433 if (that.options.liveSearch) { 1434 that.$searchbox.focus(); 1435 } else { 1436 that.$button.focus(); 1437 } 1438 }); 1439 1440 this.$menu.on('click', '.popover-title .close', function() { 1441 that.$button.click(); 1442 }); 1443 1444 this.$searchbox.on('click', function(e) { 1445 e.stopPropagation(); 1446 }); 1447 1448 this.$menu.on('click', '.actions-btn', function(e) { 1449 if (that.options.liveSearch) { 1450 that.$searchbox.focus(); 1451 } else { 1452 that.$button.focus(); 1453 } 1454 1455 e.preventDefault(); 1456 e.stopPropagation(); 1457 1458 if ($(this).hasClass('bs-select-all')) { 1459 that.selectAll(); 1460 } else { 1461 that.deselectAll(); 1462 } 1463 }); 1464 1465 this.$element.change(function() { 1466 that.render(false); 1467 that.$element.trigger('changed.bs.select', changed_arguments); 1468 changed_arguments = null; 1469 }); 1470 }, 1471 1472 liveSearchListener: function() { 1473 var that = this, 1474 $no_results = $('<li class="no-results"></li>'); 1475 1476 this.$button.on('click.dropdown.data-api', function() { 1477 that.$menuInner.find('.active').removeClass('active'); 1478 if (!!that.$searchbox.val()) { 1479 that.$searchbox.val(''); 1480 that.$lis.not('.is-hidden').removeClass('hidden'); 1481 if (!!$no_results.parent().length) $no_results.remove(); 1482 } 1483 if (!that.multiple) that.$menuInner.find('.selected').addClass('active'); 1484 setTimeout(function() { 1485 that.$searchbox.focus(); 1486 }, 10); 1487 }); 1488 1489 this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function(e) { 1490 e.stopPropagation(); 1491 }); 1492 1493 this.$searchbox.on('input propertychange', function() { 1494 that.$lis.not('.is-hidden').removeClass('hidden'); 1495 that.$lis.filter('.active').removeClass('active'); 1496 $no_results.remove(); 1497 1498 if (that.$searchbox.val()) { 1499 var $searchBase = that.$lis.not('.is-hidden, .divider, .dropdown-header'), 1500 $hideItems; 1501 if (that.options.liveSearchNormalize) { 1502 $hideItems = $searchBase.find('a').not(':a' + that._searchStyle() + '("' + normalizeToBase(that.$searchbox.val()) + '")'); 1503 } else { 1504 $hideItems = $searchBase.find('a').not(':' + that._searchStyle() + '("' + that.$searchbox.val() + '")'); 1505 } 1506 1507 if ($hideItems.length === $searchBase.length) { 1508 $no_results.html(that.options.noneResultsText.replace('{0}', '"' + htmlEscape(that.$searchbox.val()) + '"')); 1509 that.$menuInner.append($no_results); 1510 that.$lis.addClass('hidden'); 1511 } else { 1512 $hideItems.parent().addClass('hidden'); 1513 1514 var $lisVisible = that.$lis.not('.hidden'), 1515 $foundDiv; 1516 1517 // hide divider if first or last visible, or if followed by another divider 1518 $lisVisible.each(function(index) { 1519 var $this = $(this); 1520 1521 if ($this.hasClass('divider')) { 1522 if ($foundDiv === undefined) { 1523 $this.addClass('hidden'); 1524 } else { 1525 if ($foundDiv) $foundDiv.addClass('hidden'); 1526 $foundDiv = $this; 1527 } 1528 } else if ($this.hasClass('dropdown-header') && $lisVisible.eq(index + 1).data('optgroup') !== $this.data('optgroup')) { 1529 $this.addClass('hidden'); 1530 } else { 1531 $foundDiv = null; 1532 } 1533 }); 1534 if ($foundDiv) $foundDiv.addClass('hidden'); 1535 1536 $searchBase.not('.hidden').first().addClass('active'); 1537 } 1538 } 1539 }); 1540 }, 1541 1542 _searchStyle: function() { 1543 var styles = { 1544 begins: 'ibegins', 1545 startsWith: 'ibegins' 1546 }; 1547 1548 return styles[this.options.liveSearchStyle] || 'icontains'; 1549 }, 1550 1551 val: function(value) { 1552 if (typeof value !== 'undefined') { 1553 this.$element.val(value); 1554 this.render(); 1555 1556 return this.$element; 1557 } else { 1558 return this.$element.val(); 1559 } 1560 }, 1561 1562 changeAll: function(status) { 1563 if (!this.multiple) return; 1564 if (typeof status === 'undefined') status = true; 1565 1566 this.findLis(); 1567 1568 var $options = this.$element.find('option'), 1569 $lisVisible = this.$lis.not('.divider, .dropdown-header, .disabled, .hidden'), 1570 lisVisLen = $lisVisible.length, 1571 selectedOptions = []; 1572 1573 if (status) { 1574 if ($lisVisible.filter('.selected').length === $lisVisible.length) return; 1575 } else { 1576 if ($lisVisible.filter('.selected').length === 0) return; 1577 } 1578 1579 $lisVisible.toggleClass('selected', status); 1580 1581 for (var i = 0; i < lisVisLen; i++) { 1582 var origIndex = $lisVisible[i].getAttribute('data-original-index'); 1583 selectedOptions[selectedOptions.length] = $options.eq(origIndex)[0]; 1584 } 1585 1586 $(selectedOptions).prop('selected', status); 1587 1588 this.render(false); 1589 1590 this.togglePlaceholder(); 1591 1592 this.$element 1593 .triggerNative('change'); 1594 }, 1595 1596 selectAll: function() { 1597 return this.changeAll(true); 1598 }, 1599 1600 deselectAll: function() { 1601 return this.changeAll(false); 1602 }, 1603 1604 toggle: function(e) { 1605 e = e || window.event; 1606 1607 if (e) e.stopPropagation(); 1608 1609 this.$button.trigger('click'); 1610 }, 1611 1612 keydown: function(e) { 1613 var $this = $(this), 1614 $parent = $this.is('input') ? $this.parent().parent() : $this.parent(), 1615 $items, 1616 that = $parent.data('this'), 1617 index, 1618 next, 1619 first, 1620 last, 1621 prev, 1622 nextPrev, 1623 prevIndex, 1624 isActive, 1625 selector = ':not(.disabled, .hidden, .dropdown-header, .divider)', 1626 keyCodeMap = { 1627 32: ' ', 1628 48: '0', 1629 49: '1', 1630 50: '2', 1631 51: '3', 1632 52: '4', 1633 53: '5', 1634 54: '6', 1635 55: '7', 1636 56: '8', 1637 57: '9', 1638 59: ';', 1639 65: 'a', 1640 66: 'b', 1641 67: 'c', 1642 68: 'd', 1643 69: 'e', 1644 70: 'f', 1645 71: 'g', 1646 72: 'h', 1647 73: 'i', 1648 74: 'j', 1649 75: 'k', 1650 76: 'l', 1651 77: 'm', 1652 78: 'n', 1653 79: 'o', 1654 80: 'p', 1655 81: 'q', 1656 82: 'r', 1657 83: 's', 1658 84: 't', 1659 85: 'u', 1660 86: 'v',
1661 87: 'w', 1662 88: 'x', 1663 89: 'y', 1664 90: 'z', 1665 96: '0', 1666 97: '1', 1667 98: '2', 1668 99: '3', 1669 100: '4', 1670 101: '5', 1671 102: '6', 1672 103: '7', 1673 104: '8', 1674 105: '9' 1675 }; 1676 1677 if (that.options.liveSearch) $parent = $this.parent().parent(); 1678 1679 if (that.options.container) $parent = that.$menu; 1680 1681 $items = $('[role="listbox"] li', $parent); 1682 1683 isActive = that.$newElement.hasClass('open'); 1684 1685 if (!isActive && (e.keyCode >= 48 && e.keyCode <= 57 || e.keyCode >= 96 && e.keyCode <= 105 || e.keyCode >= 65 && e.keyCode <= 90)) { 1686 if (!that.options.container) { 1687 that.setSize(); 1688 that.$menu.parent().addClass('open'); 1689 isActive = true; 1690 } else { 1691 that.$button.trigger('click'); 1692 } 1693 that.$searchbox.focus(); 1694 return; 1695 } 1696 1697 if (that.options.liveSearch) { 1698 if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive) { 1699 e.preventDefault(); 1700 e.stopPropagation(); 1701 that.$menuInner.click(); 1702 that.$button.focus(); 1703 } 1704 // $items contains li elements when liveSearch is enabled 1705 $items = $('[role="listbox"] li' + selector, $parent); 1706 if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) { 1707 if ($items.filter('.active').length === 0) { 1708 $items = that.$menuInner.find('li'); 1709 if (that.options.liveSearchNormalize) { 1710 $items = $items.filter(':a' + that._searchStyle() + '(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')'); 1711 } else { 1712 $items = $items.filter(':' + that._searchStyle() + '(' + keyCodeMap[e.keyCode] + ')'); 1713 } 1714 } 1715 } 1716 } 1717 1718 if (!$items.length) return; 1719 1720 if (/(38|40)/.test(e.keyCode.toString(10))) { 1721 index = $items.index($items.find('a').filter(':focus').parent()); 1722 first = $items.filter(selector).first().index(); 1723 last = $items.filter(selector).last().index(); 1724 next = $items.eq(index).nextAll(selector).eq(0).index(); 1725 prev = $items.eq(index).prevAll(selector).eq(0).index(); 1726 nextPrev = $items.eq(next).prevAll(selector).eq(0).index(); 1727 1728 if (that.options.liveSearch) { 1729 $items.each(function(i) { 1730 if (!$(this).hasClass('disabled')) { 1731 $(this).data('index', i); 1732 } 1733 }); 1734 index = $items.index($items.filter('.active')); 1735 first = $items.first().data('index'); 1736 last = $items.last().data('index'); 1737 next = $items.eq(index).nextAll().eq(0).data('index'); 1738 prev = $items.eq(index).prevAll().eq(0).data('index'); 1739 nextPrev = $items.eq(next).prevAll().eq(0).data('index'); 1740 } 1741 1742 prevIndex = $this.data('prevIndex'); 1743 1744 if (e.keyCode == 38) { 1745 if (that.options.liveSearch) index--; 1746 if (index != nextPrev && index > prev) index = prev; 1747 if (index < first) index = first; 1748 if (index == prevIndex) index = last; 1749 } else if (e.keyCode == 40) { 1750 if (that.options.liveSearch) index++; 1751 if (index == -1) index = 0; 1752 if (index != nextPrev && index < next) index = next; 1753 if (index > last) index = last; 1754 if (index == prevIndex) index = first; 1755 } 1756 1757 $this.data('prevIndex', index); 1758 1759 if (!that.options.liveSearch) { 1760 $items.eq(index).children('a').focus(); 1761 } else { 1762 e.preventDefault(); 1763 if (!$this.hasClass('dropdown-toggle')) { 1764 $items.removeClass('active').eq(index).addClass('active').children('a').focus(); 1765 $this.focus(); 1766 } 1767 } 1768 1769 } else if (!$this.is('input')) { 1770 var keyIndex = [], 1771 count, 1772 prevKey; 1773 1774 $items.each(function() { 1775 if (!$(this).hasClass('disabled')) { 1776 if ($.trim($(this).children('a').text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { 1777 keyIndex.push($(this).index()); 1778 } 1779 } 1780 }); 1781 1782 count = $(document).data('keycount'); 1783 count++; 1784 $(document).data('keycount', count); 1785 1786 prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1); 1787 1788 if (prevKey != keyCodeMap[e.keyCode]) { 1789 count = 1; 1790 $(document).data('keycount', count); 1791 } else if (count >= keyIndex.length) { 1792 $(document).data('keycount', 0); 1793 if (count > keyIndex.length) count = 1; 1794 } 1795 1796 $items.eq(keyIndex[count - 1]).children('a').focus(); 1797 } 1798 1799 // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. 1800 if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) { 1801 if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault(); 1802 if (!that.options.liveSearch) { 1803 var elem = $(':focus'); 1804 elem.click(); 1805 // Bring back focus for multiselects 1806 elem.focus(); 1807 // Prevent screen from scrolling if the user hit the spacebar 1808 e.preventDefault(); 1809 // Fixes spacebar selection of dropdown items in FF & IE 1810 $(document).data('spaceSelect', true); 1811 } else if (!/(32)/.test(e.keyCode.toString(10))) { 1812 that.$menuInner.find('.active a').click(); 1813 $this.focus(); 1814 } 1815 $(document).data('keycount', 0); 1816 } 1817 1818 if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) { 1819 that.$menu.parent().removeClass('open'); 1820 if (that.options.container) that.$newElement.removeClass('open'); 1821 that.$button.focus(); 1822 } 1823 }, 1824 1825 mobile: function() { 1826 this.$element.addClass('mobile-device'); 1827 }, 1828 1829 refresh: function() { 1830 this.$lis = null; 1831 this.liObj = {}; 1832 this.reloadLi(); 1833 this.render(); 1834 this.checkDisabled(); 1835 this.liHeight(true); 1836 this.setStyle(); 1837 this.setWidth(); 1838 if (this.$lis) this.$searchbox.trigger('propertychange'); 1839 1840 this.$element.trigger('refreshed.bs.select'); 1841 }, 1842 1843 hide: function() { 1844 this.$newElement.hide(); 1845 }, 1846 1847 show: function() { 1848 this.$newElement.show(); 1849 }, 1850 1851 remove: function() { 1852 this.$newElement.remove(); 1853 this.$element.remove(); 1854 }, 1855 1856 destroy: function() { 1857 this.$newElement.before(this.$element).remove(); 1858 1859 if (this.$bsContainer) { 1860 this.$bsContainer.remove(); 1861 } else { 1862 this.$menu.remove(); 1863 } 1864 1865 this.$element 1866 .off('.bs.select') 1867 .removeData('selectpicker') 1868 .removeClass('bs-select-hidden selectpicker'); 1869 } 1870 }; 1871 1872 // SELECTPICKER PLUGIN DEFINITION 1873 // ============================== 1874 function Plugin(option) { 1875 // get the args of the outer function.. 1876 var args = arguments; 1877 // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them
1878 // to get lost/corrupted in android 2.3 and IE9 #715 #775 1879 var _option = option; 1880 1881 [].shift.apply(args); 1882 1883 var value; 1884 var chain = this.each(function() { 1885 var $this = $(this); 1886 if ($this.is('select')) { 1887 var data = $this.data('selectpicker'), 1888 options = typeof _option == 'object' && _option; 1889 1890 if (!data) { 1891 var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options); 1892 config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), $this.data().template, options.template); 1893 $this.data('selectpicker', (data = new Selectpicker(this, config))); 1894 } else if (options) { 1895 for (var i in options) { 1896 if (options.hasOwnProperty(i)) { 1897 data.options[i] = options[i]; 1898 } 1899 } 1900 } 1901 1902 if (typeof _option == 'string') { 1903 if (data[_option] instanceof Function) { 1904 value = data[_option].apply(data, args); 1905 } else { 1906 value = data.options[_option]; 1907 } 1908 } 1909 } 1910 }); 1911 1912 if (typeof value !== 'undefined') { 1913 //noinspection JSUnusedAssignment 1914 return value; 1915 } else { 1916 return chain; 1917 } 1918 } 1919 1920 var old = $.fn.selectpicker; 1921 $.fn.selectpicker = Plugin; 1922 $.fn.selectpicker.Constructor = Selectpicker; 1923 1924 // SELECTPICKER NO CONFLICT 1925 // ======================== 1926 $.fn.selectpicker.noConflict = function() { 1927 $.fn.selectpicker = old; 1928 return this; 1929 }; 1930 1931 $(document) 1932 .data('keycount', 0) 1933 .on('keydown.bs.select', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', Selectpicker.prototype.keydown) 1934 .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role="listbox"], .bs-searchbox input', function(e) { 1935 e.stopPropagation(); 1936 }); 1937 1938 // SELECTPICKER DATA-API 1939 // ===================== 1940 $(window).on('load.bs.select.data-api', function() { 1941 $('.selectpicker').each(function() { 1942 var $selectpicker = $(this); 1943 Plugin.call($selectpicker, $selectpicker.data()); 1944 }) 1945 }); 1946 })(jQuery); 1947 1948 1949}));
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.