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