vendor: 35,823 bytes, lines 1-1125
1/*! 2 * Bootstrap-select v1.13.7 (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.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 'content', 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 === 'content') { 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 console.warn( 632 'There was an issue retrieving Bootstrap\'s version. ' + 633 'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' + 634 'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.', 635 err 636 ); 637 } 638 639 var selectId = 0; 640 641 var EVENT_KEY = '.bs.select'; 642 643 var classNames = { 644 DISABLED: 'disabled', 645 DIVIDER: 'divider', 646 SHOW: 'open', 647 DROPUP: 'dropup', 648 MENU: 'dropdown-menu', 649 MENURIGHT: 'dropdown-menu-right', 650 MENULEFT: 'dropdown-menu-left', 651 // to-do: replace with more advanced template/customization options 652 BUTTONCLASS: 'btn-default', 653 POPOVERHEADER: 'popover-title' 654 } 655 656 var Selector = { 657 MENU: '.' + classNames.MENU 658 } 659 660 if (version.major === '4') { 661 classNames.DIVIDER = 'dropdown-divider'; 662 classNames.SHOW = 'show'; 663 classNames.BUTTONCLASS = 'btn-light'; 664 classNames.POPOVERHEADER = 'popover-header'; 665 } 666 667 var elementTemplates = { 668 span: document.createElement('span'), 669 i: document.createElement('i'), 670 subtext: document.createElement('small'), 671 a: document.createElement('a'), 672 li: document.createElement('li'), 673 whitespace: document.createTextNode('\u00A0'), 674 fragment: document.createDocumentFragment() 675 } 676 677 elementTemplates.a.setAttribute('role', 'option'); 678 elementTemplates.subtext.className = 'text-muted'; 679 680 elementTemplates.text = elementTemplates.span.cloneNode(false); 681 elementTemplates.text.className = 'text'; 682 683 var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN); 684 var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE); 685 686 var generateOption = { 687 li: function (content, classes, optgroup) { 688 var li = elementTemplates.li.cloneNode(false); 689 690 if (content) { 691 if (content.nodeType === 1 || content.nodeType === 11) { 692 li.appendChild(content); 693 } else { 694 li.innerHTML = content; 695 } 696 } 697 698 if (typeof classes !== 'undefined' && classes !== '') li.className = classes; 699 if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup); 700 701 return li; 702 }, 703 704 a: function (text, classes, inline) { 705 var a = elementTemplates.a.cloneNode(true); 706 707 if (text) { 708 if (text.nodeType === 11) { 709 a.appendChild(text); 710 } else { 711 a.insertAdjacentHTML('beforeend', text); 712 } 713 } 714 715 if (typeof classes !== 'undefined' && classes !== '') a.className = classes; 716 if (version.major === '4') a.classList.add('dropdown-item'); 717 if (inline) a.setAttribute('style', inline); 718 719 return a; 720 }, 721 722 text: function (options, useFragment) { 723 var textElement = elementTemplates.text.cloneNode(false), 724 optionSubtextElement, 725 optionIconElement; 726 727 if (options.optionContent) { 728 textElement.innerHTML = options.optionContent; 729 } else { 730 textElement.textContent = options.text; 731 732 if (options.optionIcon) { 733 var whitespace = elementTemplates.whitespace.cloneNode(false); 734 735 // need to use <i> for icons in the button to prevent a breaking change 736 // note: switch to span in next major release 737 optionIconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false); 738 optionIconElement.className = options.iconBase + ' ' + options.optionIcon; 739 740 elementTemplates.fragment.appendChild(optionIconElement); 741 elementTemplates.fragment.appendChild(whitespace); 742 } 743 744 if (options.optionSubtext) { 745 optionSubtextElement = elementTemplates.subtext.cloneNode(false); 746 optionSubtextElement.textContent = options.optionSubtext; 747 textElement.appendChild(optionSubtextElement); 748 } 749 } 750 751 if (useFragment === true) { 752 while (textElement.childNodes.length > 0) { 753 elementTemplates.fragment.appendChild(textElement.childNodes[0]); 754 } 755 } else { 756 elementTemplates.fragment.appendChild(textElement); 757 } 758 759 return elementTemplates.fragment; 760 }, 761 762 label: function (options) { 763 var labelTextElement = elementTemplates.text.cloneNode(false), 764 labelSubtextElement, 765 labelIconElement; 766 767 labelTextElement.innerHTML = options.labelEscaped; 768 769 if (options.labelIcon) { 770 var whitespace = elementTemplates.whitespace.cloneNode(false); 771 772 labelIconElement = elementTemplates.span.cloneNode(false); 773 labelIconElement.className = options.iconBase + ' ' + options.labelIcon; 774 775 elementTemplates.fragment.appendChild(labelIconElement); 776 elementTemplates.fragment.appendChild(whitespace); 777 } 778 779 if (options.labelSubtext) { 780 labelSubtextElement = elementTemplates.subtext.cloneNode(false); 781 labelSubtextElement.textContent = options.labelSubtext; 782 labelTextElement.appendChild(labelSubtextElement); 783 } 784 785 elementTemplates.fragment.appendChild(labelTextElement); 786 787 return elementTemplates.fragment; 788 } 789 } 790 791 var Selectpicker = function (element, options) { 792 var that = this; 793 794 // bootstrap-select has been initialized - revert valHooks.select.set back to its original function 795 if (!valHooks.useDefault) { 796 $.valHooks.select.set = valHooks._set; 797 valHooks.useDefault = true; 798 } 799 800 this.$element = $(element); 801 this.$newElement = null; 802 this.$button = null; 803 this.$menu = null; 804 this.options = options; 805 this.selectpicker = { 806 main: { 807 // store originalIndex (key) and newIndex (value) in this.selectpicker.main.map.newIndex for fast accessibility 808 // allows us to do this.main.elements[this.selectpicker.main.map.newIndex[index]] to select an element based on the originalIndex 809 map: { 810 newIndex: {}, 811 originalIndex: {} 812 } 813 }, 814 current: { 815 map: {} 816 }, // current changes if a search is in progress 817 search: { 818 map: {} 819 }, 820 view: {}, 821 keydown: { 822 keyHistory: '', 823 resetKeyHistory: { 824 start: function () { 825 return setTimeout(function () { 826 that.selectpicker.keydown.keyHistory = ''; 827 }, 800); 828 } 829 } 830 } 831 }; 832 // 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 833 // data-attribute) 834 if (this.options.title === null) { 835 this.options.title = this.$element.attr('title'); 836 } 837 838 // Format window padding 839 var winPad = this.options.windowPadding; 840 if (typeof winPad === 'number') { 841 this.options.windowPadding = [winPad, winPad, winPad, winPad]; 842 } 843 844 // Expose public methods 845 this.val = Selectpicker.prototype.val; 846 this.render = Selectpicker.prototype.render; 847 this.refresh = Selectpicker.prototype.refresh; 848 this.setStyle = Selectpicker.prototype.setStyle; 849 this.selectAll = Selectpicker.prototype.selectAll; 850 this.deselectAll = Selectpicker.prototype.deselectAll; 851 this.destroy = Selectpicker.prototype.destroy; 852 this.remove = Selectpicker.prototype.remove; 853 this.show = Selectpicker.prototype.show; 854 this.hide = Selectpicker.prototype.hide; 855 856 this.init(); 857 }; 858 859 Selectpicker.VERSION = '1.13.7'; 860 861 Selectpicker.BootstrapVersion = version.major; 862 863 // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. 864 Selectpicker.DEFAULTS = { 865 noneSelectedText: 'Nothing selected', 866 noneResultsText: 'No results matched {0}', 867 countSelectedText: function (numSelected, numTotal) { 868 return (numSelected == 1) ? '{0} item selected' : '{0} items selected'; 869 }, 870 maxOptionsText: function (numAll, numGroup) { 871 return [ 872 (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', 873 (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' 874 ]; 875 }, 876 selectAllText: 'Select All', 877 deselectAllText: 'Deselect All', 878 doneButton: false, 879 doneButtonText: 'Close', 880 multipleSeparator: ', ', 881 styleBase: 'btn', 882 style: classNames.BUTTONCLASS, 883 size: 'auto', 884 title: null, 885 selectedTextFormat: 'values', 886 width: false, 887 container: false, 888 hideDisabled: false, 889 showSubtext: false, 890 showIcon: true, 891 showContent: true, 892 dropupAuto: true, 893 header: false, 894 liveSearch: false, 895 liveSearchPlaceholder: null, 896 liveSearchNormalize: false, 897 liveSearchStyle: 'contains', 898 actionsBox: false, 899 iconBase: 'glyphicon', 900 tickIcon: 'glyphicon-ok', 901 showTick: false, 902 template: { 903 caret: '<span class="caret"></span>' 904 }, 905 maxOptions: false, 906 mobile: false, 907 selectOnTab: false, 908 dropdownAlignRight: false, 909 windowPadding: 0, 910 virtualScroll: 600, 911 display: false, 912 sanitize: true, 913 sanitizeFn: null, 914 whiteList: DefaultWhitelist 915 }; 916 917 if (version.major === '4') { 918 Selectpicker.DEFAULTS.style = 'btn-light'; 919 Selectpicker.DEFAULTS.iconBase = ''; 920 Selectpicker.DEFAULTS.tickIcon = 'bs-ok-default'; 921 } 922 923 Selectpicker.prototype = { 924 925 constructor: Selectpicker, 926 927 init: function () { 928 var that = this, 929 id = this.$element.attr('id'); 930 931 this.selectId = selectId++; 932 933 this.$element[0].classList.add('bs-select-hidden'); 934 935 this.multiple = this.$element.prop('multiple'); 936 this.autofocus = this.$element.prop('autofocus'); 937 938 this.$newElement = this.createDropdown(); 939 this.$element 940 .after(this.$newElement) 941 .prependTo(this.$newElement); 942 943 this.$button = this.$newElement.children('button'); 944 this.$menu = this.$newElement.children(Selector.MENU); 945 this.$menuInner = this.$menu.children('.inner'); 946 this.$searchbox = this.$menu.find('input'); 947 948 this.$element[0].classList.remove('bs-select-hidden'); 949 950 if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT); 951 952 if (typeof id !== 'undefined') { 953 this.$button.attr('data-id', id); 954 } 955 956 this.checkDisabled(); 957 this.clickListener(); 958 if (this.options.liveSearch) this.liveSearchListener(); 959 this.setStyle(); 960 this.render(); 961 this.setWidth(); 962 if (this.options.container) { 963 this.selectPosition(); 964 } else { 965 this.$element.on('hide' + EVENT_KEY, function () { 966 if (that.isVirtual()) { 967 // empty menu on close 968 var menuInner = that.$menuInner[0], 969 emptyMenu = menuInner.firstChild.cloneNode(false); 970 971 // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = '' 972 menuInner.replaceChild(emptyMenu, menuInner.firstChild); 973 menuInner.scrollTop = 0; 974 } 975 }); 976 } 977 this.$menu.data('this', this); 978 this.$newElement.data('this', this); 979 if (this.options.mobile) this.mobile(); 980 981 this.$newElement.on({ 982 'hide.bs.dropdown': function (e) { 983 that.$menuInner.attr('aria-expanded', false); 984 that.$element.trigger('hide' + EVENT_KEY, e); 985 }, 986 'hidden.bs.dropdown': function (e) { 987 that.$element.trigger('hidden' + EVENT_KEY, e); 988 }, 989 'show.bs.dropdown': function (e) { 990 that.$menuInner.attr('aria-expanded', true); 991 that.$element.trigger('show' + EVENT_KEY, e); 992 }, 993 'shown.bs.dropdown': function (e) { 994 that.$element.trigger('shown' + EVENT_KEY, e); 995 } 996 }); 997 998 if (that.$element[0].hasAttribute('required')) { 999 this.$element.on('invalid', function () { 1000 that.$button[0].classList.add('bs-invalid'); 1001 1002 that.$element 1003 .on('shown' + EVENT_KEY + '.invalid', function () { 1004 that.$element 1005 .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened 1006 .off('shown' + EVENT_KEY + '.invalid'); 1007 }) 1008 .on('rendered' + EVENT_KEY, function () { 1009 // if select is no longer invalid, remove the bs-invalid class 1010 if (this.validity.valid) that.$button[0].classList.remove('bs-invalid'); 1011 that.$element.off('rendered' + EVENT_KEY); 1012 }); 1013 1014 that.$button.on('blur' + EVENT_KEY, function () { 1015 that.$element.trigger('focus').trigger('blur'); 1016 that.$button.off('blur' + EVENT_KEY); 1017 }); 1018 }); 1019 } 1020 1021 setTimeout(function () { 1022 that.createLi(); 1023 that.$element.trigger('loaded' + EVENT_KEY); 1024 }); 1025 }, 1026 1027 createDropdown: function () { 1028 // Options 1029 // If we are multiple or showTick option is set, then add the show-tick class 1030 var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', 1031 autofocus = this.autofocus ? ' autofocus' : ''; 1032 1033 // Elements 1034 var drop, 1035 header = '', 1036 searchbox = '', 1037 actionsbox = '', 1038 donebutton = ''; 1039 1040 if (this.options.header) { 1041 header = 1042 '<div class="' + classNames.POPOVERHEADER + '">' + 1043 '<button type="button" class="close" aria-hidden="true">×</button>' + 1044 this.options.header + 1045 '</div>'; 1046 } 1047 1048 if (this.options.liveSearch) { 1049 searchbox = 1050 '<div class="bs-searchbox">' + 1051 '<input type="text" class="form-control" autocomplete="off"' + 1052 ( 1053 this.options.liveSearchPlaceholder === null ? '' 1054 : 1055 ' placeholder="' + htmlEscape(this.options.liveSearchPlaceholder) + '"' 1056 ) + 1057 ' role="textbox" aria-label="Search">' + 1058 '</div>'; 1059 } 1060 1061 if (this.multiple && this.options.actionsBox) { 1062 actionsbox = 1063 '<div class="bs-actionsbox">' + 1064 '<div class="btn-group btn-group-sm btn-block">' + 1065 '<button type="button" class="actions-btn bs-select-all btn ' + classNames.BUTTONCLASS + '">' + 1066 this.options.selectAllText + 1067 '</button>' + 1068 '<button type="button" class="actions-btn bs-deselect-all btn ' + classNames.BUTTONCLASS + '">' + 1069 this.options.deselectAllText + 1070 '</button>' + 1071 '</div>' + 1072 '</div>'; 1073 } 1074 1075 if (this.multiple && this.options.doneButton) { 1076 donebutton = 1077 '<div class="bs-donebutton">' + 1078 '<div class="btn-group btn-block">' + 1079 '<button type="button" class="btn btn-sm ' + classNames.BUTTONCLASS + '">' + 1080 this.options.doneButtonText + 1081 '</button>' + 1082 '</div>' + 1083 '</div>'; 1084 } 1085 1086 drop = 1087 '<div class="dropdown bootstrap-select' + showTick + '">' + 1088 '<button type="button" class="' + this.options.styleBase + ' dropdown-toggle" ' + (this.options.display === 'static' ? 'data-display="static"' : '') + 'data-toggle="dropdown"' + autofocus + ' role="button">' + 1089 '<div class="filter-option">' + 1090 '<div class="filter-option-inner">' + 1091 '<div class="filter-option-inner-inner"></div>' + 1092 '</div> ' + 1093 '</div>' + 1094 ( 1095 version.major === '4' ? '' 1096 : 1097 '<span class="bs-caret">' + 1098 this.options.template.caret + 1099 '</span>' 1100 ) + 1101 '</button>' + 1102 '<div class="' + classNames.MENU + ' ' + (version.major === '4' ? '' : classNames.SHOW) + '" role="combobox">' + 1103 header + 1104 searchbox + 1105 actionsbox + 1106 '<div class="inner ' + classNames.SHOW + '" role="listbox" aria-expanded="false" tabindex="-1">' + 1107 '<ul class="' + classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : '') + '">' + 1108 '</ul>' + 1109 '</div>' + 1110 donebutton + 1111 '</div>' + 1112 '</div>'; 1113 1114 return $(drop); 1115 }, 1116 1117 setPositionData: function () { 1118 this.selectpicker.view.canHighlight = []; 1119 1120 for (var i = 0; i < this.selectpicker.current.data.length; i++) { 1121 var li = this.selectpicker.current.data[i], 1122 canHighlight = true; 1123 1124 if (li.type === 'divider') { 1125 canHighlight = false;
1126 li.height = this.sizeInfo.dividerHeight; 1127 } else if (li.type === 'optgroup-label') { 1128 canHighlight = false; 1129 li.height = this.sizeInfo.dropdownHeaderHeight; 1130 } else { 1131 li.height = this.sizeInfo.liHeight; 1132 } 1133 1134 if (li.disabled) canHighlight = false; 1135 1136 this.selectpicker.view.canHighlight.push(canHighlight); 1137 1138 li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height; 1139 } 1140 }, 1141 1142 isVirtual: function () { 1143 return (this.options.virtualScroll !== false) && (this.selectpicker.main.elements.length >= this.options.virtualScroll) || this.options.virtualScroll === true; 1144 }, 1145 1146 createView: function (isSearching, scrollTop) { 1147 scrollTop = scrollTop || 0; 1148 1149 var that = this; 1150 1151 this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main; 1152 1153 var active = []; 1154 var selected; 1155 var prevActive; 1156 1157 this.setPositionData(); 1158 1159 scroll(scrollTop, true); 1160 1161 this.$menuInner.off('scroll.createView').on('scroll.createView', function (e, updateValue) { 1162 if (!that.noScroll) scroll(this.scrollTop, updateValue); 1163 that.noScroll = false; 1164 }); 1165 1166 function scroll (scrollTop, init) { 1167 var size = that.selectpicker.current.elements.length, 1168 chunks = [], 1169 chunkSize, 1170 chunkCount, 1171 firstChunk, 1172 lastChunk, 1173 currentChunk, 1174 prevPositions, 1175 positionIsDifferent, 1176 previousElements, 1177 menuIsDifferent = true, 1178 isVirtual = that.isVirtual(); 1179 1180 that.selectpicker.view.scrollTop = scrollTop; 1181 1182 if (isVirtual === true) { 1183 // if an option that is encountered that is wider than the current menu width, update the menu width accordingly 1184 if (that.sizeInfo.hasScrollBar && that.$menu[0].offsetWidth > that.sizeInfo.totalMenuWidth) { 1185 that.sizeInfo.menuWidth = that.$menu[0].offsetWidth; 1186 that.sizeInfo.totalMenuWidth = that.sizeInfo.menuWidth + that.sizeInfo.scrollBarWidth; 1187 that.$menu.css('min-width', that.sizeInfo.menuWidth); 1188 } 1189 } 1190 1191 chunkSize = Math.ceil(that.sizeInfo.menuInnerHeight / that.sizeInfo.liHeight * 1.5); // number of options in a chunk 1192 chunkCount = Math.round(size / chunkSize) || 1; // number of chunks 1193 1194 for (var i = 0; i < chunkCount; i++) { 1195 var endOfChunk = (i + 1) * chunkSize; 1196 1197 if (i === chunkCount - 1) { 1198 endOfChunk = size; 1199 } 1200 1201 chunks[i] = [ 1202 (i) * chunkSize + (!i ? 0 : 1), 1203 endOfChunk 1204 ]; 1205 1206 if (!size) break; 1207 1208 if (currentChunk === undefined && scrollTop <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) { 1209 currentChunk = i; 1210 } 1211 } 1212 1213 if (currentChunk === undefined) currentChunk = 0; 1214 1215 prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1]; 1216 1217 // always display previous, current, and next chunks 1218 firstChunk = Math.max(0, currentChunk - 1); 1219 lastChunk = Math.min(chunkCount - 1, currentChunk + 1); 1220 1221 that.selectpicker.view.position0 = Math.max(0, chunks[firstChunk][0]) || 0; 1222 that.selectpicker.view.position1 = Math.min(size, chunks[lastChunk][1]) || 0; 1223 1224 positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1; 1225 1226 if (that.activeIndex !== undefined) { 1227 prevActive = that.selectpicker.current.elements[that.selectpicker.current.map.newIndex[that.prevActiveIndex]]; 1228 active = that.selectpicker.current.elements[that.selectpicker.current.map.newIndex[that.activeIndex]]; 1229 selected = that.selectpicker.current.elements[that.selectpicker.current.map.newIndex[that.selectedIndex]]; 1230 1231 if (init) { 1232 if (that.activeIndex !== that.selectedIndex && active && active.length) { 1233 active.classList.remove('active'); 1234 if (active.firstChild) active.firstChild.classList.remove('active'); 1235 } 1236 that.activeIndex = undefined; 1237 } 1238
vendor: 4,937 bytes, lines 1239-1350
1239 if (that.activeIndex && that.activeIndex !== that.selectedIndex && selected && selected.length) { 1240 selected.classList.remove('active'); 1241 if (selected.firstChild) selected.firstChild.classList.remove('active'); 1242 } 1243 } 1244 1245 if (that.prevActiveIndex !== undefined && that.prevActiveIndex !== that.activeIndex && that.prevActiveIndex !== that.selectedIndex && prevActive && prevActive.length) { 1246 prevActive.classList.remove('active'); 1247 if (prevActive.firstChild) prevActive.firstChild.classList.remove('active'); 1248 } 1249 1250 if (init || positionIsDifferent) { 1251 previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : []; 1252 1253 that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1); 1254 1255 that.setOptionStatus(); 1256 1257 // if searching, check to make sure the list has actually been updated before updating DOM 1258 // this prevents unnecessary repaints 1259 if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements); 1260 1261 // if virtual scroll is disabled and not searching, 1262 // menu should never need to be updated more than once 1263 if ((init || isVirtual === true) && menuIsDifferent) { 1264 var menuInner = that.$menuInner[0], 1265 menuFragment = document.createDocumentFragment(), 1266 emptyMenu = menuInner.firstChild.cloneNode(false), 1267 marginTop, 1268 marginBottom, 1269 elements = isVirtual === true ? that.selectpicker.view.visibleElements : that.selectpicker.current.elements, 1270 toSanitize = []; 1271 1272 // replace the existing UL with an empty one - this is faster than $.empty() 1273 menuInner.replaceChild(emptyMenu, menuInner.firstChild); 1274 1275 for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) { 1276 var element = elements[i], 1277 elText, 1278 elementData; 1279 1280 if (that.options.sanitize) { 1281 elText = element.lastChild; 1282 1283 if (elText) { 1284 elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0].data; 1285 1286 if (elementData && elementData.content && !elementData.sanitized) { 1287 toSanitize.push(elText); 1288 elementData.sanitized = true; 1289 } 1290 } 1291 } 1292 1293 menuFragment.appendChild(element); 1294 } 1295 1296 if (that.options.sanitize && toSanitize.length) { 1297 sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn); 1298 } 1299 1300 if (isVirtual === true) { 1301 marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position); 1302 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); 1303 1304 menuInner.firstChild.style.marginTop = marginTop + 'px'; 1305 menuInner.firstChild.style.marginBottom = marginBottom + 'px'; 1306 } 1307 1308 menuInner.firstChild.appendChild(menuFragment); 1309 } 1310 } 1311 1312 that.prevActiveIndex = that.activeIndex; 1313 1314 if (!that.options.liveSearch) { 1315 that.$menuInner.trigger('focus'); 1316 } else if (isSearching && init) { 1317 var index = 0, 1318 newActive; 1319 1320 if (!that.selectpicker.view.canHighlight[index]) { 1321 index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true); 1322 } 1323 1324 newActive = that.selectpicker.view.visibleElements[index]; 1325 1326 if (that.selectpicker.view.currentActive) { 1327 that.selectpicker.view.currentActive.classList.remove('active'); 1328 if (that.selectpicker.view.currentActive.firstChild) that.selectpicker.view.currentActive.firstChild.classList.remove('active'); 1329 } 1330 1331 if (newActive) { 1332 newActive.classList.add('active'); 1333 if (newActive.firstChild) newActive.firstChild.classList.add('active'); 1334 } 1335 1336 that.activeIndex = that.selectpicker.current.map.originalIndex[index]; 1337 } 1338 } 1339 1340 $(window) 1341 .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView') 1342 .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function () { 1343 var isActive = that.$newElement.hasClass(classNames.SHOW); 1344 1345 if (isActive) scroll(that.$menuInner[0].scrollTop); 1346 }); 1347 }, 1348 1349 setPlaceholder: function () { 1350 var updateIndex = false;
1351 1352 if (this.options.title && !this.multiple) { 1353 if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option'); 1354 1355 // this option doesn't create a new <li> element, but does add a new option, so liIndex is decreased 1356 // since newIndex is recalculated on every refresh, liIndex needs to be decreased even if the titleOption is already appended 1357 updateIndex = true; 1358 1359 var element = this.$element[0], 1360 isSelected = false, 1361 titleNotAppended = !this.selectpicker.view.titleOption.parentNode; 1362 1363 if (titleNotAppended) { 1364 // Use native JS to prepend option (faster) 1365 this.selectpicker.view.titleOption.className = 'bs-title-option'; 1366 this.selectpicker.view.titleOption.value = ''; 1367 1368 // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. 1369 // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, 1370 // if so, the select will have the data-selected attribute 1371 var $opt = $(element.options[element.selectedIndex]); 1372 isSelected = $opt.attr('selected') === undefined && this.$element.data('selected') === undefined; 1373 } 1374 1375 if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) { 1376 element.insertBefore(this.selectpicker.view.titleOption, element.firstChild); 1377 } 1378 1379 // Set selected *after* appending to select, 1380 // otherwise the option doesn't get selected in IE 1381 // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11 1382 if (isSelected) element.selectedIndex = 0; 1383 } 1384 1385 return updateIndex; 1386 }, 1387 1388 createLi: function () { 1389 var that = this, 1390 iconBase = that.options.iconBase, 1391 optionSelector = ':not([hidden]):not([data-hidden="true"])', 1392 checkMark, 1393 mainElements = [], 1394 widestOption, 1395 widestOptionLength = 0, 1396 mainData = [], 1397 optID = 0, 1398 headerIndex = 0, 1399 liIndex = -1; // increment liIndex whenever a new <li> element is created to ensure newIndex is correct 1400 1401 if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; 1402 1403 if (that.options.showTick || that.multiple) { 1404 checkMark = elementTemplates.span.cloneNode(false); 1405 checkMark.className = iconBase + ' ' + that.options.tickIcon + ' check-mark'; 1406 elementTemplates.a.appendChild(checkMark); 1407 } 1408 1409 if (this.setPlaceholder()) liIndex--; 1410 1411 var selectOptions = this.$element[0].options; 1412 1413 for (var index = 0, len = selectOptions.length; index < len; index++) { 1414 var option = selectOptions[index]; 1415 1416 liIndex++; 1417 1418 if (option.classList.contains('bs-title-option')) continue; 1419 1420 var thisData = { 1421 content: option.getAttribute('data-content'), 1422 tokens: option.getAttribute('data-tokens'), 1423 subtext: option.getAttribute('data-subtext'), 1424 icon: option.getAttribute('data-icon'), 1425 hidden: option.getAttribute('data-hidden') === 'true', 1426 divider: option.getAttribute('data-divider') === 'true' 1427 }; 1428 1429 // Get the class and text for the option 1430 var optionClass = option.className || '', 1431 cssText = option.style.cssText, 1432 inline = cssText ? htmlEscape(cssText) : '', 1433 optionContent = thisData.content, 1434 text = option.textContent, 1435 parent = option.parentNode, 1436 next = option.nextElementSibling, 1437 previous = option.previousElementSibling, 1438 isOptgroup = parent.tagName === 'OPTGROUP', 1439 isOptgroupDisabled = isOptgroup && parent.disabled, 1440 isDisabled = option.disabled || isOptgroupDisabled, 1441 prevHiddenIndex, 1442 showDivider = previous && previous.tagName === 'OPTGROUP', 1443 textElement, 1444 labelElement, 1445 prevHidden; 1446 1447 var parentData = { 1448 hidden: parent.getAttribute('data-hidden') === 'true' 1449 }; 1450 1451 if ( 1452 ( 1453 (thisData.hidden === true || option.hidden) || 1454 (isOptgroup && (parentData.hidden === true || parent.hidden)) 1455 ) || 1456 (that.options.hideDisabled && (isDisabled || isOptgroupDisabled)) 1457 ) { 1458 // set prevHiddenIndex - the index of the first hidden option in a group of hidden options 1459 // used to determine whether or not a divider should be placed after an optgroup if there are 1460 // hidden options between the optgroup and the first visible option 1461 prevHiddenIndex = option.prevHiddenIndex; 1462 if (next) next.prevHiddenIndex = (prevHiddenIndex !== undefined ? prevHiddenIndex : index); 1463 1464 liIndex--; 1465 1466 continue; 1467 } else { 1468 if (next && next.prevHiddenIndex !== undefined) next.prevHiddenIndex = undefined; 1469 } 1470 1471 if (isOptgroup && thisData.divider !== true) { 1472 var optGroupClass = ' ' + parent.className || '', 1473 previousOption = option.previousElementSibling; 1474 1475 prevHiddenIndex = option.prevHiddenIndex; 1476 1477 // Get the first visible option before the first hidden option in the group. 1478 // Ensures a divider is shown if, for example, the first option in the optgroup is hidden. 1479 if (prevHiddenIndex !== undefined) { 1480 previousOption = selectOptions[prevHiddenIndex].previousElementSibling; 1481 } 1482 1483 // if there is no previous option, this option is the first visible option in the optgroup 1484 if (!previousOption) { 1485 optID += 1; 1486 1487 parentData.subtext = parent.getAttribute('data-subtext'); 1488 parentData.icon = parent.getAttribute('data-icon'); 1489 1490 // Get the opt group label 1491 var label = parent.label, 1492 labelEscaped = htmlEscape(label), 1493 labelSubtext = parentData.subtext, 1494 labelIcon = parentData.icon; 1495 1496 if (index !== 0 && mainElements.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? 1497 liIndex++; 1498 mainElements.push( 1499 generateOption.li( 1500 false, 1501 classNames.DIVIDER, 1502 optID + 'div' 1503 ) 1504 ); 1505 mainData.push({ 1506 type: 'divider', 1507 optID: optID 1508 }); 1509 } 1510 liIndex++; 1511 1512 labelElement = generateOption.label({ 1513 labelEscaped: labelEscaped, 1514 labelSubtext: labelSubtext, 1515 labelIcon: labelIcon, 1516 iconBase: iconBase 1517 }); 1518 1519 mainElements.push(generateOption.li(labelElement, 'dropdown-header' + optGroupClass, optID)); 1520 mainData.push({ 1521 content: labelEscaped, 1522 subtext: labelSubtext, 1523 type: 'optgroup-label', 1524 optID: optID 1525 }); 1526 1527 headerIndex = liIndex - 1; 1528 } 1529 1530 textElement = generateOption.text({ 1531 text: text, 1532 optionContent: optionContent, 1533 optionSubtext: thisData.subtext, 1534 optionIcon: thisData.icon, 1535 iconBase: iconBase 1536 }); 1537 1538 mainElements.push(generateOption.li(generateOption.a(textElement, 'opt ' + optionClass + optGroupClass, inline), '', optID)); 1539 mainData.push({ 1540 content: optionContent || text, 1541 subtext: thisData.subtext, 1542 tokens: thisData.tokens, 1543 type: 'option', 1544 optID: optID, 1545 headerIndex: headerIndex, 1546 lastIndex: headerIndex + parent.querySelectorAll('option' + optionSelector).length, 1547 originalIndex: index, 1548 data: thisData 1549 }); 1550 } else if (thisData.divider === true) { 1551 mainElements.push(generateOption.li(false, classNames.DIVIDER)); 1552 mainData.push({ 1553 type: 'divider', 1554 originalIndex: index, 1555 data: thisData 1556 }); 1557 } else { 1558 if (that.options.hideDisabled) {
vendor: 28,129 bytes, lines 1559-2225
1559 if (showDivider) { 1560 var disabledOptions = previous.querySelectorAll('option:disabled'); 1561 1562 if (disabledOptions.length === previous.children.length) showDivider = false; 1563 } else { 1564 prevHiddenIndex = option.prevHiddenIndex; 1565 1566 if (prevHiddenIndex !== undefined) { 1567 // select the element **before** the first hidden element in the group 1568 prevHidden = selectOptions[prevHiddenIndex].previousElementSibling; 1569 1570 if (prevHidden && prevHidden.tagName === 'OPTGROUP' && !prevHidden.disabled) { 1571 var disabledOptions = prevHidden.querySelectorAll('option:disabled'); 1572 1573 if (disabledOptions.length < prevHidden.children.length) showDivider = true; 1574 } 1575 } 1576 } 1577 } 1578 1579 if (showDivider && mainData.length && mainData[mainData.length - 1].type !== 'divider') { 1580 liIndex++; 1581 mainElements.push( 1582 generateOption.li( 1583 false, 1584 classNames.DIVIDER, 1585 optID + 'div' 1586 ) 1587 ); 1588 mainData.push({ 1589 type: 'divider', 1590 optID: optID 1591 }); 1592 } 1593 1594 textElement = generateOption.text({ 1595 text: text, 1596 optionContent: optionContent, 1597 optionSubtext: thisData.subtext, 1598 optionIcon: thisData.icon, 1599 iconBase: iconBase 1600 }); 1601 1602 mainElements.push(generateOption.li(generateOption.a(textElement, optionClass, inline))); 1603 mainData.push({ 1604 content: optionContent || text, 1605 subtext: thisData.subtext, 1606 tokens: thisData.tokens, 1607 type: 'option', 1608 originalIndex: index, 1609 data: thisData 1610 }); 1611 } 1612 1613 that.selectpicker.main.map.newIndex[index] = liIndex; 1614 that.selectpicker.main.map.originalIndex[liIndex] = index; 1615 1616 // get the most recent option info added to mainData 1617 var _mainDataLast = mainData[mainData.length - 1]; 1618 1619 _mainDataLast.disabled = isDisabled; 1620 1621 var combinedLength = 0; 1622 1623 // count the number of characters in the option - not perfect, but should work in most cases 1624 if (_mainDataLast.content) combinedLength += _mainDataLast.content.length; 1625 if (_mainDataLast.subtext) combinedLength += _mainDataLast.subtext.length; 1626 // if there is an icon, ensure this option's width is checked 1627 if (thisData.icon) combinedLength += 1; 1628 1629 if (combinedLength > widestOptionLength) { 1630 widestOptionLength = combinedLength; 1631 1632 // guess which option is the widest 1633 // use this when calculating menu width 1634 // not perfect, but it's fast, and the width will be updating accordingly when scrolling 1635 widestOption = mainElements[mainElements.length - 1]; 1636 } 1637 } 1638 1639 this.selectpicker.main.elements = mainElements; 1640 this.selectpicker.main.data = mainData; 1641 1642 this.selectpicker.current = this.selectpicker.main; 1643 1644 this.selectpicker.view.widestOption = widestOption; 1645 }, 1646 1647 findLis: function () { 1648 return this.$menuInner.find('.inner > li'); 1649 }, 1650 1651 render: function () { 1652 // ensure titleOption is appended and selected (if necessary) before getting selectedOptions 1653 this.setPlaceholder(); 1654 1655 var that = this, 1656 selectedOptions = this.$element[0].selectedOptions, 1657 selectedCount = selectedOptions.length, 1658 button = this.$button[0], 1659 buttonInner = button.querySelector('.filter-option-inner-inner'), 1660 multipleSeparator = document.createTextNode(this.options.multipleSeparator), 1661 titleFragment = elementTemplates.fragment.cloneNode(false), 1662 showCount, 1663 countMax, 1664 hasContent = false; 1665 1666 this.togglePlaceholder(); 1667 1668 this.tabIndex(); 1669 1670 if (this.options.selectedTextFormat === 'static') { 1671 titleFragment = generateOption.text({ text: this.options.title }, true); 1672 } else { 1673 showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 1; 1674 1675 // determine if the number of selected options will be shown (showCount === true) 1676 if (showCount) { 1677 countMax = this.options.selectedTextFormat.split('>'); 1678 showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2); 1679 } 1680 1681 // only loop through all selected options if the count won't be shown 1682 if (showCount === false) { 1683 for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) { 1684 if (selectedIndex < 50) { 1685 var option = selectedOptions[selectedIndex], 1686 titleOptions = {}, 1687 thisData = { 1688 content: option.getAttribute('data-content'), 1689 subtext: option.getAttribute('data-subtext'), 1690 icon: option.getAttribute('data-icon') 1691 }; 1692 1693 if (this.multiple && selectedIndex > 0) { 1694 titleFragment.appendChild(multipleSeparator.cloneNode(false)); 1695 } 1696 1697 if (option.title) { 1698 titleOptions.text = option.title; 1699 } else if (thisData.content && that.options.showContent) { 1700 titleOptions.optionContent = thisData.content.toString(); 1701 hasContent = true; 1702 } else { 1703 if (that.options.showIcon) { 1704 titleOptions.optionIcon = thisData.icon; 1705 titleOptions.iconBase = this.options.iconBase; 1706 } 1707 if (that.options.showSubtext && !that.multiple && thisData.subtext) titleOptions.optionSubtext = ' ' + thisData.subtext; 1708 titleOptions.text = option.textContent.trim(); 1709 } 1710 1711 titleFragment.appendChild(generateOption.text(titleOptions, true)); 1712 } else { 1713 break; 1714 } 1715 } 1716 1717 // add ellipsis 1718 if (selectedCount > 49) { 1719 titleFragment.appendChild(document.createTextNode('...')); 1720 } 1721 } else { 1722 var optionSelector = ':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])'; 1723 if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; 1724 1725 // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc. 1726 var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length, 1727 tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText; 1728 1729 titleFragment = generateOption.text({ 1730 text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString()) 1731 }, true); 1732 } 1733 } 1734 1735 if (this.options.title == undefined) { 1736 // use .attr to ensure undefined is returned if title attribute is not set 1737 this.options.title = this.$element.attr('title'); 1738 } 1739 1740 // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText 1741 if (!titleFragment.childNodes.length) { 1742 titleFragment = generateOption.text({ 1743 text: typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText 1744 }, true); 1745 } 1746 1747 // strip all HTML tags and trim the result, then unescape any escaped tags 1748 button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim(); 1749 1750 if (this.options.sanitize && hasContent) { 1751 sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn); 1752 } 1753 1754 buttonInner.innerHTML = ''; 1755 buttonInner.appendChild(titleFragment); 1756 1757 if (version.major < 4 && this.$newElement[0].parentNode.classList.contains('input-group')) { 1758 var filterExpand = button.querySelector('.filter-expand'), 1759 clone = buttonInner.cloneNode(true); 1760 1761 clone.className = 'filter-expand'; 1762 1763 if (filterExpand) { 1764 button.replaceChild(clone, filterExpand); 1765 } else { 1766 button.appendChild(clone); 1767 } 1768 } 1769 1770 this.$element.trigger('rendered' + EVENT_KEY); 1771 }, 1772 1773 /** 1774 * @param [style] 1775 * @param [status] 1776 */ 1777 setStyle: function (newStyle, status) { 1778 var button = this.$button[0], 1779 style = this.options.style.split(' '), 1780 buttonClass; 1781 1782 if (this.$element.attr('class')) { 1783 this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); 1784 } 1785 1786 if (version.major < 4) { 1787 this.$newElement[0].classList.add('bs3'); 1788 } 1789 1790 if (newStyle) { 1791 buttonClass = newStyle.split(' '); 1792 } else { 1793 buttonClass = style; 1794 } 1795 1796 if (status == 'add') { 1797 button.classList.add.apply(button.classList, buttonClass); 1798 } else if (status == 'remove') { 1799 button.classList.remove.apply(button.classList, buttonClass); 1800 } else { 1801 button.classList.remove.apply(button.classList, style); 1802 button.classList.add.apply(button.classList, buttonClass); 1803 } 1804 }, 1805 1806 liHeight: function (refresh) { 1807 if (!refresh && (this.options.size === false || this.sizeInfo)) return; 1808 1809 if (!this.sizeInfo) this.sizeInfo = {}; 1810 1811 var newElement = document.createElement('div'), 1812 menu = document.createElement('div'), 1813 menuInner = document.createElement('div'), 1814 menuInnerInner = document.createElement('ul'), 1815 divider = document.createElement('li'), 1816 dropdownHeader = document.createElement('li'), 1817 li = document.createElement('li'), 1818 a = document.createElement('a'), 1819 text = document.createElement('span'), 1820 header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null, 1821 search = this.options.liveSearch ? document.createElement('div') : null, 1822 actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, 1823 doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null, 1824 firstOption = this.$element.find('option')[0]; 1825 1826 this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth; 1827 1828 text.className = 'text'; 1829 a.className = 'dropdown-item ' + (firstOption ? firstOption.className : ''); 1830 newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW; 1831 newElement.style.width = this.sizeInfo.selectWidth + 'px'; 1832 if (this.options.width === 'auto') menu.style.minWidth = 0; 1833 menu.className = classNames.MENU + ' ' + classNames.SHOW; 1834 menuInner.className = 'inner ' + classNames.SHOW; 1835 menuInnerInner.className = classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : ''); 1836 divider.className = classNames.DIVIDER; 1837 dropdownHeader.className = 'dropdown-header'; 1838 1839 text.appendChild(document.createTextNode('\u200b')); 1840 a.appendChild(text); 1841 li.appendChild(a); 1842 dropdownHeader.appendChild(text.cloneNode(true)); 1843 1844 if (this.selectpicker.view.widestOption) { 1845 menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true)); 1846 } 1847 1848 menuInnerInner.appendChild(li); 1849 menuInnerInner.appendChild(divider); 1850 menuInnerInner.appendChild(dropdownHeader); 1851 if (header) menu.appendChild(header); 1852 if (search) { 1853 var input = document.createElement('input'); 1854 search.className = 'bs-searchbox'; 1855 input.className = 'form-control'; 1856 search.appendChild(input); 1857 menu.appendChild(search); 1858 } 1859 if (actions) menu.appendChild(actions); 1860 menuInner.appendChild(menuInnerInner); 1861 menu.appendChild(menuInner); 1862 if (doneButton) menu.appendChild(doneButton); 1863 newElement.appendChild(menu); 1864 1865 document.body.appendChild(newElement); 1866 1867 var liHeight = a.offsetHeight, 1868 dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0, 1869 headerHeight = header ? header.offsetHeight : 0, 1870 searchHeight = search ? search.offsetHeight : 0, 1871 actionsHeight = actions ? actions.offsetHeight : 0, 1872 doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, 1873 dividerHeight = $(divider).outerHeight(true), 1874 // fall back to jQuery if getComputedStyle is not supported 1875 menuStyle = window.getComputedStyle ? window.getComputedStyle(menu) : false, 1876 menuWidth = menu.offsetWidth, 1877 $menu = menuStyle ? null : $(menu), 1878 menuPadding = { 1879 vert: toInteger(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + 1880 toInteger(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + 1881 toInteger(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + 1882 toInteger(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), 1883 horiz: toInteger(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + 1884 toInteger(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + 1885 toInteger(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + 1886 toInteger(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) 1887 }, 1888 menuExtras = { 1889 vert: menuPadding.vert + 1890 toInteger(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + 1891 toInteger(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, 1892 horiz: menuPadding.horiz + 1893 toInteger(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + 1894 toInteger(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 1895 }, 1896 scrollBarWidth; 1897 1898 menuInner.style.overflowY = 'scroll'; 1899 1900 scrollBarWidth = menu.offsetWidth - menuWidth; 1901 1902 document.body.removeChild(newElement); 1903 1904 this.sizeInfo.liHeight = liHeight; 1905 this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight; 1906 this.sizeInfo.headerHeight = headerHeight; 1907 this.sizeInfo.searchHeight = searchHeight; 1908 this.sizeInfo.actionsHeight = actionsHeight; 1909 this.sizeInfo.doneButtonHeight = doneButtonHeight; 1910 this.sizeInfo.dividerHeight = dividerHeight; 1911 this.sizeInfo.menuPadding = menuPadding; 1912 this.sizeInfo.menuExtras = menuExtras; 1913 this.sizeInfo.menuWidth = menuWidth; 1914 this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth; 1915 this.sizeInfo.scrollBarWidth = scrollBarWidth; 1916 this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight; 1917 1918 this.setPositionData(); 1919 }, 1920 1921 getSelectPosition: function () { 1922 var that = this, 1923 $window = $(window), 1924 pos = that.$newElement.offset(), 1925 $container = $(that.options.container), 1926 containerPos; 1927 1928 if (that.options.container && $container.length && !$container.is('body')) { 1929 containerPos = $container.offset(); 1930 containerPos.top += parseInt($container.css('borderTopWidth')); 1931 containerPos.left += parseInt($container.css('borderLeftWidth')); 1932 } else { 1933 containerPos = { top: 0, left: 0 }; 1934 } 1935 1936 var winPad = that.options.windowPadding; 1937 1938 this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); 1939 this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2]; 1940 this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); 1941 this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1]; 1942 this.sizeInfo.selectOffsetTop -= winPad[0]; 1943 this.sizeInfo.selectOffsetLeft -= winPad[3]; 1944 }, 1945 1946 setMenuSize: function (isAuto) { 1947 this.getSelectPosition(); 1948 1949 var selectWidth = this.sizeInfo.selectWidth, 1950 liHeight = this.sizeInfo.liHeight, 1951 headerHeight = this.sizeInfo.headerHeight, 1952 searchHeight = this.sizeInfo.searchHeight, 1953 actionsHeight = this.sizeInfo.actionsHeight, 1954 doneButtonHeight = this.sizeInfo.doneButtonHeight, 1955 divHeight = this.sizeInfo.dividerHeight, 1956 menuPadding = this.sizeInfo.menuPadding, 1957 menuInnerHeight, 1958 menuHeight, 1959 divLength = 0, 1960 minHeight, 1961 _minHeight, 1962 maxHeight, 1963 menuInnerMinHeight, 1964 estimate; 1965 1966 if (this.options.dropupAuto) { 1967 // Get the estimated height of the menu without scrollbars. 1968 // This is useful for smaller menus, where there might be plenty of room 1969 // below the button without setting dropup, but we can't know 1970 // the exact height of the menu until createView is called later 1971 estimate = liHeight * this.selectpicker.current.elements.length + menuPadding.vert; 1972 this.$newElement.toggleClass(classNames.DROPUP, this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot); 1973 } 1974 1975 if (this.options.size === 'auto') { 1976 _minHeight = this.selectpicker.current.elements.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0; 1977 menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert; 1978 minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; 1979 menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0); 1980 1981 if (this.$newElement.hasClass(classNames.DROPUP)) { 1982 menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert; 1983 } 1984 1985 maxHeight = menuHeight; 1986 menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert; 1987 } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { 1988 for (var i = 0; i < this.options.size; i++) { 1989 if (this.selectpicker.current.data[i].type === 'divider') divLength++; 1990 } 1991 1992 menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; 1993 menuInnerHeight = menuHeight - menuPadding.vert; 1994 maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; 1995 minHeight = menuInnerMinHeight = ''; 1996 } 1997 1998 if (this.options.dropdownAlignRight === 'auto') { 1999 this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth)); 2000 } 2001 2002 this.$menu.css({ 2003 'max-height': maxHeight + 'px', 2004 'overflow': 'hidden', 2005 'min-height': minHeight + 'px' 2006 }); 2007 2008 this.$menuInner.css({ 2009 'max-height': menuInnerHeight + 'px', 2010 'overflow-y': 'auto', 2011 'min-height': menuInnerMinHeight + 'px' 2012 }); 2013 2014 // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView 2015 this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1); 2016 2017 if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) { 2018 this.sizeInfo.hasScrollBar = true; 2019 this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth; 2020 2021 this.$menu.css('min-width', this.sizeInfo.totalMenuWidth); 2022 } 2023 2024 if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update(); 2025 }, 2026 2027 setSize: function (refresh) { 2028 this.liHeight(refresh); 2029 2030 if (this.options.header) this.$menu.css('padding-top', 0); 2031 if (this.options.size === false) return; 2032 2033 var that = this, 2034 $window = $(window), 2035 selectedIndex, 2036 offset = 0; 2037 2038 this.setMenuSize(); 2039 2040 if (this.options.liveSearch) { 2041 this.$searchbox 2042 .off('input.setMenuSize propertychange.setMenuSize') 2043 .on('input.setMenuSize propertychange.setMenuSize', function () { 2044 return that.setMenuSize(); 2045 }); 2046 } 2047 2048 if (this.options.size === 'auto') { 2049 $window 2050 .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize') 2051 .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function () { 2052 return that.setMenuSize(); 2053 }); 2054 } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { 2055 $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize'); 2056 } 2057 2058 if (refresh) { 2059 offset = this.$menuInner[0].scrollTop; 2060 } else if (!that.multiple) { 2061 selectedIndex = that.selectpicker.main.map.newIndex[that.$element[0].selectedIndex]; 2062 2063 if (typeof selectedIndex === 'number' && that.options.size !== false) { 2064 offset = that.sizeInfo.liHeight * selectedIndex; 2065 offset = offset - (that.sizeInfo.menuInnerHeight / 2) + (that.sizeInfo.liHeight / 2); 2066 } 2067 } 2068 2069 that.createView(false, offset); 2070 }, 2071 2072 setWidth: function () { 2073 var that = this; 2074 2075 if (this.options.width === 'auto') { 2076 requestAnimationFrame(function () { 2077 that.$menu.css('min-width', '0'); 2078 2079 that.$element.on('loaded' + EVENT_KEY, function () { 2080 that.liHeight(); 2081 that.setMenuSize(); 2082 2083 // Get correct width if element is hidden 2084 var $selectClone = that.$newElement.clone().appendTo('body'), 2085 btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth(); 2086 2087 $selectClone.remove(); 2088 2089 // Set width to whatever's larger, button title or longest option 2090 that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth); 2091 that.$newElement.css('width', that.sizeInfo.selectWidth + 'px'); 2092 }); 2093 }); 2094 } else if (this.options.width === 'fit') { 2095 // Remove inline min-width so width can be changed from 'auto' 2096 this.$menu.css('min-width', ''); 2097 this.$newElement.css('width', '').addClass('fit-width'); 2098 } else if (this.options.width) { 2099 // Remove inline min-width so width can be changed from 'auto' 2100 this.$menu.css('min-width', ''); 2101 this.$newElement.css('width', this.options.width); 2102 } else { 2103 // Remove inline min-width/width so width can be changed 2104 this.$menu.css('min-width', ''); 2105 this.$newElement.css('width', ''); 2106 } 2107 // Remove fit-width class if width is changed programmatically 2108 if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { 2109 this.$newElement[0].classList.remove('fit-width'); 2110 } 2111 }, 2112 2113 selectPosition: function () { 2114 this.$bsContainer = $('<div class="bs-container" />'); 2115 2116 var that = this, 2117 $container = $(this.options.container), 2118 pos, 2119 containerPos, 2120 actualHeight, 2121 getPlacement = function ($element) { 2122 var containerPosition = {}, 2123 // fall back to dropdown's default display setting if display is not manually set 2124 display = that.options.display || ( 2125 // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default 2126 $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display 2127 : false 2128 ); 2129 2130 that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP)); 2131 pos = $element.offset(); 2132 2133 if (!$container.is('body')) { 2134 containerPos = $container.offset(); 2135 containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); 2136 containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); 2137 } else { 2138 containerPos = { top: 0, left: 0 }; 2139 } 2140 2141 actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight; 2142 2143 // Bootstrap 4+ uses Popper for menu positioning 2144 if (version.major < 4 || display === 'static') { 2145 containerPosition.top = pos.top - containerPos.top + actualHeight; 2146 containerPosition.left = pos.left - containerPos.left; 2147 } 2148 2149 containerPosition.width = $element[0].offsetWidth; 2150 2151 that.$bsContainer.css(containerPosition); 2152 }; 2153 2154 this.$button.on('click.bs.dropdown.data-api', function () { 2155 if (that.isDisabled()) { 2156 return; 2157 } 2158 2159 getPlacement(that.$newElement); 2160 2161 that.$bsContainer 2162 .appendTo(that.options.container) 2163 .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW)) 2164 .append(that.$menu); 2165 }); 2166 2167 $(window) 2168 .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId) 2169 .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function () { 2170 var isActive = that.$newElement.hasClass(classNames.SHOW); 2171 2172 if (isActive) getPlacement(that.$newElement); 2173 }); 2174 2175 this.$element.on('hide' + EVENT_KEY, function () { 2176 that.$menu.data('height', that.$menu.height()); 2177 that.$bsContainer.detach(); 2178 }); 2179 }, 2180 2181 setOptionStatus: function () { 2182 var that = this, 2183 selectOptions = this.$element[0].options; 2184 2185 that.noScroll = false; 2186 2187 if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) { 2188 for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) { 2189 var index = that.selectpicker.current.map.originalIndex[i + that.selectpicker.view.position0], // faster than $(li).data('originalIndex') 2190 option = selectOptions[index]; 2191 2192 if (option) { 2193 var liIndex = this.selectpicker.main.map.newIndex[index], 2194 li = this.selectpicker.main.elements[liIndex]; 2195 2196 that.setDisabled( 2197 index, 2198 option.disabled || (option.parentNode.tagName === 'OPTGROUP' && option.parentNode.disabled), 2199 liIndex, 2200 li 2201 ); 2202 2203 that.setSelected( 2204 index, 2205 option.selected, 2206 liIndex, 2207 li 2208 ); 2209 } 2210 } 2211 } 2212 }, 2213 2214 /** 2215 * @param {number} index - the index of the option that is being changed 2216 * @param {boolean} selected - true if the option is being selected, false if being deselected 2217 */ 2218 setSelected: function (index, selected, liIndex, li) { 2219 var activeIndexIsSet = this.activeIndex !== undefined, 2220 thisIsActive = this.activeIndex === index, 2221 prevActiveIndex, 2222 prevActive, 2223 a, 2224 // if current option is already active 2225 // OR
2226 // if the current option is being selected, it's NOT multiple, and 2227 // activeIndex is undefined: 2228 // - when the menu is first being opened, OR 2229 // - after a search has been performed, OR 2230 // - 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) 2231 keepActive = thisIsActive || (selected && !this.multiple && !activeIndexIsSet); 2232 2233 if (!liIndex) liIndex = this.selectpicker.main.map.newIndex[index]; 2234 if (!li) li = this.selectpicker.main.elements[liIndex]; 2235 2236 a = li.firstChild; 2237 2238 if (selected) { 2239 this.selectedIndex = index; 2240 } 2241 2242 li.classList.toggle('selected', selected); 2243 li.classList.toggle('active', keepActive); 2244 2245 if (keepActive) { 2246 this.selectpicker.view.currentActive = li; 2247 this.activeIndex = index; 2248 } 2249 2250 if (a) { 2251 a.classList.toggle('selected', selected); 2252 a.classList.toggle('active', keepActive); 2253 a.setAttribute('aria-selected', selected); 2254 } 2255 2256 if (!keepActive) { 2257 if (!activeIndexIsSet && selected && this.prevActiveIndex !== undefined) { 2258 prevActiveIndex = this.selectpicker.main.map.newIndex[this.prevActiveIndex]; 2259 prevActive = this.selectpicker.main.elements[prevActiveIndex]; 2260 2261 prevActive.classList.remove('active'); 2262 if (prevActive.firstChild) { 2263 prevActive.firstChild.classList.remove('active'); 2264 } 2265 } 2266 } 2267 }, 2268 2269 /** 2270 * @param {number} index - the index of the option that is being disabled 2271 * @param {boolean} disabled - true if the option is being disabled, false if being enabled 2272 */ 2273 setDisabled: function (index, disabled, liIndex, li) { 2274 var a; 2275 2276 if (!liIndex) liIndex = this.selectpicker.main.map.newIndex[index]; 2277 if (!li) li = this.selectpicker.main.elements[liIndex]; 2278 2279 a = li.firstChild; 2280 2281 li.classList.toggle(classNames.DISABLED, disabled); 2282 2283 if (a) { 2284 if (version.major === '4') a.classList.toggle(classNames.DISABLED, disabled); 2285 2286 a.setAttribute('aria-disabled', disabled); 2287 2288 if (disabled) { 2289 a.setAttribute('tabindex', -1); 2290 } else { 2291 a.setAttribute('tabindex', 0); 2292 } 2293 } 2294 }, 2295 2296 isDisabled: function () { 2297 return this.$element[0].disabled; 2298 }, 2299 2300 checkDisabled: function () { 2301 var that = this; 2302 2303 if (this.isDisabled()) { 2304 this.$newElement[0].classList.add(classNames.DISABLED); 2305 this.$button.addClass(classNames.DISABLED).attr('tabindex', -1).attr('aria-disabled', true); 2306 } else { 2307 if (this.$button[0].classList.contains(classNames.DISABLED)) { 2308 this.$newElement[0].classList.remove(classNames.DISABLED); 2309 this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false); 2310 } 2311 2312 if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { 2313 this.$button.removeAttr('tabindex'); 2314 } 2315 } 2316 2317 this.$button.on('click', function () { 2318 return !that.isDisabled(); 2319 }); 2320 }, 2321 2322 togglePlaceholder: function () { 2323 // much faster than calling $.val() 2324 var element = this.$element[0], 2325 selectedIndex = element.selectedIndex, 2326 nothingSelected = selectedIndex === -1; 2327 2328 if (!nothingSelected && !element.options[selectedIndex].value) nothingSelected = true; 2329 2330 this.$button.toggleClass('bs-placeholder', nothingSelected); 2331 }, 2332 2333 tabIndex: function () { 2334 if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && 2335 (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { 2336 this.$element.data('tabindex', this.$element.attr('tabindex')); 2337 this.$button.attr('tabindex', this.$element.data('tabindex')); 2338 } 2339 2340 this.$element.attr('tabindex', -98); 2341 }, 2342 2343 clickListener: function () { 2344 var that = this, 2345 $document = $(document); 2346 2347 $document.data('spaceSelect', false); 2348 2349 this.$button.on('keyup', function (e) { 2350 if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { 2351 e.preventDefault(); 2352 $document.data('spaceSelect', false); 2353 } 2354 }); 2355 2356 this.$newElement.on('show.bs.dropdown', function () { 2357 if (version.major > 3 && !that.dropdown) { 2358 that.dropdown = that.$button.data('bs.dropdown'); 2359 that.dropdown._menu = that.$menu[0]; 2360 } 2361 }); 2362 2363 this.$button.on('click.bs.dropdown.data-api', function () { 2364 if (!that.$newElement.hasClass(classNames.SHOW)) { 2365 that.setSize(); 2366 } 2367 }); 2368 2369 function setFocus () { 2370 if (that.options.liveSearch) { 2371 that.$searchbox.trigger('focus'); 2372 } else { 2373 that.$menuInner.trigger('focus'); 2374 } 2375 } 2376 2377 function checkPopperExists () { 2378 if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state.isCreated) { 2379 setFocus(); 2380 } else { 2381 requestAnimationFrame(checkPopperExists); 2382 } 2383 } 2384 2385 this.$element.on('shown' + EVENT_KEY, function () {
2386 if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) { 2387 that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop; 2388 } 2389 2390 if (version.major > 3) { 2391 requestAnimationFrame(checkPopperExists); 2392 } else { 2393 setFocus(); 2394 } 2395 }); 2396 2397 this.$menuInner.on('click', 'li a', function (e, retainActive) { 2398 var $this = $(this), 2399 position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0, 2400 clickedIndex = that.selectpicker.current.map.originalIndex[$this.parent().index() + position0], 2401 prevValue = getSelectValues(that.$element[0]), 2402 prevIndex = that.$element.prop('selectedIndex'), 2403 triggerChange = true; 2404 2405 // Don't close on multi choice menu 2406 if (that.multiple && that.options.maxOptions !== 1) { 2407 e.stopPropagation(); 2408 } 2409 2410 e.preventDefault(); 2411 2412 // Don't run if the select is disabled 2413 if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) { 2414 var $options = that.$element.find('option'), 2415 $option = $options.eq(clickedIndex), 2416 state = $option.prop('selected'), 2417 $optgroup = $option.parent('optgroup'), 2418 $optgroupOptions = $optgroup.find('option'), 2419 maxOptions = that.options.maxOptions, 2420 maxOptionsGrp = $optgroup.data('maxOptions') || false; 2421 2422 if (clickedIndex === that.activeIndex) retainActive = true; 2423 2424 if (!retainActive) { 2425 that.prevActiveIndex = that.activeIndex; 2426 that.activeIndex = undefined; 2427 } 2428 2429 if (!that.multiple) { // Deselect all others if not multi select box 2430 $options.prop('selected', false); 2431 $option.prop('selected', true); 2432 that.setSelected(clickedIndex, true); 2433 } else { // Toggle the one we have chosen if we are multi select. 2434 $option.prop('selected', !state); 2435 2436 that.setSelected(clickedIndex, !state); 2437 $this.trigger('blur'); 2438 2439 if (maxOptions !== false || maxOptionsGrp !== false) { 2440 var maxReached = maxOptions < $options.filter(':selected').length, 2441 maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; 2442 2443 if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { 2444 if (maxOptions && maxOptions == 1) { 2445 $options.prop('selected', false); 2446 $option.prop('selected', true); 2447 2448 for (var i = 0; i < $options.length; i++) { 2449 that.setSelected(i, false); 2450 } 2451 2452 that.setSelected(clickedIndex, true); 2453 } else if (maxOptionsGrp && maxOptionsGrp == 1) { 2454 $optgroup.find('option:selected').prop('selected', false); 2455 $option.prop('selected', true); 2456 2457 for (var i = 0; i < $optgroupOptions.length; i++) { 2458 var option = $optgroupOptions[i]; 2459 that.setSelected($options.index(option), false); 2460 } 2461 2462 that.setSelected(clickedIndex, true); 2463 } else { 2464 var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, 2465 maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, 2466 maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), 2467 maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), 2468 $notify = $('<div class="notify"></div>'); 2469 // If {var} is set in array, replace it 2470 /** @deprecated */ 2471 if (maxOptionsArr[2]) { 2472 maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); 2473 maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); 2474 } 2475 2476 $option.prop('selected', false); 2477 2478 that.$menu.append($notify); 2479 2480 if (maxOptions && maxReached) { 2481 $notify.append($('<div>' + maxTxt + '</div>')); 2482 triggerChange = false;
vendor: 5,220 bytes, lines 2483-2638
2483 that.$element.trigger('maxReached' + EVENT_KEY); 2484 } 2485 2486 if (maxOptionsGrp && maxReachedGrp) { 2487 $notify.append($('<div>' + maxTxtGrp + '</div>')); 2488 triggerChange = false; 2489 that.$element.trigger('maxReachedGrp' + EVENT_KEY); 2490 } 2491 2492 setTimeout(function () { 2493 that.setSelected(clickedIndex, false); 2494 }, 10); 2495 2496 $notify.delay(750).fadeOut(300, function () { 2497 $(this).remove(); 2498 }); 2499 } 2500 } 2501 } 2502 } 2503 2504 if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { 2505 that.$button.trigger('focus'); 2506 } else if (that.options.liveSearch) { 2507 that.$searchbox.trigger('focus'); 2508 } 2509 2510 // Trigger select 'change' 2511 if (triggerChange) { 2512 if ((prevValue != getSelectValues(that.$element[0]) && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { 2513 // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed. 2514 changedArguments = [clickedIndex, $option.prop('selected'), prevValue]; 2515 that.$element 2516 .triggerNative('change'); 2517 } 2518 } 2519 } 2520 }); 2521 2522 this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function (e) { 2523 if (e.currentTarget == this) { 2524 e.preventDefault(); 2525 e.stopPropagation(); 2526 if (that.options.liveSearch && !$(e.target).hasClass('close')) { 2527 that.$searchbox.trigger('focus'); 2528 } else { 2529 that.$button.trigger('focus'); 2530 } 2531 } 2532 }); 2533 2534 this.$menuInner.on('click', '.divider, .dropdown-header', function (e) { 2535 e.preventDefault(); 2536 e.stopPropagation(); 2537 if (that.options.liveSearch) { 2538 that.$searchbox.trigger('focus'); 2539 } else { 2540 that.$button.trigger('focus'); 2541 } 2542 }); 2543 2544 this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function () { 2545 that.$button.trigger('click'); 2546 }); 2547 2548 this.$searchbox.on('click', function (e) { 2549 e.stopPropagation(); 2550 }); 2551 2552 this.$menu.on('click', '.actions-btn', function (e) { 2553 if (that.options.liveSearch) { 2554 that.$searchbox.trigger('focus'); 2555 } else { 2556 that.$button.trigger('focus'); 2557 } 2558 2559 e.preventDefault(); 2560 e.stopPropagation(); 2561 2562 if ($(this).hasClass('bs-select-all')) { 2563 that.selectAll(); 2564 } else { 2565 that.deselectAll(); 2566 } 2567 }); 2568 2569 this.$element.on({ 2570 'change': function () { 2571 that.render(); 2572 that.$element.trigger('changed' + EVENT_KEY, changedArguments); 2573 changedArguments = null; 2574 }, 2575 'focus': function () { 2576 if (!that.options.mobile) that.$button.trigger('focus'); 2577 } 2578 }); 2579 }, 2580 2581 liveSearchListener: function () { 2582 var that = this, 2583 noResults = document.createElement('li'); 2584 2585 this.$button.on('click.bs.dropdown.data-api', function () { 2586 if (!!that.$searchbox.val()) { 2587 that.$searchbox.val(''); 2588 } 2589 }); 2590 2591 this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function (e) { 2592 e.stopPropagation(); 2593 }); 2594 2595 this.$searchbox.on('input propertychange', function () { 2596 var searchValue = that.$searchbox.val(); 2597 2598 that.selectpicker.search.map.newIndex = {}; 2599 that.selectpicker.search.map.originalIndex = {}; 2600 that.selectpicker.search.elements = []; 2601 that.selectpicker.search.data = []; 2602 2603 if (searchValue) { 2604 var i, 2605 searchMatch = [], 2606 q = searchValue.toUpperCase(), 2607 cache = {}, 2608 cacheArr = [], 2609 searchStyle = that._searchStyle(), 2610 normalizeSearch = that.options.liveSearchNormalize; 2611 2612 if (normalizeSearch) q = normalizeToBase(q); 2613 2614 that._$lisSelected = that.$menuInner.find('.selected'); 2615 2616 for (var i = 0; i < that.selectpicker.main.data.length; i++) { 2617 var li = that.selectpicker.main.data[i]; 2618 2619 if (!cache[i]) { 2620 cache[i] = stringSearch(li, q, searchStyle, normalizeSearch); 2621 } 2622 2623 if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) { 2624 if (li.headerIndex > 0) { 2625 cache[li.headerIndex - 1] = true; 2626 cacheArr.push(li.headerIndex - 1); 2627 } 2628 2629 cache[li.headerIndex] = true; 2630 cacheArr.push(li.headerIndex); 2631 2632 cache[li.lastIndex + 1] = true; 2633 } 2634 2635 if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i); 2636 } 2637 2638 for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) {
2639 var index = cacheArr[i], 2640 prevIndex = cacheArr[i - 1], 2641 li = that.selectpicker.main.data[index], 2642 liPrev = that.selectpicker.main.data[prevIndex]; 2643 2644 if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) { 2645 that.selectpicker.search.data.push(li); 2646 searchMatch.push(that.selectpicker.main.elements[index]); 2647 2648 if (li.hasOwnProperty('originalIndex')) { 2649 that.selectpicker.search.map.newIndex[li.originalIndex] = searchMatch.length - 1; 2650 that.selectpicker.search.map.originalIndex[searchMatch.length - 1] = li.originalIndex; 2651 } 2652 } 2653 } 2654 2655 that.activeIndex = undefined; 2656 that.noScroll = true; 2657 that.$menuInner.scrollTop(0); 2658 that.selectpicker.search.elements = searchMatch; 2659 that.createView(true); 2660 2661 if (!searchMatch.length) { 2662 noResults.className = 'no-results'; 2663 noResults.innerHTML = that.options.noneResultsText.replace('{0}', '"' + htmlEscape(searchValue) + '"'); 2664 that.$menuInner[0].firstChild.appendChild(noResults); 2665 } 2666 } else { 2667 that.$menuInner.scrollTop(0); 2668 that.createView(false); 2669 } 2670 }); 2671 }, 2672 2673 _searchStyle: function () { 2674 return this.options.liveSearchStyle || 'contains'; 2675 }, 2676 2677 val: function (value) { 2678 if (typeof value !== 'undefined') { 2679 this.$element 2680 .val(value) 2681 .trigger('changed' + EVENT_KEY, changedArguments); 2682 2683 this.render(); 2684 2685 changedArguments = null; 2686 2687 return this.$element; 2688 } else { 2689 return this.$element.val(); 2690 } 2691 }, 2692 2693 changeAll: function (status) { 2694 if (!this.multiple) return; 2695 if (typeof status === 'undefined') status = true; 2696 2697 var element = this.$element[0], 2698 selectOptions = element.options, 2699 previousSelected = 0, 2700 currentSelected = 0, 2701 prevValue = getSelectValues(element); 2702 2703 element.classList.add('bs-select-hidden'); 2704 2705 for (var i = 0, len = this.selectpicker.current.elements.length; i < len; i++) { 2706 var liData = this.selectpicker.current.data[i], 2707 index = this.selectpicker.current.map.originalIndex[i], // faster than $(li).data('originalIndex') 2708 option = selectOptions[index]; 2709 2710 if (option && !option.disabled && liData.type !== 'divider') { 2711 if (option.selected) previousSelected++; 2712 option.selected = status; 2713 if (status) currentSelected++; 2714 } 2715 } 2716 2717 element.classList.remove('bs-select-hidden'); 2718 2719 if (previousSelected === currentSelected) return; 2720 2721 this.setOptionStatus(); 2722 2723 this.togglePlaceholder(); 2724 2725 changedArguments = [null, null, prevValue]; 2726 2727 this.$element 2728 .triggerNative('change'); 2729 }, 2730 2731 selectAll: function () { 2732 return this.changeAll(true); 2733 }, 2734 2735 deselectAll: function () { 2736 return this.changeAll(false); 2737 }, 2738 2739 toggle: function (e) { 2740 e = e || window.event; 2741 2742 if (e) e.stopPropagation(); 2743 2744 this.$button.trigger('click.bs.dropdown.data-api'); 2745 }, 2746 2747 keydown: function (e) { 2748 var $this = $(this), 2749 isToggle = $this.hasClass('dropdown-toggle'), 2750 $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU), 2751 that = $parent.data('this'), 2752 $items = that.findLis(), 2753 index, 2754 isActive, 2755 liActive, 2756 activeLi, 2757 offset, 2758 updateScroll = false, 2759 downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab, 2760 isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab, 2761 scrollTop = that.$menuInner[0].scrollTop, 2762 isVirtual = that.isVirtual(), 2763 position0 = isVirtual === true ? that.selectpicker.view.position0 : 0; 2764 2765 isActive = that.$newElement.hasClass(classNames.SHOW); 2766 2767 if ( 2768 !isActive && 2769 ( 2770 isArrowKey || 2771 (e.which >= 48 && e.which <= 57) || 2772 (e.which >= 96 && e.which <= 105) || 2773 (e.which >= 65 && e.which <= 90) 2774 ) 2775 ) { 2776 that.$button.trigger('click.bs.dropdown.data-api'); 2777 2778 if (that.options.liveSearch) { 2779 that.$searchbox.trigger('focus'); 2780 return; 2781 } 2782 } 2783 2784 if (e.which === keyCodes.ESCAPE && isActive) { 2785 e.preventDefault(); 2786 that.$button.trigger('click.bs.dropdown.data-api').trigger('focus'); 2787 } 2788 2789 if (isArrowKey) { // if up or down 2790 if (!$items.length) return; 2791 2792 // $items.index/.filter is too slow with a large list and no virtual scroll 2793 index = isVirtual === true ? $items.index($items.filter('.active')) : that.selectpicker.current.map.newIndex[that.activeIndex]; 2794 2795 if (index === undefined) index = -1; 2796 2797 if (index !== -1) { 2798 liActive = that.selectpicker.current.elements[index + position0]; 2799 liActive.classList.remove('active'); 2800 if (liActive.firstChild) liActive.firstChild.classList.remove('active'); 2801 } 2802 2803 if (e.which === keyCodes.ARROW_UP) { // up 2804 if (index !== -1) index--; 2805 if (index + position0 < 0) index += $items.length; 2806 2807 if (!that.selectpicker.view.canHighlight[index + position0]) { 2808 index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0; 2809 if (index === -1) index = $items.length - 1; 2810 } 2811 } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down 2812 index++; 2813 if (index + position0 >
vendor: 10,495 bytes, lines 2813-3131
2813= that.selectpicker.view.canHighlight.length) index = 0; 2814 2815 if (!that.selectpicker.view.canHighlight[index + position0]) { 2816 index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true); 2817 } 2818 } 2819 2820 e.preventDefault(); 2821 2822 var liActiveIndex = position0 + index; 2823 2824 if (e.which === keyCodes.ARROW_UP) { // up 2825 // scroll to bottom and highlight last option 2826 if (position0 === 0 && index === $items.length - 1) { 2827 that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight; 2828 2829 liActiveIndex = that.selectpicker.current.elements.length - 1; 2830 } else { 2831 activeLi = that.selectpicker.current.data[liActiveIndex]; 2832 offset = activeLi.position - activeLi.height; 2833 2834 updateScroll = offset < scrollTop; 2835 } 2836 } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down 2837 // scroll to top and highlight first option 2838 if (index === 0) { 2839 that.$menuInner[0].scrollTop = 0; 2840 2841 liActiveIndex = 0; 2842 } else { 2843 activeLi = that.selectpicker.current.data[liActiveIndex]; 2844 offset = activeLi.position - that.sizeInfo.menuInnerHeight; 2845 2846 updateScroll = offset > scrollTop; 2847 } 2848 } 2849 2850 liActive = that.selectpicker.current.elements[liActiveIndex]; 2851 2852 if (liActive) { 2853 liActive.classList.add('active'); 2854 if (liActive.firstChild) liActive.firstChild.classList.add('active'); 2855 } 2856 2857 that.activeIndex = that.selectpicker.current.map.originalIndex[liActiveIndex]; 2858 2859 that.selectpicker.view.currentActive = liActive; 2860 2861 if (updateScroll) that.$menuInner[0].scrollTop = offset; 2862 2863 if (that.options.liveSearch) { 2864 that.$searchbox.trigger('focus'); 2865 } else { 2866 $this.trigger('focus'); 2867 } 2868 } else if ( 2869 (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) || 2870 (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory) 2871 ) { 2872 var searchMatch, 2873 matches = [], 2874 keyHistory; 2875 2876 e.preventDefault(); 2877 2878 that.selectpicker.keydown.keyHistory += keyCodeMap[e.which]; 2879 2880 if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel); 2881 that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start(); 2882 2883 keyHistory = that.selectpicker.keydown.keyHistory; 2884 2885 // if all letters are the same, set keyHistory to just the first character when searching 2886 if (/^(.)\1+$/.test(keyHistory)) { 2887 keyHistory = keyHistory.charAt(0); 2888 } 2889 2890 // find matches 2891 for (var i = 0; i < that.selectpicker.current.data.length; i++) { 2892 var li = that.selectpicker.current.data[i], 2893 hasMatch; 2894 2895 hasMatch = stringSearch(li, keyHistory, 'startsWith', true); 2896 2897 if (hasMatch && that.selectpicker.view.canHighlight[i]) { 2898 li.index = i; 2899 matches.push(li.originalIndex); 2900 } 2901 } 2902 2903 if (matches.length) { 2904 var matchIndex = 0; 2905 2906 $items.removeClass('active').find('a').removeClass('active'); 2907 2908 // either only one key has been pressed or they are all the same key 2909 if (keyHistory.length === 1) { 2910 matchIndex = matches.indexOf(that.activeIndex); 2911 2912 if (matchIndex === -1 || matchIndex === matches.length - 1) { 2913 matchIndex = 0; 2914 } else { 2915 matchIndex++; 2916 } 2917 } 2918 2919 searchMatch = that.selectpicker.current.map.newIndex[matches[matchIndex]]; 2920 2921 activeLi = that.selectpicker.current.data[searchMatch]; 2922 2923 if (scrollTop - activeLi.position > 0) { 2924 offset = activeLi.position - activeLi.height; 2925 updateScroll = true; 2926 } else { 2927 offset = activeLi.position - that.sizeInfo.menuInnerHeight; 2928 // if the option is already visible at the current scroll position, just keep it the same 2929 updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight; 2930 } 2931 2932 liActive = that.selectpicker.current.elements[searchMatch]; 2933 liActive.classList.add('active'); 2934 if (liActive.firstChild) liActive.firstChild.classList.add('active'); 2935 that.activeIndex = matches[matchIndex]; 2936 2937 liActive.firstChild.focus(); 2938 2939 if (updateScroll) that.$menuInner[0].scrollTop = offset; 2940 2941 $this.trigger('focus'); 2942 } 2943 } 2944 2945 // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. 2946 if ( 2947 isActive && 2948 ( 2949 (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) || 2950 e.which === keyCodes.ENTER || 2951 (e.which === keyCodes.TAB && that.options.selectOnTab) 2952 ) 2953 ) { 2954 if (e.which !== keyCodes.SPACE) e.preventDefault(); 2955 2956 if (!that.options.liveSearch || e.which !== keyCodes.SPACE) { 2957 that.$menuInner.find('.active a').trigger('click', true); // retain active class 2958 $this.trigger('focus'); 2959 2960 if (!that.options.liveSearch) { 2961 // Prevent screen from scrolling if the user hits the spacebar 2962 e.preventDefault(); 2963 // Fixes spacebar selection of dropdown items in FF & IE 2964 $(document).data('spaceSelect', true); 2965 } 2966 } 2967 } 2968 }, 2969 2970 mobile: function () { 2971 this.$element[0].classList.add('mobile-device'); 2972 }, 2973 2974 refresh: function () { 2975 // update options if data attributes have been changed 2976 var config = $.extend({}, this.options, this.$element.data()); 2977 this.options = config; 2978 2979 this.selectpicker.main.map.newIndex = {}; 2980 this.selectpicker.main.map.originalIndex = {}; 2981 this.checkDisabled(); 2982 this.setStyle(); 2983 this.render(); 2984 this.createLi(); 2985 this.setWidth(); 2986 2987 this.setSize(true); 2988 2989 this.$element.trigger('refreshed' + EVENT_KEY); 2990 }, 2991 2992 hide: function () { 2993 this.$newElement.hide(); 2994 }, 2995 2996 show: function () { 2997 this.$newElement.show(); 2998 }, 2999 3000 remove: function () { 3001 this.$newElement.remove(); 3002 this.$element.remove(); 3003 }, 3004 3005 destroy: function () { 3006 this.$newElement.before(this.$element).remove(); 3007 3008 if (this.$bsContainer) { 3009 this.$bsContainer.remove(); 3010 } else { 3011 this.$menu.remove(); 3012 } 3013 3014 this.$element 3015 .off(EVENT_KEY) 3016 .removeData('selectpicker') 3017 .removeClass('bs-select-hidden selectpicker'); 3018 3019 $(window).off(EVENT_KEY + '.' + this.selectId); 3020 } 3021 }; 3022 3023 // SELECTPICKER PLUGIN DEFINITION 3024 // ============================== 3025 function Plugin (option) { 3026 // get the args of the outer function.. 3027 var args = arguments; 3028 // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them 3029 // to get lost/corrupted in android 2.3 and IE9 #715 #775 3030 var _option = option; 3031 3032 [].shift.apply(args); 3033 3034 // if the version was not set successfully 3035 if (!version.success) { 3036 // try to retreive it again 3037 try { 3038 version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); 3039 } catch (err) { 3040 // fall back to use BootstrapVersion 3041 version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.'); 3042 } 3043 3044 version.major = version.full[0]; 3045 version.success = true; 3046 3047 if (version.major === '4') { 3048 classNames.DIVIDER = 'dropdown-divider'; 3049 classNames.SHOW = 'show'; 3050 classNames.BUTTONCLASS = 'btn-light'; 3051 Selectpicker.DEFAULTS.style = classNames.BUTTONCLASS = 'btn-light'; 3052 classNames.POPOVERHEADER = 'popover-header'; 3053 } 3054 } 3055 3056 var value; 3057 var chain = this.each(function () { 3058 var $this = $(this); 3059 if ($this.is('select')) { 3060 var data = $this.data('selectpicker'), 3061 options = typeof _option == 'object' && _option; 3062 3063 if (!data) { 3064 var dataAttributes = $this.data(); 3065 3066 for (var dataAttr in dataAttributes) { 3067 if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) { 3068 delete dataAttributes[dataAttr]; 3069 } 3070 } 3071 3072 var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, dataAttributes, options); 3073 config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template); 3074 $this.data('selectpicker', (data = new Selectpicker(this, config))); 3075 } else if (options) { 3076 for (var i in options) { 3077 if (options.hasOwnProperty(i)) { 3078 data.options[i] = options[i]; 3079 } 3080 } 3081 } 3082 3083 if (typeof _option == 'string') { 3084 if (data[_option] instanceof Function) { 3085 value = data[_option].apply(data, args); 3086 } else { 3087 value = data.options[_option]; 3088 } 3089 } 3090 } 3091 }); 3092 3093 if (typeof value !== 'undefined') { 3094 // noinspection JSUnusedAssignment 3095 return value; 3096 } else { 3097 return chain; 3098 } 3099 } 3100 3101 var old = $.fn.selectpicker; 3102 $.fn.selectpicker = Plugin; 3103 $.fn.selectpicker.Constructor = Selectpicker; 3104 3105 // SELECTPICKER NO CONFLICT 3106 // ======================== 3107 $.fn.selectpicker.noConflict = function () { 3108 $.fn.selectpicker = old; 3109 return this; 3110 }; 3111 3112 $(document) 3113 .off('keydown.bs.dropdown.data-api') 3114 .on('keydown' + EVENT_KEY, '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown) 3115 .on('focusin.modal', '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', function (e) { 3116 e.stopPropagation(); 3117 }); 3118 3119 // SELECTPICKER DATA-API 3120 // ===================== 3121 $(window).on('load' + EVENT_KEY + '.data-api', function () { 3122 $('.selectpicker').each(function () { 3123 var $selectpicker = $(this); 3124 Plugin.call($selectpicker, $selectpicker.data()); 3125 }) 3126 }); 3127})(jQuery); 3128 3129 3130})); 3131//# 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.