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