1/*! 2 * Bootstrap-select v1.6.3 (http://silviomoreto.github.io/bootstrap-select/) 3 * 4 * Copyright 2013-2014 bootstrap-select 5 * Licensed under MIT (https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE) 6 */ 7(function ($) { 8 'use strict'; 9 10 // Case insensitive search 11 $.expr[':'].icontains = function (obj, index, meta) { 12 return icontains($(obj).text(), meta[3]); 13 }; 14 15 // Case and accent insensitive search 16 $.expr[':'].aicontains = function (obj, index, meta) { 17 return icontains($(obj).data('normalizedText') || $(obj).text(), meta[3]); 18 }; 19 20 /** 21 * Actual implementation of the case insensitive search. 22 * @access private 23 * @param {String} haystack 24 * @param {String} needle 25 * @returns {boolean} 26 */ 27 function icontains(haystack, needle) { 28 return haystack.toUpperCase().indexOf(needle.toUpperCase()) > -1; 29 } 30 31 /** 32 * Remove all diatrics from the given text. 33 * @access private 34 * @param {String} text 35 * @returns {String} 36 */ 37 function normalizeToBase(text) { 38 var rExps = [ 39 {re: /[\xC0-\xC6]/g, ch: "A"}, 40 {re: /[\xE0-\xE6]/g, ch: "a"}, 41 {re: /[\xC8-\xCB]/g, ch: "E"}, 42 {re: /[\xE8-\xEB]/g, ch: "e"}, 43 {re: /[\xCC-\xCF]/g, ch: "I"}, 44 {re: /[\xEC-\xEF]/g, ch: "i"}, 45 {re: /[\xD2-\xD6]/g, ch: "O"}, 46 {re: /[\xF2-\xF6]/g, ch: "o"}, 47 {re: /[\xD9-\xDC]/g, ch: "U"}, 48 {re: /[\xF9-\xFC]/g, ch: "u"}, 49 {re: /[\xC7-\xE7]/g, ch: "c"}, 50 {re: /[\xD1]/g, ch: "N"}, 51 {re: /[\xF1]/g, ch: "n"} 52 ]; 53 $.each(rExps, function () { 54 text = text.replace(this.re, this.ch); 55 }); 56 return text; 57 } 58 59 60 function htmlEscape(html) { 61 var escapeMap = { 62 '&': '&', 63 '<': '<', 64 '>': '>', 65 '"': '"', 66 "'": ''', 67 '`': '`' 68 }; 69 var source = '(?:' + Object.keys(escapeMap).join('|') + ')', 70 testRegexp = new RegExp(source), 71 replaceRegexp = new RegExp(source, 'g'), 72 string = html == null ? '' : '' + html; 73 return testRegexp.test(string) ? string.replace(replaceRegexp, function (match) { 74 return escapeMap[match]; 75 }) : string; 76 } 77 78 var Selectpicker = function (element, options, e) { 79 if (e) { 80 e.stopPropagation(); 81 e.preventDefault(); 82 } 83 84 this.$element = $(element); 85 this.$newElement = null; 86 this.$button = null; 87 this.$menu = null; 88 this.$lis = null; 89 this.options = options; 90 91 // 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 92 // data-attribute) 93 if (this.options.title === null) { 94 this.options.title = this.$element.attr('title'); 95 } 96 97 //Expose public methods 98 this.val = Selectpicker.prototype.val; 99 this.render = Selectpicker.prototype.render; 100 this.refresh = Selectpicker.prototype.refresh; 101 this.setStyle = Selectpicker.prototype.setStyle; 102 this.selectAll = Selectpicker.prototype.selectAll; 103 this.deselectAll = Selectpicker.prototype.deselectAll; 104 this.destroy = Selectpicker.prototype.remove; 105 this.remove = Selectpicker.prototype.remove; 106 this.show = Selectpicker.prototype.show; 107 this.hide = Selectpicker.prototype.hide; 108 109 this.init(); 110 }; 111 112 Selectpicker.VERSION = '1.6.3'; 113 114 // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. 115 Selectpicker.DEFAULTS = { 116 noneSelectedText: 'Nothing selected', 117 noneResultsText: 'No results match', 118 countSelectedText: function (numSelected, numTotal) { 119 return (numSelected == 1) ? "{0} item selected" : "{0} items selected"; 120 }, 121 maxOptionsText: function (numAll, numGroup) { 122 var arr = []; 123 124 arr[0] = (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)'; 125 arr[1] = (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)'; 126 127 return arr; 128 }, 129 selectAllText: 'Select All', 130 deselectAllText: 'Deselect All', 131 multipleSeparator: ', ', 132 style: 'btn-default', 133 size: 'auto', 134 title: null, 135 selectedTextFormat: 'values', 136 width: false, 137 container: false, 138 hideDisabled: false, 139 showSubtext: false, 140 showIcon: true, 141 showContent: true, 142 dropupAuto: true, 143 header: false, 144 liveSearch: false, 145 actionsBox: false, 146 iconBase: 'glyphicon', 147 tickIcon: 'glyphicon-ok', 148 maxOptions: false, 149 mobile: false, 150 selectOnTab: false, 151 dropdownAlignRight: false,
152 searchAccentInsensitive: false 153 }; 154 155 Selectpicker.prototype = { 156 157 constructor: Selectpicker, 158 159 init: function () { 160 var that = this, 161 id = this.$element.attr('id'); 162 163 this.$element.hide(); 164 this.multiple = this.$element.prop('multiple'); 165 this.autofocus = this.$element.prop('autofocus'); 166 this.$newElement = this.createView(); 167 this.$element.after(this.$newElement); 168 this.$menu = this.$newElement.find('> .dropdown-menu'); 169 this.$button = this.$newElement.find('> button'); 170 this.$searchbox = this.$newElement.find('input'); 171 172 if (this.options.dropdownAlignRight) 173 this.$menu.addClass('dropdown-menu-right'); 174 175 if (typeof id !== 'undefined') { 176 this.$button.attr('data-id', id); 177 $('label[for="' + id + '"]').click(function (e) { 178 e.preventDefault(); 179 that.$button.focus(); 180 }); 181 } 182 183 this.checkDisabled(); 184 this.clickListener(); 185 if (this.options.liveSearch) this.liveSearchListener(); 186 this.render(); 187 this.liHeight(); 188 this.setStyle(); 189 this.setWidth(); 190 if (this.options.container) this.selectPosition(); 191 this.$menu.data('this', this); 192 this.$newElement.data('this', this); 193 if (this.options.mobile) this.mobile(); 194 }, 195 196 createDropdown: function () { 197 // Options 198 // If we are multiple, then add the show-tick class by default 199 var multiple = this.multiple ? ' show-tick' : '', 200 inputGroup = this.$element.parent().hasClass('input-group') ? ' input-group-btn' : '', 201 autofocus = this.autofocus ? ' autofocus' : '', 202 btnSize = this.$element.parents().hasClass('form-group-lg') ? ' btn-lg' : (this.$element.parents().hasClass('form-group-sm') ? ' btn-sm' : ''); 203 // Elements 204 var header = this.options.header ? '<div class="popover-title"><button type="button" class="close" aria-hidden="true">×</button>' + this.options.header + '</div>' : ''; 205 var searchbox = this.options.liveSearch ? '<div class="bs-searchbox"><input type="text" class="input-block-level form-control" autocomplete="off" /></div>' : ''; 206 var actionsbox = this.options.actionsBox ? '<div class="bs-actionsbox">' + 207 '<div class="btn-group btn-block">' + 208 '<button class="actions-btn bs-select-all btn btn-sm btn-default">' + 209 this.options.selectAllText + 210 '</button>' + 211 '<button class="actions-btn bs-deselect-all btn btn-sm btn-default">' + 212 this.options.deselectAllText + 213 '</button>' + 214 '</div>' + 215 '</div>' : ''; 216 var drop = 217 '<div class="btn-group bootstrap-select' + multiple + inputGroup + '">' + 218 '<button type="button" class="btn dropdown-toggle selectpicker' + btnSize + '" data-toggle="dropdown"' + autofocus + '>' + 219 '<span class="filter-option pull-left"></span> ' + 220 '<span class="select-down-arrow"></span>' + 221 '</button>' + 222 '<div class="dropdown-menu open">' + 223 header + 224 searchbox + 225 actionsbox + 226 '<ul class="dropdown-menu inner selectpicker" role="menu">' + 227 '</ul>' + 228 '</div>' + 229 '</div>'; 230 231 return $(drop); 232 }, 233 234 createView: function () { 235 var $drop = this.createDropdown(); 236 var $li = this.createLi(); 237 $drop.find('ul').append($li); 238 return $drop; 239 }, 240 241 reloadLi: function () { 242 //Remove all children. 243 this.destroyLi(); 244 //Re build 245 var $li = this.createLi(); 246 this.$menu.find('ul').append($li); 247 }, 248 249 destroyLi: function () { 250 this.$menu.find('li').remove(); 251 }, 252 253 createLi: function () { 254 var that = this, 255 _li = [], 256 optID = 0; 257 258 // Helper functions 259 /** 260 * @param content 261 * @param [index] 262 * @param [classes] 263 * @returns {string} 264 */ 265 var generateLI = function (content, index, classes) { 266 return '<li' + 267 (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') + 268 (typeof index !== 'undefined' | null === index ? ' data-original-index="' + index + '"' : '') + 269 '>' + content + '</li>'; 270 }; 271 272 /** 273 * @param text 274 * @param [classes] 275 * @param [inline] 276 * @param [optgroup] 277 * @returns {string} 278 */ 279 var generateA = function (text, classes, inline, optgroup) { 280 var normText = normalizeToBase(htmlEscape(text)); 281 return '<a tabindex="0"' + 282 (typeof classes !== 'undefined' ? ' class="' + classes + '"' : '') + 283 (typeof inline !== 'undefined' ? ' style="' + inline + '"' : '') + 284 (typeof optgroup !== 'undefined' ? 'data-optgroup="' + optgroup + '"' : '') + 285 ' data-normalized-text="' + normText + '"' + 286 '>
286' + text + 287 '<span class="' + that.options.iconBase + ' ' + that.options.tickIcon + ' check-mark"></span>' + 288 '</a>'; 289 }; 290 291 this.$element.find('option').each(function () { 292 var $this = $(this); 293 294 // Get the class and text for the option 295 var optionClass = $this.attr('class') || '', 296 inline = $this.attr('style'), 297 text = $this.data('content') ? $this.data('content') : $this.html(), 298 subtext = typeof $this.data('subtext') !== 'undefined' ? '<small class="muted text-muted">' + $this.data('subtext') + '</small>' : '', 299 icon = typeof $this.data('icon') !== 'undefined' ? '<span class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></span> ' : '', 300 isDisabled = $this.is(':disabled') || $this.parent().is(':disabled'), 301 index = $this[0].index; 302 if (icon !== '' && isDisabled) { 303 icon = '<span>' + icon + '</span>'; 304 } 305 306 if (!$this.data('content')) { 307 // Prepend any icon and append any subtext to the main text. 308 text = icon + '<span class="text">' + text + subtext + '</span>'; 309 } 310 311 if (that.options.hideDisabled && isDisabled) { 312 return; 313 } 314 315 if ($this.parent().is('optgroup') && $this.data('divider') !== true) { 316 if ($this.index() === 0) { // Is it the first option of the optgroup? 317 optID += 1; 318 319 // Get the opt group label 320 var label = $this.parent().attr('label'); 321 var labelSubtext = typeof $this.parent().data('subtext') !== 'undefined' ? '<small class="muted text-muted">' + $this.parent().data('subtext') + '</small>' : ''; 322 var labelIcon = $this.parent().data('icon') ? '<span class="' + that.options.iconBase + ' ' + $this.parent().data('icon') + '"></span> ' : ''; 323 label = labelIcon + '<span class="text">' + label + labelSubtext + '</span>'; 324 325 if (index !== 0 && _li.length > 0) { // Is it NOT the first option of the select && are there elements in the dropdown? 326 _li.push(generateLI('', null, 'divider')); 327 } 328 329 _li.push(generateLI(label, null, 'dropdown-header')); 330 } 331 332 _li.push(generateLI(generateA(text, 'opt ' + optionClass, inline, optID), index)); 333 } else if ($this.data('divider') === true) { 334 _li.push(generateLI('', index, 'divider')); 335 } else if ($this.data('hidden') === true) { 336 _li.push(generateLI(generateA(text, optionClass, inline), index, 'hide is-hidden')); 337 } else { 338 _li.push(generateLI(generateA(text, optionClass, inline), index)); 339 } 340 }); 341 342 //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 343 if (!this.multiple && this.$element.find('option:selected').length === 0 && !this.options.title) { 344 this.$element.find('option').eq(0).prop('selected', true).attr('selected', 'selected'); 345 } 346 347 return $(_li.join('')); 348 }, 349 350 findLis: function () { 351 if (this.$lis == null) this.$lis = this.$menu.find('li'); 352 return this.$lis; 353 }, 354 355 /** 356 * @param [updateLi] defaults to true 357 */ 358 render: function (updateLi) { 359 var that = this; 360 361 //Update the LI to match the SELECT 362 if (updateLi !== false) { 363 this.$element.find('option').each(function (index) { 364 that.setDisabled(index, $(this).is(':disabled') || $(this).parent().is(':disabled')); 365 that.setSelected(index, $(this).is(':selected')); 366 }); 367 } 368 369 this.tabIndex(); 370 var notDisabled = this.options.hideDisabled ? ':not([disabled])' : ''; 371 var selectedItems = this.$element.find('option:selected' + notDisabled).map(function () { 372 var $this = $(this); 373 var icon = $this.data('icon') && that.options.showIcon ? '<i class="' + that.options.iconBase + ' ' + $this.data('icon') + '"></i> ' : ''; 374 var subtext; 375 if (that.options.showSubtext && $this.attr('data-subtext') && !that.multiple) { 376 subtext = ' <small class="muted text-muted">' + $this.data('subtext') + '</small>'; 377 } else { 378 subtext = ''; 379 } 380 if ($this.data('content') && that.options.showContent) { 381 return $this.data('content'); 382 } else if (typeof $this.attr('title') !== 'undefined') { 383 return $this.attr('title'); 384 } else { 385 return icon + $this.html() + subtext; 386 } 387 }).toArray(); 388 389 //Fixes issue in IE10 occurring when no default option is selected and at least one option is disabled 390 //Convert all the values into a comma delimited string 391 var title = !this.multiple ? selectedItems[0] : selectedItems.join(this.options.multipleSeparator); 392 393 //If this is multi select, and the selectText type is count, the show 1 of 2 selected etc.. 394 if (this.multiple && this.options.selectedTextFormat.indexOf('count') > -1) { 395 var max = this.options.selectedTextFormat.split('>'); 396 if ((max.length > 1 && selectedItems.length > max[1]) || (max.length == 1 && selectedItems.length >= 2)) { 397 notDisabled = this.options.hideDisabled ? ', [disabled]' : ''; 398 var totalCount = this.$element.find('option').not('[data-divider="true"], [data-hidden="true"]' + notDisabled).length, 399 tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedItems.length, totalCount) : this.options.countSelectedText; 400 title = tr8nText.replace('{0}', selectedItems.length.toString()).replace('{1}', totalCount.toString()); 401 } 402 } 403 404 this.options.title = this.$element.attr('title'); 405 406 if (this.options.selectedTextFormat == 'static') { 407 title = this.options.title; 408 } 409 410 //If we dont have a title, then use the default, or if nothing is set at all, use the not selected text 411 if (!title) { 412 title = typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText; 413 } 414 415 this.$button.attr('title', htmlEscape(title)); 416 this.$newElement.find('.filter-option').html(title); 417 }, 418 419 /** 420 * @param [style] 421 * @param [status] 422 */ 423 setStyle: function (style, status) { 424 if (this.$element.attr('class')) { 425 this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|validate\[.*\]/gi, '')); 426 } 427 428 var buttonClass = style ? style : this.options.style; 429 430 if (status == 'add') { 431 this.$button.addClass(buttonClass); 432 } else if (status == 'remove') { 433 this.$button.removeClass(buttonClass); 434 } else { 435 this.$button.removeClass(this.options.style); 436 this.$button.addClass(buttonClass); 437 } 438 }, 439 440 liHeight: function () { 441 if (this.options.size === false) return; 442 443 var $selectClone = this.$menu.parent().clone().find('> .dropdown-toggle').prop('autofocus', false).end().appendTo('body'), 444 $menuClone = $selectClone.addClass('open').find('> .dropdown-menu'), 445 liHeight = $menuClone.find('li').not('.divider').not('.dropdown-header').filter(':visible').children('a').outerHeight(), 446 headerHeight = this.options.header ? $menuClone.find('.popover-title').outerHeight() : 0, 447 searchHeight = this.options.liveSearch ? $menuClone.find('.bs-searchbox').outerHeight() : 0, 448 actionsHeight = this.options.actionsBox ? $menuClone.find('.bs-actionsbox').outerHeight() : 0; 449 450 $selectClone.remove(); 451 452 this.$newElement 453 .data('liHeight', liHeight) 454 .data('headerHeight', headerHeight) 455 .data('searchHeight', searchHeight) 456 .data('actionsHeight', actionsHeight); 457 }, 458 459 setSize: function () { 460 this.findLis(); 461 var that = this, 462 menu = this.$menu, 463 menuInner = menu.find('.inner'), 464 selectHeight = this.$newElement.outerHeight(), 465 liHeight = this.$newElement.data('liHeight'), 466 headerHeight = this.$newElement.data('headerHeight'), 467 searchHeight = this.$newElement.data('searchHeight'), 468 actionsHeight = this.$newElement.data('actionsHeight'), 469 divHeight = this.$lis.filter('.divider').outerHeight(true), 470 menuPadding = parseInt(menu.css('padding-top')) + 471 parseInt(menu.css('padding-bottom')) + 472 parseInt(menu.css('border-top-width')) + 473 parseInt(menu.css('border-bottom-width')), 474 notDisabled = this.options.hideDisabled ? ', .disabled' : '', 475 $window = $(window), 476 menuExtras = menuPadding + parseInt(menu.css('margin-top')) + parseInt(menu.css('margin-bottom')) + 2, 477 menuHeight, 478 selectOffsetTop, 479 selectOffsetBot, 480 posVert = function () { 481 // JQuery defines a scrollTop function, but in pure JS it's a property 482 //noinspection JSValidateTypes 483 selectOffsetTop = that.$newElement.offset().top - $window.scrollTop(); 484 selectOffsetBot = $window.height() - selectOffsetTop - selectHeight; 485 }; 486 posVert(); 487 if (this.options.header) menu.css('padding-top', 0); 488 489 if (this.options.size == 'auto') { 490 var getSize = function () { 491 var minHeight, 492 lisVis = that.$lis.not('.hide'); 493 494 posVert(); 495 menuHeight = selectOffsetBot - menuExtras; 496 497 if (that.options.dropupAuto) { 498 that.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && ((menuHeight - menuExtras) < menu.height())); 499 } 500 if (that.$newElement.hasClass('dropup')) { 501 menuHeight = selectOffsetTop - menuExtras; 502 } 503 504 if ((lisVis.length + lisVis.filter('.dropdown-header').length) > 3) { 505 minHeight = liHeight * 3 + menuExtras - 2; 506 } else { 507 minHeight = 0; 508 } 509 510 menu.css({ 511 'max-height': menuHeight + 'px', 512 'overflow': 'hidden', 513 'min-height': minHeight + headerHeight + searchHeight + actionsHeight + 'px' 514 }); 515 menuInner.css({ 516 'max-height': menuHeight - headerHeight - searchHeight - actionsHeight - menuPadding + 'px', 517 'overflow-y': 'auto', 518 'min-height': Math.max(minHeight - menuPadding, 0) + 'px' 519 }); 520 }; 521 getSize(); 522 this.$searchbox.off('input.getSize propertychange.getSize').on('input.getSize propertychange.getSize', getSize); 523 $(window).off('resize.getSize').on('resize.getSize', getSize); 524 $(window).off('scroll.getSize').on('scroll.getSize', getSize); 525 } else if (this.options.size && this.options.size != 'auto' && menu.find('li' + notDisabled).length > this.options.size) { 526 var optIndex = this.$lis.not('.divider' + notDisabled).find(' > *').slice(0, this.options.size).last().parent().index(); 527 var divLength = this.$lis.slice(0, optIndex + 1).filter('.divider').length; 528 menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding; 529 if (that.options.dropupAuto) { 530 //noinspection JSUnusedAssignment 531 this.$newElement.toggleClass('dropup', (selectOffsetTop > selectOffsetBot) && (menuHeight < menu.height())); 532 } 533 menu.css({'max-height': menuHeight + headerHeight + searchHeight + actionsHeight + 'px', 'overflow': 'hidden'}); 534 menuInner.css({'max-height': menuHeight - menuPadding + 'px', 'overflow-y': 'auto'}); 535 } 536 }, 537 538 setWidth: function () { 539 if (this.options.width == 'auto') { 540 this.$menu.css('min-width', '0'); 541 542 // Get correct width if element hidden 543 var selectClone = this.$newElement.clone().appendTo('body'); 544 var ulWidth = selectClone.find('> .dropdown-menu').css('width'); 545 var btnWidth = selectClone.css('width', 'auto').find('> button').css('width'); 546 selectClone.remove(); 547 548 // Set width to whatever's larger, button title or longest option 549 this.$newElement.css('width', Math.max(parseInt(ulWidth), parseInt(btnWidth)) + 'px'); 550 } else if (this.options.width == 'fit') { 551 // Remove inline min-width so width can be changed from 'auto' 552 this.$menu.css('min-width', ''); 553 this.$newElement.css('width', '').addClass('fit-width'); 554 } else if (this.options.width) { 555 // Remove inline min-width so width can be changed from 'auto' 556 this.$menu.css('min-width', ''); 557 this.$newElement.css('width', this.options.width); 558 } else { 559 // Remove inline min-width/width so width can be changed 560 this.$menu.css('min-width', ''); 561 this.$newElement.css('width', ''); 562 } 563 // Remove fit-width class if width is changed programmatically 564 if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { 565 this.$newElement.removeClass('fit-width'); 566 } 567 }, 568 569 selectPosition: function () { 570 var that = this, 571 drop = '<div />', 572 $drop = $(drop), 573 pos, 574 actualHeight, 575 getPlacement = function ($element) { 576 $drop.addClass($element.attr('class').replace(/form-control/gi, '')).toggleClass('dropup', $element.hasClass('dropup')); 577 pos = $element.offset(); 578 actualHeight = $element.hasClass('dropup') ? 0 : $element[0].offsetHeight; 579 $drop.css({ 580 'top': pos.top + actualHeight, 581 'left': pos.left, 582 'width': $element[0].offsetWidth, 583 'position': 'absolute' 584 }); 585 }; 586 this.$newElement.on('click', function () { 587 if (that.isDisabled()) { 588 return; 589 } 590 getPlacement($(this)); 591 $drop.appendTo(that.options.container); 592 $drop.toggleClass('open', !$(this).hasClass('open')); 593 $drop.append(that.$menu); 594 }); 595 $(window).resize(function () { 596 getPlacement(that.$newElement); 597 }); 598 $(window).on('scroll', function () { 599 getPlacement(that.$newElement); 600 }); 601 $('html').on('click', function (e) { 602 if ($(e.target).closest(that.$newElement).length < 1) { 603 $drop.removeClass('open'); 604 } 605 }); 606 }, 607 608 setSelected: function (index, selected) { 609 this.findLis(); 610 this.$lis.filter('[data-original-index="' + index + '"]').toggleClass('selected', selected); 611 }, 612 613 setDisabled: function (index, disabled) { 614 this.findLis(); 615 if (disabled) { 616 this.$lis.filter('[data-original-index="' + index + '"]').addClass('disabled').find('a').attr('href', '#').attr('tabindex', -1); 617 } else { 618 this.$lis.filter('[data-original-index="' + index + '"]').removeClass('disabled').find('a').removeAttr('href').attr('tabindex', 0); 619 } 620 }, 621 622 isDisabled: function () { 623 return this.$element.is(':disabled'); 624 }, 625 626 checkDisabled: function () { 627 var that = this; 628 629 if (this.isDisabled()) { 630 this.$button.addClass('disabled').attr('tabindex', -1); 631 } else { 632 if (this.$button.hasClass('disabled')) { 633 this.$button.removeClass('disabled'); 634 } 635 636 if (this.$button.attr('tabindex') == -1) { 637 if (!this.$element.data('tabindex')) this.$button.removeAttr('tabindex'); 638 } 639 } 640 641 this.$button.click(function () { 642 return !that.isDisabled(); 643 }); 644 }, 645 646 tabIndex: function () { 647 if (this.$element.is('[tabindex]')) { 648 this.$element.data('tabindex', this.$element.attr('tabindex')); 649 this.$button.attr('tabindex', this.$element.data('tabindex')); 650 } 651 }, 652 653 clickListener: function () { 654 var that = this; 655 656 this.$newElement.on('touchstart.dropdown', '.dropdown-menu', function (e) { 657 e.stopPropagation(); 658 }); 659 660 this.$newElement.on('click', function () { 661 that.setSize(); 662 if (!that.options.liveSearch && !that.multiple) { 663 setTimeout(function () { 664 that.$menu.find('.selected a').focus(); 665 }, 10); 666 } 667 }); 668 669 this.$menu.on('click', 'li a', function (e) { 670 var $this = $(this), 671 clickedIndex = $this.parent().data('originalIndex'),
672 prevValue = that.$element.val(), 673 prevIndex = that.$element.prop('selectedIndex'); 674 675 // Don't close on multi choice menu 676 if (that.multiple) { 677 e.stopPropagation(); 678 } 679 680 e.preventDefault(); 681 682 //Don't run if we have been disabled 683 if (!that.isDisabled() && !$this.parent().hasClass('disabled')) { 684 var $options = that.$element.find('option'), 685 $option = $options.eq(clickedIndex), 686 state = $option.prop('selected'), 687 $optgroup = $option.parent('optgroup'), 688 maxOptions = that.options.maxOptions, 689 maxOptionsGrp = $optgroup.data('maxOptions') || false; 690 691 if (!that.multiple) { // Deselect all others if not multi select box 692 $options.prop('selected', false); 693 $option.prop('selected', true); 694 that.$menu.find('.selected').removeClass('selected'); 695 that.setSelected(clickedIndex, true); 696 } else { // Toggle the one we have chosen if we are multi select. 697 $option.prop('selected', !state); 698 that.setSelected(clickedIndex, !state); 699 $this.blur(); 700 701 if ((maxOptions !== false) || (maxOptionsGrp !== false)) { 702 var maxReached = maxOptions < $options.filter(':selected').length, 703 maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; 704 705 if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { 706 if (maxOptions && maxOptions == 1) { 707 $options.prop('selected', false); 708 $option.prop('selected', true); 709 that.$menu.find('.selected').removeClass('selected'); 710 that.setSelected(clickedIndex, true); 711 } else if (maxOptionsGrp && maxOptionsGrp == 1) { 712 $optgroup.find('option:selected').prop('selected', false); 713 $option.prop('selected', true); 714 var optgroupID = $this.data('optgroup'); 715 716 that.$menu.find('.selected').has('a[data-optgroup="' + optgroupID + '"]').removeClass('selected'); 717 718 that.setSelected(clickedIndex, true); 719 } else { 720 var maxOptionsArr = (typeof that.options.maxOptionsText === 'function') ? 721 that.options.maxOptionsText(maxOptions, maxOptionsGrp) : that.options.maxOptionsText, 722 maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), 723 maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), 724 $notify = $('<div class="notify"></div>'); 725 // If {var} is set in array, replace it 726 /** @deprecated */ 727 if (maxOptionsArr[2]) { 728 maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); 729 maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); 730 } 731 732 $option.prop('selected', false); 733 734 that.$menu.append($notify); 735 736 if (maxOptions && maxReached) { 737 $notify.append($('<div>' + maxTxt + '</div>')); 738 that.$element.trigger('maxReached.bs.select'); 739 } 740 741 if (maxOptionsGrp && maxReachedGrp) { 742 $notify.append($('<div>' + maxTxtGrp + '</div>')); 743 that.$element.trigger('maxReachedGrp.bs.select'); 744 } 745 746 setTimeout(function () { 747 that.setSelected(clickedIndex, false); 748 }, 10); 749 750 $notify.delay(750).fadeOut(300, function () { 751 $(this).remove(); 752 }); 753 } 754 } 755 } 756 } 757 758 if (!that.multiple) { 759 that.$button.focus(); 760 } else if (that.options.liveSearch) { 761 that.$searchbox.focus(); 762 } 763 764 // Trigger select 'change' 765 if ((prevValue != that.$element.val() && that.multiple) || (prevIndex != that.$element.prop('selectedIndex') && !that.multiple)) { 766 that.$element.change(); 767 } 768 } 769 }); 770 771 this.$menu.on('click', 'li.disabled a, .popover-title, .popover-title :not(.close)', function (e) { 772 if (e.target == this) { 773 e.preventDefault(); 774 e.stopPropagation(); 775 if (!that.options.liveSearch) { 776 that.$button.focus(); 777 } else { 778 that.$searchbox.focus(); 779 } 780 } 781 }); 782 783 this.$menu.on('click', 'li.divider, li.dropdown-header', function (e) { 784 e.preventDefault(); 785 e.stopPropagation(); 786 if (!that.options.liveSearch) { 787 that.$button.focus(); 788 } else { 789 that.$searchbox.focus(); 790 } 791 }); 792 793 this.$menu.on('click', '.popover-title .close', function () { 794 that.$button.focus(); 795 }); 796 797 this.$searchbox.on('click', function (e) { 798 e.stopPropagation(); 799 }); 800 801 802 this.$menu.on('click', '.actions-btn', function (e) { 803 if (that.options.liveSearch) { 804 that.$searchbox.focus(); 805 } else { 806 that.$button.focus(); 807 } 808 809 e.preventDefault(); 810 e.stopPropagation(); 811 812 if ($(this).is('.bs-select-all')) { 813 that.selectAll(); 814 } else { 815 that.deselectAll(); 816 } 817 that.$element.change(); 818 }); 819 820 this.$element.change(function () { 821 that.render(false); 822 }); 823 }, 824 825 liveSearchListener: function () { 826 var that = this, 827 no_results = $('<li class="no-results"></li>'); 828 829 this.$newElement.on('click.dropdown.data-api touchstart.dropdown.data-api', function () { 830 that.$menu.find('.active').removeClass('active'); 831 if (!!that.$searchbox.val()) { 832 that.$searchbox.val(''); 833 that.$lis.not('.is-hidden').removeClass('hide'); 834 if (!!no_results.parent().length) no_results.remove(); 835 } 836 if (!that.multiple) that.$menu.find('.selected').addClass('active'); 837 setTimeout(function () { 838 that.$searchbox.focus(); 839 }, 10); 840 }); 841 842 this.$searchbox.on('click.dropdown.data-api focus.dropdown.data-api touchend.dropdown.data-api', function (e) { 843 e.stopPropagation(); 844 }); 845 846 this.$searchbox.on('input propertychange', function () { 847 if (that.$searchbox.val()) { 848
849 if (that.options.searchAccentInsensitive) { 850 that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':aicontains(' + normalizeToBase(that.$searchbox.val()) + ')').parent().addClass('hide'); 851 } else { 852 that.$lis.not('.is-hidden').removeClass('hide').find('a').not(':icontains(' + that.$searchbox.val() + ')').parent().addClass('hide'); 853 } 854 855 if (!that.$menu.find('li').filter(':visible:not(.no-results)').length) { 856 if (!!no_results.parent().length) no_results.remove(); 857 no_results.html(that.options.noneResultsText + ' "' + htmlEscape(that.$searchbox.val()) + '"').show(); 858 that.$menu.find('li').last().after(no_results); 859 } else if (!!no_results.parent().length) { 860 no_results.remove(); 861 } 862 863 } else { 864 that.$lis.not('.is-hidden').removeClass('hide'); 865 if (!!no_results.parent().length) no_results.remove(); 866 } 867 868 that.$menu.find('li.active').removeClass('active'); 869 that.$menu.find('li').filter(':visible:not(.divider)').eq(0).addClass('active').find('a').focus(); 870 $(this).focus(); 871 }); 872 }, 873 874 val: function (value) { 875 if (typeof value !== 'undefined') { 876 this.$element.val(value); 877 this.render(); 878 879 return this.$element; 880 } else { 881 return this.$element.val(); 882 } 883 }, 884 885 selectAll: function () { 886 this.findLis(); 887 this.$lis.not('.divider').not('.disabled').not('.selected').filter(':visible').find('a').click(); 888 }, 889 890 deselectAll: function () { 891 this.findLis(); 892 this.$lis.not('.divider').not('.disabled').filter('.selected').filter(':visible').find('a').click(); 893 }, 894 895 keydown: function (e) { 896 var $this = $(this), 897 $parent = ($this.is('input')) ? $this.parent().parent() : $this.parent(), 898 $items, 899 that = $parent.data('this'), 900 index, 901 next, 902 first, 903 last, 904 prev, 905 nextPrev, 906 prevIndex, 907 isActive, 908 keyCodeMap = { 909 32: ' ', 910 48: '0', 911 49: '1', 912 50: '2', 913 51: '3', 914 52: '4', 915 53: '5', 916 54: '6', 917 55: '7', 918 56: '8', 919 57: '9', 920 59: ';', 921 65: 'a', 922 66: 'b', 923 67: 'c', 924 68: 'd', 925 69: 'e', 926 70: 'f', 927 71: 'g', 928 72: 'h', 929 73: 'i', 930 74: 'j', 931 75: 'k', 932 76: 'l', 933 77: 'm', 934 78: 'n', 935 79: 'o', 936 80: 'p', 937 81: 'q', 938 82: 'r', 939 83: 's', 940 84: 't', 941 85: 'u', 942 86: 'v', 943 87: 'w', 944 88: 'x', 945 89: 'y', 946 90: 'z', 947 96: '0', 948 97: '1', 949 98: '2', 950 99: '3', 951 100: '4', 952 101: '5', 953 102: '6', 954 103: '7', 955 104: '8', 956 105: '9' 957 }; 958 959 if (that.options.liveSearch) $parent = $this.parent().parent(); 960 961 if (that.options.container) $parent = that.$menu; 962 963 $items = $('[role=menu] li a', $parent); 964 965 isActive = that.$menu.parent().hasClass('open'); 966 967 if (!isActive && /([0-9]|[A-z])/.test(String.fromCharCode(e.keyCode))) { 968 if (!that.options.container) { 969 that.setSize(); 970 that.$menu.parent().addClass('open'); 971 isActive = true; 972 } else { 973 that.$newElement.trigger('click'); 974 } 975 that.$searchbox.focus(); 976 } 977 978 if (that.options.liveSearch) { 979 if (/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && that.$menu.find('.active').length === 0) { 980 e.preventDefault(); 981 that.$menu.parent().removeClass('open'); 982 that.$button.focus(); 983 } 984 $items = $('[role=menu] li:not(.divider):not(.dropdown-header):visible', $parent); 985 if (!$this.val() && !/(38|40)/.test(e.keyCode.toString(10))) { 986 if ($items.filter('.active').length === 0) {
987 if (that.options.searchAccentInsensitive) { 988 $items = that.$newElement.find('li').filter(':aicontains(' + normalizeToBase(keyCodeMap[e.keyCode]) + ')'); 989 } else { 990 $items = that.$newElement.find('li').filter(':icontains(' + keyCodeMap[e.keyCode] + ')'); 991 } 992 } 993 } 994 } 995 996 if (!$items.length) return; 997 998 if (/(38|40)/.test(e.keyCode.toString(10))) { 999 index = $items.index($items.filter(':focus')); 1000 first = $items.parent(':not(.disabled):visible').first().index(); 1001 last = $items.parent(':not(.disabled):visible').last().index(); 1002 next = $items.eq(index).parent().nextAll(':not(.disabled):visible').eq(0).index(); 1003 prev = $items.eq(index).parent().prevAll(':not(.disabled):visible').eq(0).index(); 1004 nextPrev = $items.eq(next).parent().prevAll(':not(.disabled):visible').eq(0).index(); 1005 1006 if (that.options.liveSearch) { 1007 $items.each(function (i) { 1008 if ($(this).is(':not(.disabled)')) { 1009 $(this).data('index', i); 1010 } 1011 }); 1012 index = $items.index($items.filter('.active')); 1013 first = $items.filter(':not(.disabled):visible').first().data('index'); 1014 last = $items.filter(':not(.disabled):visible').last().data('index'); 1015 next = $items.eq(index).nextAll(':not(.disabled):visible').eq(0).data('index'); 1016 prev = $items.eq(index).prevAll(':not(.disabled):visible').eq(0).data('index'); 1017 nextPrev = $items.eq(next).prevAll(':not(.disabled):visible').eq(0).data('index'); 1018 } 1019 1020 prevIndex = $this.data('prevIndex'); 1021 1022 if (e.keyCode == 38) { 1023 if (that.options.liveSearch) index -= 1; 1024 if (index != nextPrev && index > prev) index = prev; 1025 if (index < first) index = first; 1026 if (index == prevIndex) index = last; 1027 } 1028 1029 if (e.keyCode == 40) { 1030 if (that.options.liveSearch) index += 1; 1031 if (index == -1) index = 0; 1032 if (index != nextPrev && index < next) index = next; 1033 if (index > last) index = last; 1034 if (index == prevIndex) index = first; 1035 } 1036 1037 $this.data('prevIndex', index); 1038 1039 if (!that.options.liveSearch) { 1040 $items.eq(index).focus(); 1041 } else { 1042 e.preventDefault(); 1043 if (!$this.is('.dropdown-toggle')) { 1044 $items.removeClass('active'); 1045 $items.eq(index).addClass('active').find('a').focus(); 1046 $this.focus(); 1047 } 1048 } 1049 1050 } else if (!$this.is('input')) { 1051 var keyIndex = [], 1052 count, 1053 prevKey; 1054 1055 $items.each(function () { 1056 if ($(this).parent().is(':not(.disabled)')) { 1057 if ($.trim($(this).text().toLowerCase()).substring(0, 1) == keyCodeMap[e.keyCode]) { 1058 keyIndex.push($(this).parent().index()); 1059 } 1060 } 1061 }); 1062 1063 count = $(document).data('keycount'); 1064 count++; 1065 $(document).data('keycount', count); 1066 1067 prevKey = $.trim($(':focus').text().toLowerCase()).substring(0, 1); 1068 1069 if (prevKey != keyCodeMap[e.keyCode]) { 1070 count = 1; 1071 $(document).data('keycount', count); 1072 } else if (count >= keyIndex.length) { 1073 $(document).data('keycount', 0); 1074 if (count > keyIndex.length) count = 1; 1075 } 1076 1077 $items.eq(keyIndex[count - 1]).focus(); 1078 } 1079 1080 // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. 1081 if ((/(13|32)/.test(e.keyCode.toString(10)) || (/(^9$)/.test(e.keyCode.toString(10)) && that.options.selectOnTab)) && isActive) { 1082 if (!/(32)/.test(e.keyCode.toString(10))) e.preventDefault(); 1083 if (!that.options.liveSearch) { 1084 $(':focus').click(); 1085 } else if (!/(32)/.test(e.keyCode.toString(10))) { 1086 that.$menu.find('.active a').click(); 1087 $this.focus(); 1088 } 1089 $(document).data('keycount', 0); 1090 } 1091 1092 if ((/(^9$|27)/.test(e.keyCode.toString(10)) && isActive && (that.multiple || that.options.liveSearch)) || (/(27)/.test(e.keyCode.toString(10)) && !isActive)) { 1093 that.$menu.parent().removeClass('open'); 1094 that.$button.focus(); 1095 } 1096 }, 1097 1098 mobile: function () { 1099 this.$element.addClass('mobile-device').appendTo(this.$newElement); 1100 if (this.options.container) this.$menu.hide(); 1101 }, 1102 1103 refresh: function () { 1104 this.$lis = null; 1105 this.reloadLi(); 1106 this.render(); 1107 this.setWidth(); 1108 this.setStyle(); 1109 this.checkDisabled(); 1110 this.liHeight(); 1111 }, 1112 1113 update: function () { 1114 this.reloadLi(); 1115 this.setWidth(); 1116 this.setStyle(); 1117 this.checkDisabled(); 1118 this.liHeight(); 1119 }, 1120 1121 hide: function () { 1122 this.$newElement.hide(); 1123 }, 1124 1125 show: function () { 1126 this.$newElement.show(); 1127 }, 1128 1129 remove: function () { 1130 this.$newElement.remove(); 1131 this.$element.remove(); 1132 } 1133 }; 1134 1135 // SELECTPICKER PLUGIN DEFINITION 1136 // ============================== 1137 function Plugin(option, event) { 1138 // get the args of the outer function.. 1139 var args = arguments; 1140 // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them
1141 // to get lost 1142 //noinspection JSDuplicatedDeclaration 1143 var _option = option, 1144 option = args[0], 1145 event = args[1]; 1146 [].shift.apply(args); 1147 1148 // This fixes a bug in the js implementation on android 2.3 #715 1149 if (typeof option == 'undefined') { 1150 option = _option; 1151 } 1152 1153 var value; 1154 var chain = this.each(function () { 1155 var $this = $(this); 1156 if ($this.is('select')) { 1157 var data = $this.data('selectpicker'), 1158 options = typeof option == 'object' && option; 1159 1160 if (!data) { 1161 var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, $this.data(), options); 1162 $this.data('selectpicker', (data = new Selectpicker(this, config, event))); 1163 } else if (options) { 1164 for (var i in options) { 1165 if (options.hasOwnProperty(i)) { 1166 data.options[i] = options[i]; 1167 } 1168 } 1169 } 1170 1171 if (typeof option == 'string') { 1172 if (data[option] instanceof Function) { 1173 value = data[option].apply(data, args); 1174 } else { 1175 value = data.options[option]; 1176 } 1177 } 1178 } 1179 }); 1180 1181 if (typeof value !== 'undefined') { 1182 //noinspection JSUnusedAssignment 1183 return value; 1184 } else { 1185 return chain; 1186 } 1187 } 1188 1189 var old = $.fn.selectpicker; 1190 $.fn.selectpicker = Plugin; 1191 $.fn.selectpicker.Constructor = Selectpicker; 1192 1193 // SELECTPICKER NO CONFLICT 1194 // ======================== 1195 $.fn.selectpicker.noConflict = function () { 1196 $.fn.selectpicker = old; 1197 return this; 1198 }; 1199 1200 $(document) 1201 .data('keycount', 0) 1202 .on('keydown', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', Selectpicker.prototype.keydown) 1203 .on('focusin.modal', '.bootstrap-select [data-toggle=dropdown], .bootstrap-select [role=menu], .bs-searchbox input', function (e) { 1204 e.stopPropagation(); 1205 }); 1206 1207 // SELECTPICKER DATA-API 1208 // ===================== 1209 $(window).on('load.bs.select.data-api', function () { 1210 $('.selectpicker').each(function () { 1211 var $selectpicker = $(this); 1212 Plugin.call($selectpicker, $selectpicker.data()); 1213 }) 1214 }); 1215})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.