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