vendor: 44,192 bytes, lines 1-1333
1/*! 2 * Bootstrap-select v1.13.9 (https://developer.snapappointments.com/bootstrap-select) 3 * 4 * Copyright 2012-2019 SnapAppointments, LLC 5 * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) 6 */ 7 8(function (root, factory) { 9 if (root === undefined && window !== undefined) root = window; 10 if (typeof define === 'function' && define.amd) { 11 // AMD. Register as an anonymous module unless amdModuleId is set 12 define(["jquery"], function (a0) { 13 return (factory(a0)); 14 }); 15 } else if (typeof module === 'object' && module.exports) { 16 // Node. Does not work with strict CommonJS, but 17 // only CommonJS-like environments that support module.exports, 18 // like Node. 19 module.exports = factory(require("jquery")); 20 } else { 21 factory(root["jQuery"]); 22 } 23}(this, function (jQuery) { 24 25(function ($) { 26 'use strict'; 27 28 var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']; 29 30 var uriAttrs = [ 31 'background', 32 'cite', 33 'href', 34 'itemtype', 35 'longdesc', 36 'poster', 37 'src', 38 'xlink:href' 39 ]; 40 41 var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; 42 43 var DefaultWhitelist = { 44 // Global attributes allowed on any supplied element below. 45 '*': ['class', 'dir', 'id', 'lang', 'role', 'tabindex', 'style', ARIA_ATTRIBUTE_PATTERN], 46 a: ['target', 'href', 'title', 'rel'], 47 area: [], 48 b: [], 49 br: [], 50 col: [], 51 code: [], 52 div: [], 53 em: [], 54 hr: [], 55 h1: [], 56 h2: [], 57 h3: [], 58 h4: [], 59 h5: [], 60 h6: [], 61 i: [], 62 img: ['src', 'alt', 'title', 'width', 'height'], 63 li: [], 64 ol: [], 65 p: [], 66 pre: [], 67 s: [], 68 small: [], 69 span: [], 70 sub: [], 71 sup: [], 72 strong: [], 73 u: [], 74 ul: [] 75 } 76 77 /** 78 * A pattern that recognizes a commonly useful subset of URLs that are safe. 79 * 80 * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts 81 */ 82 var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi; 83 84 /** 85 * A pattern that matches safe data URLs. Only matches image, video and audio types. 86 * 87 * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts 88 */ 89 var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i; 90 91 function allowedAttribute (attr, allowedAttributeList) { 92 var attrName = attr.nodeName.toLowerCase() 93 94 if ($.inArray(attrName, allowedAttributeList) !== -1) { 95 if ($.inArray(attrName, uriAttrs) !== -1) { 96 return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)) 97 } 98 99 return true 100 } 101 102 var regExp = $(allowedAttributeList).filter(function (index, value) { 103 return value instanceof RegExp 104 }) 105 106 // Check if a regular expression validates the attribute. 107 for (var i = 0, l = regExp.length; i < l; i++) { 108 if (attrName.match(regExp[i])) { 109 return true 110 } 111 } 112 113 return false 114 } 115 116 function sanitizeHtml (unsafeElements, whiteList, sanitizeFn) { 117 if (sanitizeFn && typeof sanitizeFn === 'function') { 118 return sanitizeFn(unsafeElements); 119 } 120 121 var whitelistKeys = Object.keys(whiteList); 122 123 for (var i = 0, len = unsafeElements.length; i < len; i++) { 124 var elements = unsafeElements[i].querySelectorAll('*'); 125 126 for (var j = 0, len2 = elements.length; j < len2; j++) { 127 var el = elements[j]; 128 var elName = el.nodeName.toLowerCase(); 129 130 if (whitelistKeys.indexOf(elName) === -1) { 131 el.parentNode.removeChild(el); 132 133 continue; 134 } 135 136 var attributeList = [].slice.call(el.attributes); 137 var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []); 138 139 for (var k = 0, len3 = attributeList.length; k < len3; k++) { 140 var attr = attributeList[k]; 141 142 if (!allowedAttribute(attr, whitelistedAttributes)) { 143 el.removeAttribute(attr.nodeName); 144 } 145 } 146 } 147 } 148 } 149 150 // Polyfill for browsers with no classList support 151 // Remove in v2 152 if (!('classList' in document.createElement('_'))) { 153 (function (view) { 154 if (!('Element' in view)) return; 155 156 var classListProp = 'classList', 157 protoProp = 'prototype', 158 elemCtrProto = view.Element[protoProp], 159 objCtr = Object, 160 classListGetter = function () { 161 var $elem = $(this); 162 163 return { 164 add: function (classes) { 165 classes = Array.prototype.slice.call(arguments).join(' '); 166 return $elem.addClass(classes); 167 }, 168 remove: function (classes) { 169 classes = Array.prototype.slice.call(arguments).join(' '); 170 return $elem.removeClass(classes); 171 }, 172 toggle: function (classes, force) { 173 return $elem.toggleClass(classes, force); 174 }, 175 contains: function (classes) { 176 return $elem.hasClass(classes); 177 } 178 } 179 }; 180 181 if (objCtr.defineProperty) { 182 var classListPropDesc = { 183 get: classListGetter, 184 enumerable: true, 185 configurable: true 186 }; 187 try { 188 objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 189 } catch (ex) { // IE 8 doesn't support enumerable:true 190 // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 191 // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected 192 if (ex.number === undefined || ex.number === -0x7FF5EC54) { 193 classListPropDesc.enumerable = false; 194 objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); 195 } 196 } 197 } else if (objCtr[protoProp].__defineGetter__) { 198 elemCtrProto.__defineGetter__(classListProp, classListGetter); 199 } 200 }(window)); 201 } 202 203 var testElement = document.createElement('_'); 204 205 testElement.classList.add('c1', 'c2'); 206 207 if (!testElement.classList.contains('c2')) { 208 var _add = DOMTokenList.prototype.add, 209 _remove = DOMTokenList.prototype.remove; 210 211 DOMTokenList.prototype.add = function () { 212 Array.prototype.forEach.call(arguments, _add.bind(this)); 213 } 214 215 DOMTokenList.prototype.remove = function () { 216 Array.prototype.forEach.call(arguments, _remove.bind(this)); 217 } 218 } 219 220 testElement.classList.toggle('c3', false); 221 222 // Polyfill for IE 10 and Firefox <24, where classList.toggle does not 223 // support the second argument. 224 if (testElement.classList.contains('c3')) { 225 var _toggle = DOMTokenList.prototype.toggle; 226 227 DOMTokenList.prototype.toggle = function (token, force) { 228 if (1 in arguments && !this.contains(token) === !force) { 229 return force; 230 } else { 231 return _toggle.call(this, token); 232 } 233 }; 234 } 235 236 testElement = null; 237 238 // shallow array comparison 239 function isEqual (array1, array2) { 240 return array1.length === array2.length && array1.every(function (element, index) { 241 return element === array2[index]; 242 }); 243 }; 244 245 // <editor-fold desc="Shims"> 246 if (!String.prototype.startsWith) { 247 (function () { 248 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` 249 var defineProperty = (function () { 250 // IE 8 only supports `Object.defineProperty` on DOM elements 251 try { 252 var object = {}; 253 var $defineProperty = Object.defineProperty; 254 var result = $defineProperty(object, object, object) && $defineProperty; 255 } catch (error) { 256 } 257 return result; 258 }()); 259 var toString = {}.toString; 260 var startsWith = function (search) { 261 if (this == null) { 262 throw new TypeError(); 263 } 264 var string = String(this); 265 if (search && toString.call(search) == '[object RegExp]') { 266 throw new TypeError(); 267 } 268 var stringLength = string.length; 269 var searchString = String(search); 270 var searchLength = searchString.length; 271 var position = arguments.length > 1 ? arguments[1] : undefined; 272 // `ToInteger` 273 var pos = position ? Number(position) : 0; 274 if (pos != pos) { // better `isNaN` 275 pos = 0; 276 } 277 var start = Math.min(Math.max(pos, 0), stringLength); 278 // Avoid the `indexOf` call if no match is possible 279 if (searchLength + start > stringLength) { 280 return false; 281 } 282 var index = -1; 283 while (++index < searchLength) { 284 if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { 285 return false; 286 } 287 } 288 return true; 289 }; 290 if (defineProperty) { 291 defineProperty(String.prototype, 'startsWith', { 292 'value': startsWith, 293 'configurable': true, 294 'writable': true 295 }); 296 } else { 297 String.prototype.startsWith = startsWith; 298 } 299 }()); 300 } 301 302 if (!Object.keys) { 303 Object.keys = function ( 304 o, // object 305 k, // key 306 r // result array 307 ) { 308 // initialize object and result 309 r = []; 310 // iterate over object keys 311 for (k in o) { 312 // fill result array with non-prototypical keys 313 r.hasOwnProperty.call(o, k) && r.push(k); 314 } 315 // return result 316 return r; 317 }; 318 } 319 320 if (HTMLSelectElement && !HTMLSelectElement.prototype.hasOwnProperty('selectedOptions')) { 321 Object.defineProperty(HTMLSelectElement.prototype, 'selectedOptions', { 322 get: function () { 323 return this.querySelectorAll(':checked'); 324 } 325 }); 326 } 327 328 // much faster than $.val() 329 function getSelectValues (select) { 330 var result = []; 331 var options = select.selectedOptions; 332 var opt; 333 334 if (select.multiple) { 335 for (var i = 0, len = options.length; i < len; i++) { 336 opt = options[i]; 337 338 result.push(opt.value || opt.text); 339 } 340 } else { 341 result = select.value; 342 } 343 344 return result; 345 } 346 347 // set data-selected on select element if the value has been programmatically selected 348 // prior to initialization of bootstrap-select 349 // * consider removing or replacing an alternative method * 350 var valHooks = { 351 useDefault: false, 352 _set: $.valHooks.select.set 353 }; 354 355 $.valHooks.select.set = function (elem, value) { 356 if (value && !valHooks.useDefault) $(elem).data('selected', true); 357 358 return valHooks._set.apply(this, arguments); 359 }; 360 361 var changedArguments = null; 362 363 var EventIsSupported = (function () { 364 try { 365 new Event('change'); 366 return true; 367 } catch (e) { 368 return false; 369 } 370 })(); 371 372 $.fn.triggerNative = function (eventName) { 373 var el = this[0], 374 event; 375 376 if (el.dispatchEvent) { // for modern browsers & IE9+ 377 if (EventIsSupported) { 378 // For modern browsers 379 event = new Event(eventName, { 380 bubbles: true 381 }); 382 } else { 383 // For IE since it doesn't support Event constructor 384 event = document.createEvent('Event'); 385 event.initEvent(eventName, true, false); 386 } 387 388 el.dispatchEvent(event); 389 } else if (el.fireEvent) { // for IE8 390 event = document.createEventObject(); 391 event.eventType = eventName; 392 el.fireEvent('on' + eventName, event); 393 } else { 394 // fall back to jQuery.trigger 395 this.trigger(eventName); 396 } 397 }; 398 // </editor-fold> 399 400 function stringSearch (li, searchString, method, normalize) { 401 var stringTypes = [ 402 'display', 403 'subtext', 404 'tokens' 405 ], 406 searchSuccess = false; 407 408 for (var i = 0; i < stringTypes.length; i++) { 409 var stringType = stringTypes[i], 410 string = li[stringType]; 411 412 if (string) { 413 string = string.toString(); 414 415 // Strip HTML tags. This isn't perfect, but it's much faster than any other method 416 if (stringType === 'display') { 417 string = string.replace(/<[^>]+>/g, ''); 418 } 419 420 if (normalize) string = normalizeToBase(string); 421 string = string.toUpperCase(); 422 423 if (method === 'contains') { 424 searchSuccess = string.indexOf(searchString) >= 0; 425 } else { 426 searchSuccess = string.startsWith(searchString); 427 } 428 429 if (searchSuccess) break; 430 } 431 } 432 433 return searchSuccess; 434 } 435 436 function toInteger (value) { 437 return parseInt(value, 10) || 0; 438 } 439 440 // Borrowed from Lodash (_.deburr) 441 /** Used to map Latin Unicode letters to basic Latin letters. */ 442 var deburredLetters = { 443 // Latin-1 Supplement block. 444 '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', 445 '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', 446 '\xc7': 'C', '\xe7': 'c', 447 '\xd0': 'D', '\xf0': 'd', 448 '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', 449 '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', 450 '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', 451 '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', 452 '\xd1': 'N', '\xf1': 'n', 453 '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', 454 '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', 455 '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', 456 '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', 457 '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', 458 '\xc6': 'Ae', '\xe6': 'ae', 459 '\xde': 'Th', '\xfe': 'th', 460 '\xdf': 'ss', 461 // Latin Extended-A block. 462 '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', 463 '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', 464 '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', 465 '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', 466 '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', 467 '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', 468 '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', 469 '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', 470 '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', 471 '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', 472 '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', 473 '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', 474 '\u0134': 'J', '\u0135': 'j', 475 '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', 476 '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', 477 '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', 478 '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', 479 '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', 480 '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', 481 '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', 482 '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', 483 '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', 484 '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', 485 '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', 486 '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', 487 '\u0163': 't', '\u0165': 't', '\u0167': 't', 488 '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', 489 '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', 490 '\u0174': 'W', '\u0175': 'w', 491 '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', 492 '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', 493 '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', 494 '\u0132': 'IJ', '\u0133': 'ij', 495 '\u0152': 'Oe', '\u0153': 'oe', 496 '\u0149': "'n", '\u017f': 's' 497 }; 498 499 /** Used to match Latin Unicode letters (excluding mathematical operators). */ 500 var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; 501 502 /** Used to compose unicode character classes. */ 503 var rsComboMarksRange = '\\u0300-\\u036f', 504 reComboHalfMarksRange = '\\ufe20-\\ufe2f', 505 rsComboSymbolsRange = '\\u20d0-\\u20ff', 506 rsComboMarksExtendedRange = '\\u1ab0-\\u1aff', 507 rsComboMarksSupplementRange = '\\u1dc0-\\u1dff', 508 rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange; 509 510 /** Used to compose unicode capture groups. */ 511 var rsCombo = '[' + rsComboRange + ']'; 512 513 /** 514 * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and 515 * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). 516 */ 517 var reComboMark = RegExp(rsCombo, 'g'); 518 519 function deburrLetter (key) { 520 return deburredLetters[key]; 521 }; 522 523 function normalizeToBase (string) { 524 string = string.toString(); 525 return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); 526 } 527 528 // List of HTML entities for escaping. 529 var escapeMap = { 530 '&': '&', 531 '<': '<', 532 '>': '>', 533 '"': '"', 534 "'": ''', 535 '`': '`' 536 }; 537 538 // Functions for escaping and unescaping strings to/from HTML interpolation. 539 var createEscaper = function (map) { 540 var escaper = function (match) { 541 return map[match]; 542 }; 543 // Regexes for identifying a key that needs to be escaped. 544 var source = '(?:' + Object.keys(map).join('|') + ')'; 545 var testRegexp = RegExp(source); 546 var replaceRegexp = RegExp(source, 'g'); 547 return function (string) { 548 string = string == null ? '' : '' + string; 549 return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; 550 }; 551 }; 552 553 var htmlEscape = createEscaper(escapeMap); 554 555 /** 556 * ------------------------------------------------------------------------ 557 * Constants 558 * ------------------------------------------------------------------------ 559 */ 560 561 var keyCodeMap = { 562 32: ' ', 563 48: '0', 564 49: '1', 565 50: '2', 566 51: '3', 567 52: '4', 568 53: '5', 569 54: '6', 570 55: '7', 571 56: '8', 572 57: '9', 573 59: ';', 574 65: 'A', 575 66: 'B', 576 67: 'C', 577 68: 'D', 578 69: 'E', 579 70: 'F', 580 71: 'G', 581 72: 'H', 582 73: 'I', 583 74: 'J', 584 75: 'K', 585 76: 'L', 586 77: 'M', 587 78: 'N', 588 79: 'O', 589 80: 'P', 590 81: 'Q', 591 82: 'R', 592 83: 'S', 593 84: 'T', 594 85: 'U', 595 86: 'V', 596 87: 'W', 597 88: 'X', 598 89: 'Y', 599 90: 'Z', 600 96: '0', 601 97: '1', 602 98: '2', 603 99: '3', 604 100: '4', 605 101: '5', 606 102: '6', 607 103: '7', 608 104: '8', 609 105: '9' 610 }; 611 612 var keyCodes = { 613 ESCAPE: 27, // KeyboardEvent.which value for Escape (Esc) key 614 ENTER: 13, // KeyboardEvent.which value for Enter key 615 SPACE: 32, // KeyboardEvent.which value for space key 616 TAB: 9, // KeyboardEvent.which value for tab key 617 ARROW_UP: 38, // KeyboardEvent.which value for up arrow key 618 ARROW_DOWN: 40 // KeyboardEvent.which value for down arrow key 619 } 620 621 var version = { 622 success: false, 623 major: '3' 624 }; 625 626 try { 627 version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); 628 version.major = version.full[0]; 629 version.success = true; 630 } catch (err) { 631 // do nothing 632 } 633 634 var selectId = 0; 635 636 var EVENT_KEY = '.bs.select'; 637 638 var classNames = { 639 DISABLED: 'disabled', 640 DIVIDER: 'divider', 641 SHOW: 'open', 642 DROPUP: 'dropup', 643 MENU: 'dropdown-menu', 644 MENURIGHT: 'dropdown-menu-right', 645 MENULEFT: 'dropdown-menu-left', 646 // to-do: replace with more advanced template/customization options 647 BUTTONCLASS: 'btn-default', 648 POPOVERHEADER: 'popover-title', 649 ICONBASE: 'glyphicon', 650 TICKICON: 'glyphicon-ok' 651 } 652 653 var Selector = { 654 MENU: '.' + classNames.MENU 655 } 656 657 var elementTemplates = { 658 span: document.createElement('span'), 659 i: document.createElement('i'), 660 subtext: document.createElement('small'), 661 a: document.createElement('a'), 662 li: document.createElement('li'), 663 whitespace: document.createTextNode('\u00A0'), 664 fragment: document.createDocumentFragment() 665 } 666 667 elementTemplates.a.setAttribute('role', 'option'); 668 elementTemplates.subtext.className = 'text-muted'; 669 670 elementTemplates.text = elementTemplates.span.cloneNode(false); 671 elementTemplates.text.className = 'text'; 672 673 elementTemplates.checkMark = elementTemplates.span.cloneNode(false); 674 675 var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN); 676 var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE); 677 678 var generateOption = { 679 li: function (content, classes, optgroup) { 680 var li = elementTemplates.li.cloneNode(false); 681 682 if (content) { 683 if (content.nodeType === 1 || content.nodeType === 11) { 684 li.appendChild(content); 685 } else { 686 li.innerHTML = content; 687 } 688 } 689 690 if (typeof classes !== 'undefined' && classes !== '') li.className = classes; 691 if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup); 692 693 return li; 694 }, 695 696 a: function (text, classes, inline) { 697 var a = elementTemplates.a.cloneNode(true); 698 699 if (text) { 700 if (text.nodeType === 11) { 701 a.appendChild(text); 702 } else { 703 a.insertAdjacentHTML('beforeend', text); 704 } 705 } 706 707 if (typeof classes !== 'undefined' && classes !== '') a.className = classes; 708 if (version.major === '4') a.classList.add('dropdown-item'); 709 if (inline) a.setAttribute('style', inline); 710 711 return a; 712 }, 713 714 text: function (options, useFragment) { 715 var textElement = elementTemplates.text.cloneNode(false), 716 subtextElement, 717 iconElement; 718 719 if (options.content) { 720 textElement.innerHTML = options.content; 721 } else { 722 textElement.textContent = options.text; 723 724 if (options.icon) { 725 var whitespace = elementTemplates.whitespace.cloneNode(false); 726 727 // need to use <i> for icons in the button to prevent a breaking change 728 // note: switch to span in next major release 729 iconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false); 730 iconElement.className = options.iconBase + ' ' + options.icon; 731 732 elementTemplates.fragment.appendChild(iconElement); 733 elementTemplates.fragment.appendChild(whitespace); 734 } 735 736 if (options.subtext) { 737 subtextElement = elementTemplates.subtext.cloneNode(false); 738 subtextElement.textContent = options.subtext; 739 textElement.appendChild(subtextElement); 740 } 741 } 742 743 if (useFragment === true) { 744 while (textElement.childNodes.length > 0) { 745 elementTemplates.fragment.appendChild(textElement.childNodes[0]); 746 } 747 } else { 748 elementTemplates.fragment.appendChild(textElement); 749 } 750 751 return elementTemplates.fragment; 752 }, 753 754 label: function (options) { 755 var textElement = elementTemplates.text.cloneNode(false), 756 subtextElement, 757 iconElement; 758 759 textElement.innerHTML = options.label; 760 761 if (options.icon) { 762 var whitespace = elementTemplates.whitespace.cloneNode(false); 763 764 iconElement = elementTemplates.span.cloneNode(false); 765 iconElement.className = options.iconBase + ' ' + options.icon; 766 767 elementTemplates.fragment.appendChild(iconElement); 768 elementTemplates.fragment.appendChild(whitespace); 769 } 770 771 if (options.subtext) { 772 subtextElement = elementTemplates.subtext.cloneNode(false); 773 subtextElement.textContent = options.subtext; 774 textElement.appendChild(subtextElement); 775 } 776 777 elementTemplates.fragment.appendChild(textElement); 778 779 return elementTemplates.fragment; 780 } 781 } 782 783 var Selectpicker = function (element, options) { 784 var that = this; 785 786 // bootstrap-select has been initialized - revert valHooks.select.set back to its original function 787 if (!valHooks.useDefault) { 788 $.valHooks.select.set = valHooks._set; 789 valHooks.useDefault = true; 790 } 791 792 this.$element = $(element); 793 this.$newElement = null; 794 this.$button = null; 795 this.$menu = null; 796 this.options = options; 797 this.selectpicker = { 798 main: {}, 799 current: {}, // current changes if a search is in progress 800 search: {}, 801 view: {}, 802 keydown: { 803 keyHistory: '', 804 resetKeyHistory: { 805 start: function () { 806 return setTimeout(function () { 807 that.selectpicker.keydown.keyHistory = ''; 808 }, 800); 809 } 810 } 811 } 812 }; 813 // 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 814 // data-attribute) 815 if (this.options.title === null) { 816 this.options.title = this.$element.attr('title'); 817 } 818 819 // Format window padding 820 var winPad = this.options.windowPadding; 821 if (typeof winPad === 'number') { 822 this.options.windowPadding = [winPad, winPad, winPad, winPad]; 823 } 824 825 // Expose public methods 826 this.val = Selectpicker.prototype.val; 827 this.render = Selectpicker.prototype.render; 828 this.refresh = Selectpicker.prototype.refresh; 829 this.setStyle = Selectpicker.prototype.setStyle; 830 this.selectAll = Selectpicker.prototype.selectAll; 831 this.deselectAll = Selectpicker.prototype.deselectAll; 832 this.destroy = Selectpicker.prototype.destroy; 833 this.remove = Selectpicker.prototype.remove; 834 this.show = Selectpicker.prototype.show; 835 this.hide = Selectpicker.prototype.hide; 836 837 this.init(); 838 }; 839 840 Selectpicker.VERSION = '1.13.9'; 841 842 // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. 843 Selectpicker.DEFAULTS = { 844 noneSelectedText: 'Nothing selected', 845 noneResultsText: 'No results matched {0}', 846 countSelectedText: function (numSelected, numTotal) { 847 return (numSelected == 1) ? '{0} item selected' : '{0} items selected'; 848 }, 849 maxOptionsText: function (numAll, numGroup) { 850 return [ 851 (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', 852 (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' 853 ]; 854 }, 855 selectAllText: 'Select All', 856 deselectAllText: 'Deselect All', 857 doneButton: false, 858 doneButtonText: 'Close', 859 multipleSeparator: ', ', 860 styleBase: 'btn', 861 style: classNames.BUTTONCLASS, 862 size: 'auto', 863 title: null, 864 selectedTextFormat: 'values', 865 width: false, 866 container: false, 867 hideDisabled: false, 868 showSubtext: false, 869 showIcon: true, 870 showContent: true, 871 dropupAuto: true, 872 header: false, 873 liveSearch: false, 874 liveSearchPlaceholder: null, 875 liveSearchNormalize: false, 876 liveSearchStyle: 'contains', 877 actionsBox: false, 878 iconBase: classNames.ICONBASE, 879 tickIcon: classNames.TICKICON, 880 showTick: false, 881 template: { 882 caret: '<span class="caret"></span>' 883 }, 884 maxOptions: false, 885 mobile: false, 886 selectOnTab: false, 887 dropdownAlignRight: false, 888 windowPadding: 0, 889 virtualScroll: 600, 890 display: false, 891 sanitize: true, 892 sanitizeFn: null, 893 whiteList: DefaultWhitelist 894 }; 895 896 Selectpicker.prototype = { 897 898 constructor: Selectpicker, 899 900 init: function () { 901 var that = this, 902 id = this.$element.attr('id'); 903 904 this.selectId = selectId++; 905 906 this.$element[0].classList.add('bs-select-hidden'); 907 908 this.multiple = this.$element.prop('multiple'); 909 this.autofocus = this.$element.prop('autofocus'); 910 this.options.showTick = this.$element[0].classList.contains('show-tick'); 911 912 this.$newElement = this.createDropdown(); 913 this.$element 914 .after(this.$newElement) 915 .prependTo(this.$newElement); 916 917 this.$button = this.$newElement.children('button'); 918 this.$menu = this.$newElement.children(Selector.MENU); 919 this.$menuInner = this.$menu.children('.inner'); 920 this.$searchbox = this.$menu.find('input'); 921 922 this.$element[0].classList.remove('bs-select-hidden'); 923 924 if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT); 925 926 if (typeof id !== 'undefined') { 927 this.$button.attr('data-id', id); 928 } 929 930 this.checkDisabled(); 931 this.clickListener(); 932 if (this.options.liveSearch) this.liveSearchListener(); 933 this.setStyle(); 934 this.render(); 935 this.setWidth(); 936 if (this.options.container) { 937 this.selectPosition(); 938 } else { 939 this.$element.on('hide' + EVENT_KEY, function () { 940 if (that.isVirtual()) { 941 // empty menu on close 942 var menuInner = that.$menuInner[0], 943 emptyMenu = menuInner.firstChild.cloneNode(false); 944 945 // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = '' 946 menuInner.replaceChild(emptyMenu, menuInner.firstChild); 947 menuInner.scrollTop = 0; 948 } 949 }); 950 } 951 this.$menu.data('this', this); 952 this.$newElement.data('this', this); 953 if (this.options.mobile) this.mobile(); 954 955 this.$newElement.on({ 956 'hide.bs.dropdown': function (e) { 957 that.$menuInner.attr('aria-expanded', false); 958 that.$element.trigger('hide' + EVENT_KEY, e); 959 }, 960 'hidden.bs.dropdown': function (e) { 961 that.$element.trigger('hidden' + EVENT_KEY, e); 962 }, 963 'show.bs.dropdown': function (e) { 964 that.$menuInner.attr('aria-expanded', true); 965 that.$element.trigger('show' + EVENT_KEY, e); 966 }, 967 'shown.bs.dropdown': function (e) { 968 that.$element.trigger('shown' + EVENT_KEY, e); 969 } 970 }); 971 972 if (that.$element[0].hasAttribute('required')) { 973 this.$element.on('invalid' + EVENT_KEY, function () { 974 that.$button[0].classList.add('bs-invalid'); 975 976 that.$element 977 .on('shown' + EVENT_KEY + '.invalid', function () { 978 that.$element 979 .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened 980 .off('shown' + EVENT_KEY + '.invalid'); 981 }) 982 .on('rendered' + EVENT_KEY, function () { 983 // if select is no longer invalid, remove the bs-invalid class 984 if (this.validity.valid) that.$button[0].classList.remove('bs-invalid'); 985 that.$element.off('rendered' + EVENT_KEY); 986 }); 987 988 that.$button.on('blur' + EVENT_KEY, function () { 989 that.$element.trigger('focus').trigger('blur'); 990 that.$button.off('blur' + EVENT_KEY); 991 }); 992 }); 993 } 994 995 setTimeout(function () { 996 that.createLi(); 997 that.$element.trigger('loaded' + EVENT_KEY); 998 }); 999 }, 1000 1001 createDropdown: function () { 1002 // Options 1003 // If we are multiple or showTick option is set, then add the show-tick class 1004 var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', 1005 inputGroup = '', 1006 autofocus = this.autofocus ? ' autofocus' : ''; 1007 1008 if (version.major < 4 && this.$element.parent().hasClass('input-group')) { 1009 inputGroup = ' input-group-btn'; 1010 } 1011 1012 // Elements 1013 var drop, 1014 header = '', 1015 searchbox = '', 1016 actionsbox = '', 1017 donebutton = ''; 1018 1019 if (this.options.header) { 1020 header = 1021 '<div class="' + classNames.POPOVERHEADER + '">' + 1022 '<button type="button" class="close" aria-hidden="true">×</button>' + 1023 this.options.header + 1024 '</div>'; 1025 } 1026 1027 if (this.options.liveSearch) { 1028 searchbox = 1029 '<div class="bs-searchbox">' + 1030 '<input type="text" class="form-control" autocomplete="off"' + 1031 ( 1032 this.options.liveSearchPlaceholder === null ? '' 1033 : 1034 ' placeholder="' + htmlEscape(this.options.liveSearchPlaceholder) + '"' 1035 ) + 1036 ' role="textbox" aria-label="Search">' + 1037 '</div>'; 1038 } 1039 1040 if (this.multiple && this.options.actionsBox) { 1041 actionsbox = 1042 '<div class="bs-actionsbox">' + 1043 '<div class="btn-group btn-group-sm btn-block">' + 1044 '<button type="button" class="actions-btn bs-select-all btn ' + classNames.BUTTONCLASS + '">' + 1045 this.options.selectAllText + 1046 '</button>' + 1047 '<button type="button" class="actions-btn bs-deselect-all btn ' + classNames.BUTTONCLASS + '">' + 1048 this.options.deselectAllText + 1049 '</button>' + 1050 '</div>' + 1051 '</div>'; 1052 } 1053 1054 if (this.multiple && this.options.doneButton) { 1055 donebutton = 1056 '<div class="bs-donebutton">' + 1057 '<div class="btn-group btn-block">' + 1058 '<button type="button" class="btn btn-sm ' + classNames.BUTTONCLASS + '">' + 1059 this.options.doneButtonText + 1060 '</button>' + 1061 '</div>' + 1062 '</div>'; 1063 } 1064 1065 drop = 1066 '<div class="dropdown bootstrap-select' + showTick + inputGroup + '">' + 1067 '<button type="button" class="' + this.options.styleBase + ' dropdown-toggle" ' + (this.options.display === 'static' ? 'data-display="static"' : '') + 'data-toggle="dropdown"' + autofocus + ' role="button">' + 1068 '<div class="filter-option">' + 1069 '<div class="filter-option-inner">' + 1070 '<div class="filter-option-inner-inner"></div>' + 1071 '</div> ' + 1072 '</div>' + 1073 ( 1074 version.major === '4' ? '' 1075 : 1076 '<span class="bs-caret">' + 1077 this.options.template.caret + 1078 '</span>' 1079 ) + 1080 '</button>' + 1081 '<div class="' + classNames.MENU + ' ' + (version.major === '4' ? '' : classNames.SHOW) + '" role="combobox">' + 1082 header + 1083 searchbox + 1084 actionsbox + 1085 '<div class="inner ' + classNames.SHOW + '" role="listbox" aria-expanded="false" tabindex="-1">' + 1086 '<ul class="' + classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : '') + '">' + 1087 '</ul>' + 1088 '</div>' + 1089 donebutton + 1090 '</div>' + 1091 '</div>'; 1092 1093 return $(drop); 1094 }, 1095 1096 setPositionData: function () { 1097 this.selectpicker.view.canHighlight = []; 1098 1099 for (var i = 0; i < this.selectpicker.current.data.length; i++) { 1100 var li = this.selectpicker.current.data[i], 1101 canHighlight = true; 1102 1103 if (li.type === 'divider') { 1104 canHighlight = false; 1105 li.height = this.sizeInfo.dividerHeight; 1106 } else if (li.type === 'optgroup-label') { 1107 canHighlight = false; 1108 li.height = this.sizeInfo.dropdownHeaderHeight; 1109 } else { 1110 li.height = this.sizeInfo.liHeight; 1111 } 1112 1113 if (li.disabled) canHighlight = false; 1114 1115 this.selectpicker.view.canHighlight.push(canHighlight); 1116 1117 li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height; 1118 } 1119 }, 1120 1121 isVirtual: function () { 1122 return (this.options.virtualScroll !== false) && (this.selectpicker.main.elements.length >= this.options.virtualScroll) || this.options.virtualScroll === true; 1123 }, 1124 1125 createView: function (isSearching, scrollTop) { 1126 scrollTop = scrollTop || 0; 1127 1128 var that = this; 1129 1130 this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main; 1131 1132 var active = []; 1133 var selected; 1134 var prevActive; 1135 1136 this.setPositionData(); 1137 1138 scroll(scrollTop, true); 1139 1140 this.$menuInner.off('scroll.createView').on('scroll.createView', function (e, updateValue) { 1141 if (!that.noScroll) scroll(this.scrollTop, updateValue); 1142 that.noScroll = false; 1143 }); 1144 1145 function scroll (scrollTop, init) { 1146 var size = that.selectpicker.current.elements.length, 1147 chunks = [], 1148 chunkSize, 1149 chunkCount, 1150 firstChunk, 1151 lastChunk, 1152 currentChunk, 1153 prevPositions, 1154 positionIsDifferent, 1155 previousElements, 1156 menuIsDifferent = true, 1157 isVirtual = that.isVirtual(); 1158 1159 that.selectpicker.view.scrollTop = scrollTop; 1160 1161 if (isVirtual === true) { 1162 // if an option that is encountered that is wider than the current menu width, update the menu width accordingly 1163 if (that.sizeInfo.hasScrollBar && that.$menu[0].offsetWidth > that.sizeInfo.totalMenuWidth) { 1164 that.sizeInfo.menuWidth = that.$menu[0].offsetWidth; 1165 that.sizeInfo.totalMenuWidth = that.sizeInfo.menuWidth + that.sizeInfo.scrollBarWidth; 1166 that.$menu.css('min-width', that.sizeInfo.menuWidth); 1167 } 1168 } 1169 1170 chunkSize = Math.ceil(that.sizeInfo.menuInnerHeight / that.sizeInfo.liHeight * 1.5); // number of options in a chunk 1171 chunkCount = Math.round(size / chunkSize) || 1; // number of chunks 1172 1173 for (var i = 0; i < chunkCount; i++) { 1174 var endOfChunk = (i + 1) * chunkSize; 1175 1176 if (i === chunkCount - 1) { 1177 endOfChunk = size; 1178 } 1179 1180 chunks[i] = [ 1181 (i) * chunkSize + (!i ? 0 : 1), 1182 endOfChunk 1183 ]; 1184 1185 if (!size) break; 1186 1187 if (currentChunk === undefined && scrollTop <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) { 1188 currentChunk = i; 1189 } 1190 } 1191 1192 if (currentChunk === undefined) currentChunk = 0; 1193 1194 prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1]; 1195 1196 // always display previous, current, and next chunks 1197 firstChunk = Math.max(0, currentChunk - 1); 1198 lastChunk = Math.min(chunkCount - 1, currentChunk + 1); 1199 1200 that.selectpicker.view.position0 = isVirtual === false ? 0 : (Math.max(0, chunks[firstChunk][0]) || 0); 1201 that.selectpicker.view.position1 = isVirtual === false ? size : (Math.min(size, chunks[lastChunk][1]) || 0); 1202 1203 positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1; 1204 1205 if (that.activeIndex !== undefined) { 1206 prevActive = that.selectpicker.main.elements[that.prevActiveIndex]; 1207 active = that.selectpicker.main.elements[that.activeIndex]; 1208 selected = that.selectpicker.main.elements[that.selectedIndex]; 1209 1210 if (init) { 1211 if (that.activeIndex !== that.selectedIndex && active && active.length) { 1212 active.classList.remove('active'); 1213 if (active.firstChild) active.firstChild.classList.remove('active'); 1214 } 1215 that.activeIndex = undefined; 1216 } 1217 1218 if (that.activeIndex && that.activeIndex !== that.selectedIndex && selected && selected.length) { 1219 selected.classList.remove('active'); 1220 if (selected.firstChild) selected.firstChild.classList.remove('active'); 1221 } 1222 } 1223 1224 if (that.prevActiveIndex !== undefined && that.prevActiveIndex !== that.activeIndex && that.prevActiveIndex !== that.selectedIndex && prevActive && prevActive.length) { 1225 prevActive.classList.remove('active'); 1226 if (prevActive.firstChild) prevActive.firstChild.classList.remove('active'); 1227 } 1228 1229 if (init || positionIsDifferent) { 1230 previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : []; 1231 1232 if (isVirtual === false) { 1233 that.selectpicker.view.visibleElements = that.selectpicker.current.elements; 1234 } else { 1235 that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1); 1236 } 1237 1238 that.setOptionStatus(); 1239 1240 // if searching, check to make sure the list has actually been updated before updating DOM 1241 // this prevents unnecessary repaints 1242 if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements); 1243 1244 // if virtual scroll is disabled and not searching, 1245 // menu should never need to be updated more than once 1246 if ((init || isVirtual === true) && menuIsDifferent) { 1247 var menuInner = that.$menuInner[0], 1248 menuFragment = document.createDocumentFragment(), 1249 emptyMenu = menuInner.firstChild.cloneNode(false), 1250 marginTop, 1251 marginBottom, 1252 elements = that.selectpicker.view.visibleElements, 1253 toSanitize = []; 1254 1255 // replace the existing UL with an empty one - this is faster than $.empty() 1256 menuInner.replaceChild(emptyMenu, menuInner.firstChild); 1257 1258 for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) { 1259 var element = elements[i], 1260 elText, 1261 elementData; 1262 1263 if (that.options.sanitize) { 1264 elText = element.lastChild; 1265 1266 if (elText) { 1267 elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0]; 1268 1269 if (elementData && elementData.content && !elementData.sanitized) { 1270 toSanitize.push(elText); 1271 elementData.sanitized = true; 1272 } 1273 } 1274 } 1275 1276 menuFragment.appendChild(element); 1277 } 1278 1279 if (that.options.sanitize && toSanitize.length) { 1280 sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn); 1281 } 1282 1283 if (isVirtual === true) { 1284 marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position); 1285 marginBottom = (that.selectpicker.view.position1 > size - 1 ? 0 : that.selectpicker.current.data[size - 1].position - that.selectpicker.current.data[that.selectpicker.view.position1 - 1].position); 1286 1287 menuInner.firstChild.style.marginTop = marginTop + 'px'; 1288 menuInner.firstChild.style.marginBottom = marginBottom + 'px'; 1289 } 1290 1291 menuInner.firstChild.appendChild(menuFragment); 1292 } 1293 } 1294 1295 that.prevActiveIndex = that.activeIndex; 1296 1297 if (!that.options.liveSearch) { 1298 that.$menuInner.trigger('focus'); 1299 } else if (isSearching && init) { 1300 var index = 0, 1301 newActive; 1302 1303 if (!that.selectpicker.view.canHighlight[index]) { 1304 index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true); 1305 } 1306 1307 newActive = that.selectpicker.view.visibleElements[index]; 1308 1309 if (that.selectpicker.view.currentActive) { 1310 that.selectpicker.view.currentActive.classList.remove('active'); 1311 if (that.selectpicker.view.currentActive.firstChild) that.selectpicker.view.currentActive.firstChild.classList.remove('active'); 1312 } 1313 1314 if (newActive) { 1315 newActive.classList.add('active'); 1316 if (newActive.firstChild) newActive.firstChild.classList.add('active'); 1317 } 1318 1319 that.activeIndex = (that.selectpicker.current.data[index] || {}).index; 1320 } 1321 } 1322 1323 $(window) 1324 .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView') 1325 .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function () { 1326 var isActive = that.$newElement.hasClass(classNames.SHOW); 1327 1328 if (isActive) scroll(that.$menuInner[0].scrollTop); 1329 }); 1330 }, 1331 1332 setPlaceholder: function () { 1333 var updateIndex = false;
1334 1335 if (this.options.title && !this.multiple) { 1336 if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option'); 1337 1338 // this option doesn't create a new <li> element, but does add a new option at the start, 1339 // so startIndex should increase to prevent having to check every option for the bs-title-option class 1340 updateIndex = true; 1341 1342 var element = this.$element[0], 1343 isSelected = false, 1344 titleNotAppended = !this.selectpicker.view.titleOption.parentNode; 1345 1346 if (titleNotAppended) { 1347 // Use native JS to prepend option (faster) 1348 this.selectpicker.view.titleOption.className = 'bs-title-option'; 1349 this.selectpicker.view.titleOption.value = ''; 1350 1351 // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. 1352 // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, 1353 // if so, the select will have the data-selected attribute 1354 var $opt = $(element.options[element.selectedIndex]); 1355 isSelected = $opt.attr('selected') === undefined && this.$element.data('selected') === undefined; 1356 } 1357 1358 if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) { 1359 element.insertBefore(this.selectpicker.view.titleOption, element.firstChild); 1360 } 1361 1362 // Set selected *after* appending to select, 1363 // otherwise the option doesn't get selected in IE 1364 // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11 1365 if (isSelected) element.selectedIndex = 0; 1366 } 1367 1368 return updateIndex; 1369 }, 1370 1371 createLi: function () { 1372 var that = this, 1373 iconBase = this.options.iconBase, 1374 optionSelector = ':not([hidden]):not([data-hidden="true"])', 1375 mainElements = [], 1376 mainData = [], 1377 widestOptionLength = 0, 1378 optID = 0, 1379 startIndex = this.setPlaceholder() ? 1 : 0; // append the titleOption if necessary and skip the first option in the loop 1380 1381 if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; 1382 1383 if ((that.options.showTick || that.multiple) && !elementTemplates.checkMark.parentNode) { 1384 elementTemplates.checkMark.className = iconBase + ' ' + that.options.tickIcon + ' check-mark'; 1385 elementTemplates.a.appendChild(elementTemplates.checkMark); 1386 } 1387 1388 var selectOptions = this.$element[0].querySelectorAll('select > *' + optionSelector); 1389 1390 function addDivider (config) { 1391 var previousData = mainData[mainData.length - 1]; 1392 1393 // ensure optgroup doesn't create back-to-back dividers 1394 if ( 1395 previousData && 1396 previousData.type === 'divider' && 1397 (previousData.optID || config.optID) 1398 ) { 1399 return; 1400 } 1401 1402 config = config || {}; 1403 config.type = 'divider'; 1404 1405 mainElements.push( 1406 generateOption.li( 1407 false, 1408 classNames.DIVIDER, 1409 (config.optID ? config.optID + 'div' : undefined) 1410 ) 1411 ); 1412 1413 mainData.push(config); 1414 } 1415 1416 function addOption (option, config) { 1417 config = config || {}; 1418 1419 config.divider = option.getAttribute('data-divider') === 'true'; 1420 1421 if (config.divider) { 1422 addDivider({ 1423 optID: config.optID 1424 }); 1425 } else { 1426 var liIndex = mainData.length, 1427 cssText = option.style.cssText, 1428 inlineStyle = cssText ? htmlEscape(cssText) : '', 1429 optionClass = (option.className || '') + (config.optgroupClass || ''); 1430 1431 if (config.optID) optionClass = 'opt ' + optionClass; 1432 1433 config.text = option.textContent; 1434 1435 config.content = option.getAttribute('data-content'); 1436 config.tokens = option.getAttribute('data-tokens'); 1437 config.subtext = option.getAttribute('data-subtext'); 1438 config.icon = option.getAttribute('data-icon'); 1439 config.iconBase = iconBase; 1440 1441 var textElement = generateOption.text(config); 1442 1443 mainElements.push( 1444 generateOption.li( 1445 generateOption.a( 1446 textElement, 1447 optionClass, 1448 inlineStyle 1449 ), 1450 '', 1451 config.optID 1452 ) 1453 ); 1454 1455 option.liIndex = liIndex; 1456 1457 config.display = config.content || config.text; 1458 config.type = 'option'; 1459 config.index = liIndex; 1460 config.option = option; 1461 config.disabled = config.disabled || option.disabled; 1462 1463 mainData.push(config); 1464 1465 var combinedLength = 0; 1466 1467 // count the number of characters in the option - not perfect, but should work in most cases 1468 if (config.display) combinedLength += config.display.length; 1469 if (config.subtext) combinedLength += config.subtext.length; 1470 // if there is an icon, ensure this option's width is checked
vendor: 32,678 bytes, lines 1471-2297
1471 if (config.icon) combinedLength += 1; 1472 1473 if (combinedLength > widestOptionLength) { 1474 widestOptionLength = combinedLength; 1475 1476 // guess which option is the widest 1477 // use this when calculating menu width 1478 // not perfect, but it's fast, and the width will be updating accordingly when scrolling 1479 that.selectpicker.view.widestOption = mainElements[mainElements.length - 1]; 1480 } 1481 } 1482 } 1483 1484 function addOptgroup (index, selectOptions) { 1485 var optgroup = selectOptions[index], 1486 previous = selectOptions[index - 1], 1487 next = selectOptions[index + 1], 1488 options = optgroup.querySelectorAll('option' + optionSelector); 1489 1490 if (!options.length) return; 1491 1492 var config = { 1493 label: htmlEscape(optgroup.label), 1494 subtext: optgroup.getAttribute('data-subtext'), 1495 icon: optgroup.getAttribute('data-icon'), 1496 iconBase: iconBase 1497 }, 1498 optgroupClass = ' ' + (optgroup.className || ''), 1499 headerIndex, 1500 lastIndex; 1501 1502 optID++; 1503 1504 if (previous) { 1505 addDivider({ optID: optID }); 1506 } 1507 1508 var labelElement = generateOption.label(config); 1509 1510 mainElements.push( 1511 generateOption.li(labelElement, 'dropdown-header' + optgroupClass, optID) 1512 ); 1513 1514 mainData.push({ 1515 display: config.label, 1516 subtext: config.subtext, 1517 type: 'optgroup-label', 1518 optID: optID 1519 }); 1520 1521 for (var j = 0, len = options.length; j < len; j++) { 1522 var option = options[j]; 1523 1524 if (j === 0) { 1525 headerIndex = mainData.length - 1; 1526 lastIndex = headerIndex + len; 1527 } 1528 1529 addOption(option, { 1530 headerIndex: headerIndex, 1531 lastIndex: lastIndex, 1532 optID: optID, 1533 optgroupClass: optgroupClass, 1534 disabled: optgroup.disabled 1535 }); 1536 } 1537 1538 if (next) { 1539 addDivider({ optID: optID }); 1540 } 1541 } 1542 1543 for (var len = selectOptions.length; startIndex < len; startIndex++) { 1544 var item = selectOptions[startIndex]; 1545 1546 if (item.tagName !== 'OPTGROUP') { 1547 addOption(item, {}); 1548 } else { 1549 addOptgroup(startIndex, selectOptions); 1550 } 1551 } 1552 1553 this.selectpicker.main.elements = mainElements; 1554 this.selectpicker.main.data = mainData; 1555 1556 this.selectpicker.current = this.selectpicker.main; 1557 }, 1558 1559 findLis: function () { 1560 return this.$menuInner.find('.inner > li'); 1561 }, 1562 1563 render: function () { 1564 // ensure titleOption is appended and selected (if necessary) before getting selectedOptions 1565 this.setPlaceholder(); 1566 1567 var that = this, 1568 selectedOptions = this.$element[0].selectedOptions, 1569 selectedCount = selectedOptions.length, 1570 button = this.$button[0], 1571 buttonInner = button.querySelector('.filter-option-inner-inner'), 1572 multipleSeparator = document.createTextNode(this.options.multipleSeparator), 1573 titleFragment = elementTemplates.fragment.cloneNode(false), 1574 showCount, 1575 countMax, 1576 hasContent = false; 1577 1578 this.togglePlaceholder(); 1579 1580 this.tabIndex(); 1581 1582 if (this.options.selectedTextFormat === 'static') { 1583 titleFragment = generateOption.text({ text: this.options.title }, true); 1584 } else { 1585 showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 1; 1586 1587 // determine if the number of selected options will be shown (showCount === true) 1588 if (showCount) { 1589 countMax = this.options.selectedTextFormat.split('>'); 1590 showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2); 1591 } 1592 1593 // only loop through all selected options if the count won't be shown 1594 if (showCount === false) { 1595 for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) { 1596 if (selectedIndex < 50) { 1597 var option = selectedOptions[selectedIndex], 1598 titleOptions = {}, 1599 thisData = { 1600 content: option.getAttribute('data-content'), 1601 subtext: option.getAttribute('data-subtext'), 1602 icon: option.getAttribute('data-icon') 1603 }; 1604 1605 if (this.multiple && selectedIndex > 0) { 1606 titleFragment.appendChild(multipleSeparator.cloneNode(false)); 1607 } 1608 1609 if (option.title) { 1610 titleOptions.text = option.title; 1611 } else if (thisData.content && that.options.showContent) { 1612 titleOptions.content = thisData.content.toString(); 1613 hasContent = true; 1614 } else { 1615 if (that.options.showIcon) { 1616 titleOptions.icon = thisData.icon; 1617 titleOptions.iconBase = this.options.iconBase; 1618 } 1619 if (that.options.showSubtext && !that.multiple && thisData.subtext) titleOptions.subtext = ' ' + thisData.subtext; 1620 titleOptions.text = option.textContent.trim(); 1621 } 1622 1623 titleFragment.appendChild(generateOption.text(titleOptions, true)); 1624 } else { 1625 break; 1626 } 1627 } 1628 1629 // add ellipsis 1630 if (selectedCount > 49) { 1631 titleFragment.appendChild(document.createTextNode('...')); 1632 } 1633 } else { 1634 var optionSelector = ':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])'; 1635 if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; 1636 1637 // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc. 1638 var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length, 1639 tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText; 1640 1641 titleFragment = generateOption.text({ 1642 text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString()) 1643 }, true); 1644 } 1645 } 1646 1647 if (this.options.title == undefined) { 1648 // use .attr to ensure undefined is returned if title attribute is not set 1649 this.options.title = this.$element.attr('title'); 1650 } 1651 1652 // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText 1653 if (!titleFragment.childNodes.length) { 1654 titleFragment = generateOption.text({ 1655 text: typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText 1656 }, true); 1657 } 1658 1659 // strip all HTML tags and trim the result, then unescape any escaped tags 1660 button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim(); 1661 1662 if (this.options.sanitize && hasContent) { 1663 sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn); 1664 } 1665 1666 buttonInner.innerHTML = ''; 1667 buttonInner.appendChild(titleFragment); 1668 1669 if (version.major < 4 && this.$newElement[0].classList.contains('bs3-has-addon')) { 1670 var filterExpand = button.querySelector('.filter-expand'), 1671 clone = buttonInner.cloneNode(true); 1672 1673 clone.className = 'filter-expand'; 1674 1675 if (filterExpand) { 1676 button.replaceChild(clone, filterExpand); 1677 } else { 1678 button.appendChild(clone); 1679 } 1680 } 1681 1682 this.$element.trigger('rendered' + EVENT_KEY); 1683 }, 1684 1685 /** 1686 * @param [style] 1687 * @param [status] 1688 */ 1689 setStyle: function (newStyle, status) { 1690 var button = this.$button[0], 1691 newElement = this.$newElement[0], 1692 style = this.options.style.trim(), 1693 buttonClass; 1694 1695 if (this.$element.attr('class')) { 1696 this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); 1697 } 1698 1699 if (version.major < 4) { 1700 newElement.classList.add('bs3'); 1701 1702 if (newElement.parentNode.classList.contains('input-group') && 1703 (newElement.previousElementSibling || newElement.nextElementSibling) && 1704 (newElement.previousElementSibling || newElement.nextElementSibling).classList.contains('input-group-addon') 1705 ) { 1706 newElement.classList.add('bs3-has-addon'); 1707 } 1708 } 1709 1710 if (newStyle) { 1711 buttonClass = newStyle.trim(); 1712 } else { 1713 buttonClass = style; 1714 } 1715 1716 if (status == 'add') { 1717 if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' ')); 1718 } else if (status == 'remove') { 1719 if (buttonClass) button.classList.remove.apply(button.classList, buttonClass.split(' ')); 1720 } else { 1721 if (style) button.classList.remove.apply(button.classList, style.split(' ')); 1722 if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' ')); 1723 } 1724 }, 1725 1726 liHeight: function (refresh) { 1727 if (!refresh && (this.options.size === false || this.sizeInfo)) return; 1728 1729 if (!this.sizeInfo) this.sizeInfo = {}; 1730 1731 var newElement = document.createElement('div'), 1732 menu = document.createElement('div'), 1733 menuInner = document.createElement('div'), 1734 menuInnerInner = document.createElement('ul'), 1735 divider = document.createElement('li'), 1736 dropdownHeader = document.createElement('li'), 1737 li = document.createElement('li'), 1738 a = document.createElement('a'), 1739 text = document.createElement('span'), 1740 header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null, 1741 search = this.options.liveSearch ? document.createElement('div') : null, 1742 actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, 1743 doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null, 1744 firstOption = this.$element.find('option')[0]; 1745 1746 this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth; 1747 1748 text.className = 'text'; 1749 a.className = 'dropdown-item ' + (firstOption ? firstOption.className : ''); 1750 newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW; 1751 newElement.style.width = this.sizeInfo.selectWidth + 'px'; 1752 if (this.options.width === 'auto') menu.style.minWidth = 0; 1753 menu.className = classNames.MENU + ' ' + classNames.SHOW; 1754 menuInner.className = 'inner ' + classNames.SHOW; 1755 menuInnerInner.className = classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : ''); 1756 divider.className = classNames.DIVIDER; 1757 dropdownHeader.className = 'dropdown-header'; 1758 1759 text.appendChild(document.createTextNode('\u200b')); 1760 a.appendChild(text); 1761 li.appendChild(a); 1762 dropdownHeader.appendChild(text.cloneNode(true)); 1763 1764 if (this.selectpicker.view.widestOption) { 1765 menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true)); 1766 } 1767 1768 menuInnerInner.appendChild(li); 1769 menuInnerInner.appendChild(divider); 1770 menuInnerInner.appendChild(dropdownHeader); 1771 if (header) menu.appendChild(header); 1772 if (search) { 1773 var input = document.createElement('input'); 1774 search.className = 'bs-searchbox'; 1775 input.className = 'form-control'; 1776 search.appendChild(input); 1777 menu.appendChild(search); 1778 } 1779 if (actions) menu.appendChild(actions); 1780 menuInner.appendChild(menuInnerInner); 1781 menu.appendChild(menuInner); 1782 if (doneButton) menu.appendChild(doneButton); 1783 newElement.appendChild(menu); 1784 1785 document.body.appendChild(newElement); 1786 1787 var liHeight = li.offsetHeight, 1788 dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0, 1789 headerHeight = header ? header.offsetHeight : 0, 1790 searchHeight = search ? search.offsetHeight : 0, 1791 actionsHeight = actions ? actions.offsetHeight : 0, 1792 doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, 1793 dividerHeight = $(divider).outerHeight(true), 1794 // fall back to jQuery if getComputedStyle is not supported 1795 menuStyle = window.getComputedStyle ? window.getComputedStyle(menu) : false, 1796 menuWidth = menu.offsetWidth, 1797 $menu = menuStyle ? null : $(menu), 1798 menuPadding = { 1799 vert: toInteger(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + 1800 toInteger(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + 1801 toInteger(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + 1802 toInteger(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), 1803 horiz: toInteger(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + 1804 toInteger(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + 1805 toInteger(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + 1806 toInteger(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) 1807 }, 1808 menuExtras = { 1809 vert: menuPadding.vert + 1810 toInteger(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + 1811 toInteger(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, 1812 horiz: menuPadding.horiz + 1813 toInteger(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + 1814 toInteger(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 1815 }, 1816 scrollBarWidth; 1817 1818 menuInner.style.overflowY = 'scroll'; 1819 1820 scrollBarWidth = menu.offsetWidth - menuWidth; 1821 1822 document.body.removeChild(newElement); 1823 1824 this.sizeInfo.liHeight = liHeight; 1825 this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight; 1826 this.sizeInfo.headerHeight = headerHeight; 1827 this.sizeInfo.searchHeight = searchHeight; 1828 this.sizeInfo.actionsHeight = actionsHeight; 1829 this.sizeInfo.doneButtonHeight = doneButtonHeight; 1830 this.sizeInfo.dividerHeight = dividerHeight; 1831 this.sizeInfo.menuPadding = menuPadding; 1832 this.sizeInfo.menuExtras = menuExtras; 1833 this.sizeInfo.menuWidth = menuWidth; 1834 this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth; 1835 this.sizeInfo.scrollBarWidth = scrollBarWidth; 1836 this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight; 1837 1838 this.setPositionData(); 1839 }, 1840 1841 getSelectPosition: function () { 1842 var that = this, 1843 $window = $(window), 1844 pos = that.$newElement.offset(), 1845 $container = $(that.options.container), 1846 containerPos; 1847 1848 if (that.options.container && $container.length && !$container.is('body')) { 1849 containerPos = $container.offset(); 1850 containerPos.top += parseInt($container.css('borderTopWidth')); 1851 containerPos.left += parseInt($container.css('borderLeftWidth')); 1852 } else { 1853 containerPos = { top: 0, left: 0 }; 1854 } 1855 1856 var winPad = that.options.windowPadding; 1857 1858 this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); 1859 this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2]; 1860 this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); 1861 this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1]; 1862 this.sizeInfo.selectOffsetTop -= winPad[0]; 1863 this.sizeInfo.selectOffsetLeft -= winPad[3]; 1864 }, 1865 1866 setMenuSize: function (isAuto) { 1867 this.getSelectPosition(); 1868 1869 var selectWidth = this.sizeInfo.selectWidth, 1870 liHeight = this.sizeInfo.liHeight, 1871 headerHeight = this.sizeInfo.headerHeight, 1872 searchHeight = this.sizeInfo.searchHeight, 1873 actionsHeight = this.sizeInfo.actionsHeight, 1874 doneButtonHeight = this.sizeInfo.doneButtonHeight, 1875 divHeight = this.sizeInfo.dividerHeight, 1876 menuPadding = this.sizeInfo.menuPadding, 1877 menuInnerHeight, 1878 menuHeight, 1879 divLength = 0, 1880 minHeight, 1881 _minHeight, 1882 maxHeight, 1883 menuInnerMinHeight, 1884 estimate; 1885 1886 if (this.options.dropupAuto) { 1887 // Get the estimated height of the menu without scrollbars. 1888 // This is useful for smaller menus, where there might be plenty of room 1889 // below the button without setting dropup, but we can't know 1890 // the exact height of the menu until createView is called later 1891 estimate = liHeight * this.selectpicker.current.elements.length + menuPadding.vert; 1892 this.$newElement.toggleClass(classNames.DROPUP, this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot); 1893 } 1894 1895 if (this.options.size === 'auto') { 1896 _minHeight = this.selectpicker.current.elements.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0; 1897 menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert; 1898 minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; 1899 menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0); 1900 1901 if (this.$newElement.hasClass(classNames.DROPUP)) { 1902 menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert; 1903 } 1904 1905 maxHeight = menuHeight; 1906 menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert; 1907 } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { 1908 for (var i = 0; i < this.options.size; i++) { 1909 if (this.selectpicker.current.data[i].type === 'divider') divLength++; 1910 } 1911 1912 menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; 1913 menuInnerHeight = menuHeight - menuPadding.vert; 1914 maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; 1915 minHeight = menuInnerMinHeight = ''; 1916 } 1917 1918 if (this.options.dropdownAlignRight === 'auto') { 1919 this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth)); 1920 } 1921 1922 this.$menu.css({ 1923 'max-height': maxHeight + 'px', 1924 'overflow': 'hidden', 1925 'min-height': minHeight + 'px' 1926 }); 1927 1928 this.$menuInner.css({ 1929 'max-height': menuInnerHeight + 'px', 1930 'overflow-y': 'auto', 1931 'min-height': menuInnerMinHeight + 'px' 1932 }); 1933 1934 // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView 1935 this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1); 1936 1937 if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) { 1938 this.sizeInfo.hasScrollBar = true; 1939 this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth; 1940 1941 this.$menu.css('min-width', this.sizeInfo.totalMenuWidth); 1942 } 1943 1944 if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update(); 1945 }, 1946 1947 setSize: function (refresh) { 1948 this.liHeight(refresh); 1949 1950 if (this.options.header) this.$menu.css('padding-top', 0); 1951 if (this.options.size === false) return; 1952 1953 var that = this, 1954 $window = $(window), 1955 selectedIndex, 1956 offset = 0; 1957 1958 this.setMenuSize(); 1959 1960 if (this.options.liveSearch) { 1961 this.$searchbox 1962 .off('input.setMenuSize propertychange.setMenuSize') 1963 .on('input.setMenuSize propertychange.setMenuSize', function () { 1964 return that.setMenuSize(); 1965 }); 1966 } 1967 1968 if (this.options.size === 'auto') { 1969 $window 1970 .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize') 1971 .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function () { 1972 return that.setMenuSize(); 1973 }); 1974 } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { 1975 $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize'); 1976 } 1977 1978 if (refresh) { 1979 offset = this.$menuInner[0].scrollTop; 1980 } else if (!that.multiple) { 1981 var element = that.$element[0]; 1982 selectedIndex = (element.options[element.selectedIndex] || {}).liIndex; 1983 1984 if (typeof selectedIndex === 'number' && that.options.size !== false) { 1985 offset = that.sizeInfo.liHeight * selectedIndex; 1986 offset = offset - (that.sizeInfo.menuInnerHeight / 2) + (that.sizeInfo.liHeight / 2); 1987 } 1988 } 1989 1990 that.createView(false, offset); 1991 }, 1992 1993 setWidth: function () { 1994 var that = this; 1995 1996 if (this.options.width === 'auto') { 1997 requestAnimationFrame(function () { 1998 that.$menu.css('min-width', '0'); 1999 2000 that.$element.on('loaded' + EVENT_KEY, function () { 2001 that.liHeight(); 2002 that.setMenuSize(); 2003 2004 // Get correct width if element is hidden 2005 var $selectClone = that.$newElement.clone().appendTo('body'), 2006 btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth(); 2007 2008 $selectClone.remove(); 2009 2010 // Set width to whatever's larger, button title or longest option 2011 that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth); 2012 that.$newElement.css('width', that.sizeInfo.selectWidth + 'px'); 2013 }); 2014 }); 2015 } else if (this.options.width === 'fit') { 2016 // Remove inline min-width so width can be changed from 'auto' 2017 this.$menu.css('min-width', ''); 2018 this.$newElement.css('width', '').addClass('fit-width'); 2019 } else if (this.options.width) { 2020 // Remove inline min-width so width can be changed from 'auto' 2021 this.$menu.css('min-width', ''); 2022 this.$newElement.css('width', this.options.width); 2023 } else { 2024 // Remove inline min-width/width so width can be changed 2025 this.$menu.css('min-width', ''); 2026 this.$newElement.css('width', ''); 2027 } 2028 // Remove fit-width class if width is changed programmatically 2029 if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { 2030 this.$newElement[0].classList.remove('fit-width'); 2031 } 2032 }, 2033 2034 selectPosition: function () { 2035 this.$bsContainer = $('<div class="bs-container" />'); 2036 2037 var that = this, 2038 $container = $(this.options.container), 2039 pos, 2040 containerPos, 2041 actualHeight, 2042 getPlacement = function ($element) { 2043 var containerPosition = {}, 2044 // fall back to dropdown's default display setting if display is not manually set 2045 display = that.options.display || ( 2046 // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default 2047 $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display 2048 : false 2049 ); 2050 2051 that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP)); 2052 pos = $element.offset(); 2053 2054 if (!$container.is('body')) { 2055 containerPos = $container.offset(); 2056 containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); 2057 containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); 2058 } else { 2059 containerPos = { top: 0, left: 0 }; 2060 } 2061 2062 actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight; 2063 2064 // Bootstrap 4+ uses Popper for menu positioning 2065 if (version.major < 4 || display === 'static') { 2066 containerPosition.top = pos.top - containerPos.top + actualHeight; 2067 containerPosition.left = pos.left - containerPos.left; 2068 } 2069 2070 containerPosition.width = $element[0].offsetWidth; 2071 2072 that.$bsContainer.css(containerPosition); 2073 }; 2074 2075 this.$button.on('click.bs.dropdown.data-api', function () { 2076 if (that.isDisabled()) { 2077 return; 2078 } 2079 2080 getPlacement(that.$newElement); 2081 2082 that.$bsContainer 2083 .appendTo(that.options.container) 2084 .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW)) 2085 .append(that.$menu); 2086 }); 2087 2088 $(window) 2089 .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId) 2090 .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function () { 2091 var isActive = that.$newElement.hasClass(classNames.SHOW); 2092 2093 if (isActive) getPlacement(that.$newElement); 2094 }); 2095 2096 this.$element.on('hide' + EVENT_KEY, function () { 2097 that.$menu.data('height', that.$menu.height()); 2098 that.$bsContainer.detach(); 2099 }); 2100 }, 2101 2102 setOptionStatus: function () { 2103 var that = this; 2104 2105 that.noScroll = false; 2106 2107 if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) { 2108 for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) { 2109 var liData = that.selectpicker.current.data[i + that.selectpicker.view.position0], 2110 option = liData.option; 2111 2112 if (option) { 2113 that.setDisabled( 2114 liData.index, 2115 liData.disabled 2116 ); 2117 2118 that.setSelected( 2119 liData.index, 2120 option.selected 2121 ); 2122 } 2123 } 2124 } 2125 }, 2126 2127 /** 2128 * @param {number} index - the index of the option that is being changed 2129 * @param {boolean} selected - true if the option is being selected, false if being deselected 2130 */ 2131 setSelected: function (index, selected) { 2132 var li = this.selectpicker.main.elements[index], 2133 liData = this.selectpicker.main.data[index], 2134 activeIndexIsSet = this.activeIndex !== undefined, 2135 thisIsActive = this.activeIndex === index, 2136 prevActive, 2137 a, 2138 // if current option is already active 2139 // OR 2140 // if the current option is being selected, it's NOT multiple, and 2141 // activeIndex is undefined: 2142 // - when the menu is first being opened, OR 2143 // - after a search has been performed, OR 2144 // - when retainActive is false when selecting a new option (i.e. index of the newly selected option is not the same as the current activeIndex) 2145 keepActive = thisIsActive || (selected && !this.multiple && !activeIndexIsSet); 2146 2147 liData.selected = selected; 2148 2149 a = li.firstChild; 2150 2151 if (selected) { 2152 this.selectedIndex = index; 2153 } 2154 2155 li.classList.toggle('selected', selected); 2156 li.classList.toggle('active', keepActive); 2157 2158 if (keepActive) { 2159 this.selectpicker.view.currentActive = li; 2160 this.activeIndex = index; 2161 } 2162 2163 if (a) { 2164 a.classList.toggle('selected', selected); 2165 a.classList.toggle('active', keepActive); 2166 a.setAttribute('aria-selected', selected); 2167 } 2168 2169 if (!keepActive) { 2170 if (!activeIndexIsSet && selected && this.prevActiveIndex !== undefined) { 2171 prevActive = this.selectpicker.main.elements[this.prevActiveIndex]; 2172 2173 prevActive.classList.remove('active'); 2174 if (prevActive.firstChild) { 2175 prevActive.firstChild.classList.remove('active'); 2176 } 2177 } 2178 } 2179 }, 2180 2181 /** 2182 * @param {number} index - the index of the option that is being disabled 2183 * @param {boolean} disabled - true if the option is being disabled, false if being enabled 2184 */ 2185 setDisabled: function (index, disabled) { 2186 var li = this.selectpicker.main.elements[index], 2187 a; 2188 2189 this.selectpicker.main.data[index].disabled = disabled; 2190 2191 a = li.firstChild; 2192 2193 li.classList.toggle(classNames.DISABLED, disabled); 2194 2195 if (a) { 2196 if (version.major === '4') a.classList.toggle(classNames.DISABLED, disabled); 2197 2198 a.setAttribute('aria-disabled', disabled); 2199 2200 if (disabled) { 2201 a.setAttribute('tabindex', -1); 2202 } else { 2203 a.setAttribute('tabindex', 0); 2204 } 2205 } 2206 }, 2207 2208 isDisabled: function () { 2209 return this.$element[0].disabled; 2210 }, 2211 2212 checkDisabled: function () { 2213 var that = this; 2214 2215 if (this.isDisabled()) { 2216 this.$newElement[0].classList.add(classNames.DISABLED); 2217 this.$button.addClass(classNames.DISABLED).attr('tabindex', -1).attr('aria-disabled', true); 2218 } else { 2219 if (this.$button[0].classList.contains(classNames.DISABLED)) { 2220 this.$newElement[0].classList.remove(classNames.DISABLED); 2221 this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false); 2222 } 2223 2224 if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { 2225 this.$button.removeAttr('tabindex'); 2226 } 2227 } 2228 2229 this.$button.on('click', function () { 2230 return !that.isDisabled(); 2231 }); 2232 }, 2233 2234 togglePlaceholder: function () { 2235 // much faster than calling $.val() 2236 var element = this.$element[0], 2237 selectedIndex = element.selectedIndex, 2238 nothingSelected = selectedIndex === -1; 2239 2240 if (!nothingSelected && !element.options[selectedIndex].value) nothingSelected = true; 2241 2242 this.$button.toggleClass('bs-placeholder', nothingSelected); 2243 }, 2244 2245 tabIndex: function () { 2246 if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && 2247 (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { 2248 this.$element.data('tabindex', this.$element.attr('tabindex')); 2249 this.$button.attr('tabindex', this.$element.data('tabindex')); 2250 } 2251 2252 this.$element.attr('tabindex', -98); 2253 }, 2254 2255 clickListener: function () { 2256 var that = this, 2257 $document = $(document); 2258 2259 $document.data('spaceSelect', false); 2260 2261 this.$button.on('keyup', function (e) { 2262 if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { 2263 e.preventDefault(); 2264 $document.data('spaceSelect', false); 2265 } 2266 }); 2267 2268 this.$newElement.on('show.bs.dropdown', function () { 2269 if (version.major > 3 && !that.dropdown) { 2270 that.dropdown = that.$button.data('bs.dropdown'); 2271 that.dropdown._menu = that.$menu[0]; 2272 } 2273 }); 2274 2275 this.$button.on('click.bs.dropdown.data-api', function () { 2276 if (!that.$newElement.hasClass(classNames.SHOW)) { 2277 that.setSize(); 2278 } 2279 }); 2280 2281 function setFocus () { 2282 if (that.options.liveSearch) { 2283 that.$searchbox.trigger('focus'); 2284 } else { 2285 that.$menuInner.trigger('focus'); 2286 } 2287 } 2288 2289 function checkPopperExists () { 2290 if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state.isCreated) { 2291 setFocus(); 2292 } else { 2293 requestAnimationFrame(checkPopperExists); 2294 } 2295 } 2296 2297 this.$element.on('shown' + EVENT_KEY, function () {
2298 if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) { 2299 that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop; 2300 } 2301 2302 if (version.major > 3) { 2303 requestAnimationFrame(checkPopperExists); 2304 } else { 2305 setFocus(); 2306 } 2307 }); 2308 2309 this.$menuInner.on('click', 'li a', function (e, retainActive) { 2310 var $this = $(this), 2311 position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0, 2312 clickedData = that.selectpicker.current.data[$this.parent().index() + position0], 2313 clickedIndex = clickedData.index, 2314 prevValue = getSelectValues(that.$element[0]), 2315 prevIndex = that.$element.prop('selectedIndex'), 2316 triggerChange = true; 2317 2318 // Don't close on multi choice menu 2319 if (that.multiple && that.options.maxOptions !== 1) { 2320 e.stopPropagation(); 2321 } 2322 2323 e.preventDefault(); 2324 2325 // Don't run if the select is disabled 2326 if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) { 2327 var $options = that.$element.find('option'), 2328 option = clickedData.option, 2329 $option = $(option), 2330 state = option.selected, 2331 $optgroup = $option.parent('optgroup'), 2332 $optgroupOptions = $optgroup.find('option'), 2333 maxOptions = that.options.maxOptions, 2334 maxOptionsGrp = $optgroup.data('maxOptions') || false; 2335 2336 if (clickedIndex === that.activeIndex) retainActive = true; 2337 2338 if (!retainActive) { 2339 that.prevActiveIndex = that.activeIndex; 2340 that.activeIndex = undefined; 2341 } 2342 2343 if (!that.multiple) { // Deselect all others if not multi select box 2344 $options.prop('selected', false); 2345 option.selected = true; 2346 that.setSelected(clickedIndex, true); 2347 } else { // Toggle the one we have chosen if we are multi select. 2348 option.selected = !state; 2349 2350 that.setSelected(clickedIndex, !state); 2351 $this.trigger('blur'); 2352 2353 if (maxOptions !== false || maxOptionsGrp !== false) { 2354 var maxReached = maxOptions < $options.filter(':selected').length, 2355 maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; 2356 2357 if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { 2358 if (maxOptions && maxOptions == 1) { 2359 $options.prop('selected', false); 2360 $option.prop('selected', true); 2361 2362 for (var i = 0; i < $options.length; i++) { 2363 that.setSelected(i, false); 2364 } 2365 2366 that.setSelected(clickedIndex, true); 2367 } else if (maxOptionsGrp && maxOptionsGrp == 1) { 2368 $optgroup.find('option:selected').prop('selected', false); 2369 $option.prop('selected', true); 2370 2371 for (var i = 0; i < $optgroupOptions.length; i++) { 2372 var option = $optgroupOptions[i]; 2373 that.setSelected($options.index(option), false); 2374 } 2375 2376 that.setSelected(clickedIndex, true); 2377 } else { 2378 var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, 2379 maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, 2380 maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), 2381 maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), 2382 $notify = $('<div class="notify"></div>'); 2383 // If {var} is set in array, replace it 2384 /** @deprecated */ 2385 if (maxOptionsArr[2]) { 2386 maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); 2387 maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); 2388 } 2389 2390 $option.prop('selected', false); 2391 2392 that.$menu.append($notify); 2393 2394 if (maxOptions && maxReached) { 2395 $notify.append($('<div>' + maxTxt + '</div>')); 2396 triggerChange = false;
vendor: 5,130 bytes, lines 2397-2549
2397 that.$element.trigger('maxReached' + EVENT_KEY); 2398 } 2399 2400 if (maxOptionsGrp && maxReachedGrp) { 2401 $notify.append($('<div>' + maxTxtGrp + '</div>')); 2402 triggerChange = false; 2403 that.$element.trigger('maxReachedGrp' + EVENT_KEY); 2404 } 2405 2406 setTimeout(function () { 2407 that.setSelected(clickedIndex, false); 2408 }, 10); 2409 2410 $notify.delay(750).fadeOut(300, function () { 2411 $(this).remove(); 2412 }); 2413 } 2414 } 2415 } 2416 } 2417 2418 if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { 2419 that.$button.trigger('focus'); 2420 } else if (that.options.liveSearch) { 2421 that.$searchbox.trigger('focus'); 2422 } 2423 2424 // Trigger select 'change' 2425 if (triggerChange) { 2426 if ((prevValue != getSelectValues(that.$element[0]) && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { 2427 // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed. 2428 changedArguments = [option.index, $option.prop('selected'), prevValue]; 2429 that.$element 2430 .triggerNative('change'); 2431 } 2432 } 2433 } 2434 }); 2435 2436 this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function (e) { 2437 if (e.currentTarget == this) { 2438 e.preventDefault(); 2439 e.stopPropagation(); 2440 if (that.options.liveSearch && !$(e.target).hasClass('close')) { 2441 that.$searchbox.trigger('focus'); 2442 } else { 2443 that.$button.trigger('focus'); 2444 } 2445 } 2446 }); 2447 2448 this.$menuInner.on('click', '.divider, .dropdown-header', function (e) { 2449 e.preventDefault(); 2450 e.stopPropagation(); 2451 if (that.options.liveSearch) { 2452 that.$searchbox.trigger('focus'); 2453 } else { 2454 that.$button.trigger('focus'); 2455 } 2456 }); 2457 2458 this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function () { 2459 that.$button.trigger('click'); 2460 }); 2461 2462 this.$searchbox.on('click', function (e) { 2463 e.stopPropagation(); 2464 }); 2465 2466 this.$menu.on('click', '.actions-btn', function (e) { 2467 if (that.options.liveSearch) { 2468 that.$searchbox.trigger('focus'); 2469 } else { 2470 that.$button.trigger('focus'); 2471 } 2472 2473 e.preventDefault(); 2474 e.stopPropagation(); 2475 2476 if ($(this).hasClass('bs-select-all')) { 2477 that.selectAll(); 2478 } else { 2479 that.deselectAll(); 2480 } 2481 }); 2482 2483 this.$element 2484 .on('change' + EVENT_KEY, function () { 2485 that.render(); 2486 that.$element.trigger('changed' + EVENT_KEY, changedArguments); 2487 changedArguments = null; 2488 }) 2489 .on('focus' + EVENT_KEY, function () { 2490 if (!that.options.mobile) that.$button.trigger('focus'); 2491 }); 2492 }, 2493 2494 liveSearchListener: function () { 2495 var that = this, 2496 noResults = document.createElement('li'); 2497 2498 this.$button.on('click.bs.dropdown.data-api', function () { 2499 if (!!that.$searchbox.val()) { 2500 that.$searchbox.val(''); 2501 } 2502 }); 2503 2504 this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function (e) { 2505 e.stopPropagation(); 2506 }); 2507 2508 this.$searchbox.on('input propertychange', function () { 2509 var searchValue = that.$searchbox.val(); 2510 2511 that.selectpicker.search.elements = []; 2512 that.selectpicker.search.data = []; 2513 2514 if (searchValue) { 2515 var i, 2516 searchMatch = [], 2517 q = searchValue.toUpperCase(), 2518 cache = {}, 2519 cacheArr = [], 2520 searchStyle = that._searchStyle(), 2521 normalizeSearch = that.options.liveSearchNormalize; 2522 2523 if (normalizeSearch) q = normalizeToBase(q); 2524 2525 that._$lisSelected = that.$menuInner.find('.selected'); 2526 2527 for (var i = 0; i < that.selectpicker.main.data.length; i++) { 2528 var li = that.selectpicker.main.data[i]; 2529 2530 if (!cache[i]) { 2531 cache[i] = stringSearch(li, q, searchStyle, normalizeSearch); 2532 } 2533 2534 if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) { 2535 if (li.headerIndex > 0) { 2536 cache[li.headerIndex - 1] = true; 2537 cacheArr.push(li.headerIndex - 1); 2538 } 2539 2540 cache[li.headerIndex] = true; 2541 cacheArr.push(li.headerIndex); 2542 2543 cache[li.lastIndex + 1] = true; 2544 } 2545 2546 if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i); 2547 } 2548 2549 for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) {
2550 var index = cacheArr[i], 2551 prevIndex = cacheArr[i - 1], 2552 li = that.selectpicker.main.data[index], 2553 liPrev = that.selectpicker.main.data[prevIndex]; 2554 2555 if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) { 2556 that.selectpicker.search.data.push(li); 2557 searchMatch.push(that.selectpicker.main.elements[index]); 2558 } 2559 } 2560 2561 that.activeIndex = undefined; 2562 that.noScroll = true; 2563 that.$menuInner.scrollTop(0); 2564 that.selectpicker.search.elements = searchMatch; 2565 that.createView(true); 2566 2567 if (!searchMatch.length) { 2568 noResults.className = 'no-results'; 2569 noResults.innerHTML = that.options.noneResultsText.replace('{0}', '"' + htmlEscape(searchValue) + '"'); 2570 that.$menuInner[0].firstChild.appendChild(noResults); 2571 } 2572 } else { 2573 that.$menuInner.scrollTop(0); 2574 that.createView(false); 2575 } 2576 }); 2577 }, 2578 2579 _searchStyle: function () { 2580 return this.options.liveSearchStyle || 'contains'; 2581 }, 2582 2583 val: function (value) { 2584 if (typeof value !== 'undefined') { 2585 var prevValue = getSelectValues(this.$element[0]); 2586 2587 changedArguments = [null, null, prevValue]; 2588 2589 this.$element 2590 .val(value) 2591 .trigger('changed' + EVENT_KEY, changedArguments); 2592 2593 this.render(); 2594 2595 changedArguments = null; 2596 2597 return this.$element; 2598 } else { 2599 return this.$element.val(); 2600 } 2601 }, 2602 2603 changeAll: function (status) { 2604 if (!this.multiple) return; 2605 if (typeof status === 'undefined') status = true; 2606 2607 var element = this.$element[0], 2608 previousSelected = 0, 2609 currentSelected = 0, 2610 prevValue = getSelectValues(element); 2611 2612 element.classList.add('bs-select-hidden'); 2613 2614 for (var i = 0, len = this.selectpicker.current.elements.length; i < len; i++) { 2615 var liData = this.selectpicker.current.data[i], 2616 option = liData.option; 2617 2618 if (option && !liData.disabled && liData.type !== 'divider') { 2619 if (liData.selected) previousSelected++; 2620 option.selected = status; 2621 if (status) currentSelected++; 2622 } 2623 } 2624 2625 element.classList.remove('bs-select-hidden'); 2626 2627 if (previousSelected === currentSelected) return; 2628 2629 this.setOptionStatus(); 2630 2631 this.togglePlaceholder(); 2632 2633 changedArguments = [null, null, prevValue]; 2634 2635 this.$element 2636 .triggerNative('change'); 2637 }, 2638 2639 selectAll: function () { 2640 return this.changeAll(true); 2641 }, 2642 2643 deselectAll: function () { 2644 return this.changeAll(false); 2645 }, 2646 2647 toggle: function (e) { 2648 e = e || window.event; 2649 2650 if (e) e.stopPropagation(); 2651 2652 this.$button.trigger('click.bs.dropdown.data-api'); 2653 }, 2654 2655 keydown: function (e) { 2656 var $this = $(this), 2657 isToggle = $this.hasClass('dropdown-toggle'), 2658 $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU), 2659 that = $parent.data('this'), 2660 $items = that.findLis(), 2661 index, 2662 isActive, 2663 liActive, 2664 activeLi, 2665 offset, 2666 updateScroll = false, 2667 downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab, 2668 isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab, 2669 scrollTop = that.$menuInner[0].scrollTop, 2670 isVirtual = that.isVirtual(), 2671 position0 = isVirtual === true ? that.selectpicker.view.position0 : 0; 2672 2673 isActive = that.$newElement.hasClass(classNames.SHOW); 2674 2675 if ( 2676 !isActive && 2677 ( 2678 isArrowKey || 2679 (e.which >= 48 && e.which <= 57) || 2680 (e.which >= 96 && e.which <= 105) || 2681 (e.which >= 65 && e.which <= 90) 2682 ) 2683 ) { 2684 that.$button.trigger('click.bs.dropdown.data-api'); 2685 2686 if (that.options.liveSearch) { 2687 that.$searchbox.trigger('focus'); 2688 return; 2689 } 2690 } 2691 2692 if (e.which === keyCodes.ESCAPE && isActive) { 2693 e.preventDefault(); 2694 that.$button.trigger('click.bs.dropdown.data-api').trigger('focus'); 2695 } 2696 2697 if (isArrowKey) { // if up or down 2698 if (!$items.length) return; 2699 2700 // $items.index/.filter is too slow with a large list and no virtual scroll 2701 index = isVirtual === true ? $items.index($items.filter('.active')) : that.activeIndex; 2702 2703 if (index === undefined) index = -1; 2704 2705 if (index !== -1) { 2706 liActive = that.selectpicker.current.elements[index + position0]; 2707 liActive.classList.remove('active'); 2708 if (liActive.firstChild) liActive.firstChild.classList.remove('active'); 2709 } 2710 2711 if (e.which === keyCodes.ARROW_UP) { // up 2712 if (index !== -1) index--; 2713 if (index + position0 < 0) index += $items.length; 2714 2715 if (!that.selectpicker.view.canHighlight[index + position0]) { 2716 index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0; 2717 if (index === -1) index = $items.length - 1; 2718 } 2719 } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down 2720 index++; 2721 if (index + position0 >
vendor: 11,544 bytes, lines 2721-3061
2721= that.selectpicker.view.canHighlight.length) index = 0; 2722 2723 if (!that.selectpicker.view.canHighlight[index + position0]) { 2724 index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true); 2725 } 2726 } 2727 2728 e.preventDefault(); 2729 2730 var liActiveIndex = position0 + index; 2731 2732 if (e.which === keyCodes.ARROW_UP) { // up 2733 // scroll to bottom and highlight last option 2734 if (position0 === 0 && index === $items.length - 1) { 2735 that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight; 2736 2737 liActiveIndex = that.selectpicker.current.elements.length - 1; 2738 } else { 2739 activeLi = that.selectpicker.current.data[liActiveIndex]; 2740 offset = activeLi.position - activeLi.height; 2741 2742 updateScroll = offset < scrollTop; 2743 } 2744 } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down 2745 // scroll to top and highlight first option 2746 if (index === 0) { 2747 that.$menuInner[0].scrollTop = 0; 2748 2749 liActiveIndex = 0; 2750 } else { 2751 activeLi = that.selectpicker.current.data[liActiveIndex]; 2752 offset = activeLi.position - that.sizeInfo.menuInnerHeight; 2753 2754 updateScroll = offset > scrollTop; 2755 } 2756 } 2757 2758 liActive = that.selectpicker.current.elements[liActiveIndex]; 2759 2760 if (liActive) { 2761 liActive.classList.add('active'); 2762 if (liActive.firstChild) liActive.firstChild.classList.add('active'); 2763 } 2764 2765 that.activeIndex = that.selectpicker.current.data[liActiveIndex].index; 2766 2767 that.selectpicker.view.currentActive = liActive; 2768 2769 if (updateScroll) that.$menuInner[0].scrollTop = offset; 2770 2771 if (that.options.liveSearch) { 2772 that.$searchbox.trigger('focus'); 2773 } else { 2774 $this.trigger('focus'); 2775 } 2776 } else if ( 2777 (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) || 2778 (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory) 2779 ) { 2780 var searchMatch, 2781 matches = [], 2782 keyHistory; 2783 2784 e.preventDefault(); 2785 2786 that.selectpicker.keydown.keyHistory += keyCodeMap[e.which]; 2787 2788 if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel); 2789 that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start(); 2790 2791 keyHistory = that.selectpicker.keydown.keyHistory; 2792 2793 // if all letters are the same, set keyHistory to just the first character when searching 2794 if (/^(.)\1+$/.test(keyHistory)) { 2795 keyHistory = keyHistory.charAt(0); 2796 } 2797 2798 // find matches 2799 for (var i = 0; i < that.selectpicker.current.data.length; i++) { 2800 var li = that.selectpicker.current.data[i], 2801 hasMatch; 2802 2803 hasMatch = stringSearch(li, keyHistory, 'startsWith', true); 2804 2805 if (hasMatch && that.selectpicker.view.canHighlight[i]) { 2806 matches.push(li.index); 2807 } 2808 } 2809 2810 if (matches.length) { 2811 var matchIndex = 0; 2812 2813 $items.removeClass('active').find('a').removeClass('active'); 2814 2815 // either only one key has been pressed or they are all the same key 2816 if (keyHistory.length === 1) { 2817 matchIndex = matches.indexOf(that.activeIndex); 2818 2819 if (matchIndex === -1 || matchIndex === matches.length - 1) { 2820 matchIndex = 0; 2821 } else { 2822 matchIndex++; 2823 } 2824 } 2825 2826 searchMatch = matches[matchIndex]; 2827 2828 activeLi = that.selectpicker.main.data[searchMatch]; 2829 2830 if (scrollTop - activeLi.position > 0) { 2831 offset = activeLi.position - activeLi.height; 2832 updateScroll = true; 2833 } else { 2834 offset = activeLi.position - that.sizeInfo.menuInnerHeight; 2835 // if the option is already visible at the current scroll position, just keep it the same 2836 updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight; 2837 } 2838 2839 liActive = that.selectpicker.main.elements[searchMatch]; 2840 liActive.classList.add('active'); 2841 if (liActive.firstChild) liActive.firstChild.classList.add('active'); 2842 that.activeIndex = matches[matchIndex]; 2843 2844 liActive.firstChild.focus(); 2845 2846 if (updateScroll) that.$menuInner[0].scrollTop = offset; 2847 2848 $this.trigger('focus'); 2849 } 2850 } 2851 2852 // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. 2853 if ( 2854 isActive && 2855 ( 2856 (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) || 2857 e.which === keyCodes.ENTER || 2858 (e.which === keyCodes.TAB && that.options.selectOnTab) 2859 ) 2860 ) { 2861 if (e.which !== keyCodes.SPACE) e.preventDefault(); 2862 2863 if (!that.options.liveSearch || e.which !== keyCodes.SPACE) { 2864 that.$menuInner.find('.active a').trigger('click', true); // retain active class 2865 $this.trigger('focus'); 2866 2867 if (!that.options.liveSearch) { 2868 // Prevent screen from scrolling if the user hits the spacebar 2869 e.preventDefault(); 2870 // Fixes spacebar selection of dropdown items in FF & IE 2871 $(document).data('spaceSelect', true); 2872 } 2873 } 2874 } 2875 }, 2876 2877 mobile: function () { 2878 this.$element[0].classList.add('mobile-device'); 2879 }, 2880 2881 refresh: function () { 2882 // update options if data attributes have been changed 2883 var config = $.extend({}, this.options, this.$element.data()); 2884 this.options = config; 2885 2886 this.checkDisabled(); 2887 this.setStyle(); 2888 this.render(); 2889 this.createLi(); 2890 this.setWidth(); 2891 2892 this.setSize(true); 2893 2894 this.$element.trigger('refreshed' + EVENT_KEY); 2895 }, 2896 2897 hide: function () { 2898 this.$newElement.hide(); 2899 }, 2900 2901 show: function () { 2902 this.$newElement.show(); 2903 }, 2904 2905 remove: function () { 2906 this.$newElement.remove(); 2907 this.$element.remove(); 2908 }, 2909 2910 destroy: function () { 2911 this.$newElement.before(this.$element).remove(); 2912 2913 if (this.$bsContainer) { 2914 this.$bsContainer.remove(); 2915 } else { 2916 this.$menu.remove(); 2917 } 2918 2919 this.$element 2920 .off(EVENT_KEY) 2921 .removeData('selectpicker') 2922 .removeClass('bs-select-hidden selectpicker'); 2923 2924 $(window).off(EVENT_KEY + '.' + this.selectId); 2925 } 2926 }; 2927 2928 // SELECTPICKER PLUGIN DEFINITION 2929 // ============================== 2930 function Plugin (option) { 2931 // get the args of the outer function.. 2932 var args = arguments; 2933 // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them 2934 // to get lost/corrupted in android 2.3 and IE9 #715 #775 2935 var _option = option; 2936 2937 [].shift.apply(args); 2938 2939 // if the version was not set successfully 2940 if (!version.success) { 2941 // try to retreive it again 2942 try { 2943 version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); 2944 } catch (err) { 2945 // fall back to use BootstrapVersion if set 2946 if (Selectpicker.BootstrapVersion) { 2947 version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.'); 2948 } else { 2949 version.full = [version.major, '0', '0']; 2950 2951 console.warn( 2952 'There was an issue retrieving Bootstrap\'s version. ' + 2953 'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' + 2954 'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.', 2955 err 2956 ); 2957 } 2958 } 2959 2960 version.major = version.full[0]; 2961 version.success = true; 2962 } 2963 2964 if (version.major === '4') { 2965 // some defaults need to be changed if using Bootstrap 4 2966 // check to see if they have already been manually changed before forcing them to update 2967 var toUpdate = []; 2968 2969 if (Selectpicker.DEFAULTS.style === classNames.BUTTONCLASS) toUpdate.push({ name: 'style', className: 'BUTTONCLASS' }); 2970 if (Selectpicker.DEFAULTS.iconBase === classNames.ICONBASE) toUpdate.push({ name: 'iconBase', className: 'ICONBASE' }); 2971 if (Selectpicker.DEFAULTS.tickIcon === classNames.TICKICON) toUpdate.push({ name: 'tickIcon', className: 'TICKICON' }); 2972 2973 classNames.DIVIDER = 'dropdown-divider'; 2974 classNames.SHOW = 'show'; 2975 classNames.BUTTONCLASS = 'btn-light'; 2976 classNames.POPOVERHEADER = 'popover-header'; 2977 classNames.ICONBASE = ''; 2978 classNames.TICKICON = 'bs-ok-default'; 2979 2980 for (var i = 0; i < toUpdate.length; i++) { 2981 var option = toUpdate[i]; 2982 Selectpicker.DEFAULTS[option.name] = classNames[option.className]; 2983 } 2984 } 2985 2986 var value; 2987 var chain = this.each(function () { 2988 var $this = $(this); 2989 if ($this.is('select')) { 2990 var data = $this.data('selectpicker'), 2991 options = typeof _option == 'object' && _option; 2992 2993 if (!data) { 2994 var dataAttributes = $this.data(); 2995 2996 for (var dataAttr in dataAttributes) { 2997 if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) { 2998 delete dataAttributes[dataAttr]; 2999 } 3000 } 3001 3002 var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, dataAttributes, options); 3003 config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template); 3004 $this.data('selectpicker', (data = new Selectpicker(this, config))); 3005 } else if (options) { 3006 for (var i in options) { 3007 if (options.hasOwnProperty(i)) { 3008 data.options[i] = options[i]; 3009 } 3010 } 3011 } 3012 3013 if (typeof _option == 'string') { 3014 if (data[_option] instanceof Function) { 3015 value = data[_option].apply(data, args); 3016 } else { 3017 value = data.options[_option]; 3018 } 3019 } 3020 } 3021 }); 3022 3023 if (typeof value !== 'undefined') { 3024 // noinspection JSUnusedAssignment 3025 return value; 3026 } else { 3027 return chain; 3028 } 3029 } 3030 3031 var old = $.fn.selectpicker; 3032 $.fn.selectpicker = Plugin; 3033 $.fn.selectpicker.Constructor = Selectpicker; 3034 3035 // SELECTPICKER NO CONFLICT 3036 // ======================== 3037 $.fn.selectpicker.noConflict = function () { 3038 $.fn.selectpicker = old; 3039 return this; 3040 }; 3041 3042 $(document) 3043 .off('keydown.bs.dropdown.data-api') 3044 .on('keydown' + EVENT_KEY, '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown) 3045 .on('focusin.modal', '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', function (e) { 3046 e.stopPropagation(); 3047 }); 3048 3049 // SELECTPICKER DATA-API 3050 // ===================== 3051 $(window).on('load' + EVENT_KEY + '.data-api', function () { 3052 $('.selectpicker').each(function () { 3053 var $selectpicker = $(this); 3054 Plugin.call($selectpicker, $selectpicker.data()); 3055 }) 3056 }); 3057})(jQuery); 3058 3059 3060})); 3061//# sourceMappingURL=bootstrap-select.js.map
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.