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