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