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