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