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