1/*! ikSelect 1.0.3 2 Copyright (c) 2013 Igor Kozlov 3 http://igorkozlov.me */ 4 5;(function ($, window, document, undefined) { 6 var $window = $(window); 7 var defaults = { 8 syntax: '<div class="ik_select_link"><div class="ik_select_link_text"></div></div><div class="ik_select_dropdown"><div class="ik_select_list"></div></div>', 9 autoWidth: true, // set select width according to the longest option 10 ddFullWidth: true, // set dropdown width according to the longest option 11 equalWidths: true, // include scrollbar width in fake select calculations 12 dynamicWidth: false, // change fake select's width according to it's contents 13 extractLink: false, 14 customClass: '', 15 linkCustomClass: '', 16 ddCustomClass: '', 17 ddMaxHeight: 200, 18 filter: false, 19 nothingFoundText: 'Nothing found', 20 isDisabled: false, 21 onInit: function() {}, 22 onShow: function () {}, 23 onHide: function () {}, 24 onKeyUp: function () {}, 25 onKeyDown: function () {}, 26 onHoverMove: function () {} 27 }; 28 29 var instOpened; // instance of the currently opened select 30 31 // browser detection part was taken from jQuery migrate 32 // https://github.com/jquery/jquery-migrate/blob/e6bda6a84c294eb1319fceb48c09f51042c80892/src/core.js 33 var uaMatch = function (ua) { 34 ua = ua.toLowerCase(); 35 36 var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || 37 /(webkit)[ \/]([\w.]+)/.exec(ua) || 38 /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || 39 /(msie) ([\w.]+)/.exec(ua) || 40 ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || 41 []; 42 43 return { 44 browser: match[1] || '', 45 version: match[2] || '0' 46 }; 47 }; 48 49 if (! $.browser) { 50 var matched = uaMatch(navigator.userAgent); 51 var browser = {}; 52 53 if (matched.browser) { 54 browser[matched.browser] = true; 55 browser.version = matched.version; 56 } 57 58 if (browser.chrome) { 59 browser.webkit = true; 60 } else if (browser.webkit) { 61 browser.safari = true; 62 } 63 64 $.browser = browser; 65 } 66 67 $.browser.mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile/i.test(navigator.userAgent); 68 $.browser.operamini = Object.prototype.toString.call(window.operamini) === "[object OperaMini]"; 69 70 function IkSelect(element, options) { 71 var dataOptions = {}; // parsed data- attributes 72 73 this.el = element; 74 this.$el = $(element); // original select 75 76 for (var key in defaults) { 77 dataOptions[key] = this.$el.data(key.toLowerCase()); 78 } 79 80 this.options = $.extend({}, defaults, options, dataOptions); 81 82 if ($.browser.mobile) { 83 this.options.filter = false; 84 } 85 86 this.init(); 87 } 88 89 $.extend(IkSelect.prototype, { 90 init: function () { 91 this.$wrapper = $('<div class="ik_select">' + this.options.syntax + '</div>'); // wrapper for the fake select and the fake dropdown 92 this.$link = $('.ik_select_link', this.$wrapper); // fake select 93 this.$linkText = $('.ik_select_link_text', this.$wrapper); // fake select's text 94 this.$dropdown = $('.ik_select_dropdown', this.$wrapper); // fake dropdown 95 this.$list = $('.ik_select_list', this.$wrapper); // options list inside the fake dropdown
96 this.$listInner = $('<div class="ik_select_list_inner"/>'); // support dropdown for scrolling 97 this.$active = $([]); // active fake option 98 this.$hover = $([]); // hovered fake option 99 this.hoverIndex = 0; // hovered fake option's index 100 101 this.$optionSet = $([]); 102 this.$optgroupSet = $([]); 103 104 this.$list.append(this.$listInner); 105 106 if (this.options.filter) { 107 this.$filter = $([]); // filter text input 108 this.$optionSetOriginal = $([]); // contains original list items when filtering 109 this.$nothingFoundText = $('<div class="ik_select_nothing_found"/>').html(this.options.nothingFoundText); 110 this.$filterWrap = $('.ik_select_filter_wrap', this.$wrapper); 111 112 if (! this.$filterWrap.length) { 113 this.$filterWrap = $('<div class="ik_select_filter_wrap"/>'); 114 } 115 116 this.$filter = $('<input type="text" class="ik_select_filter">'); 117 118 this.$filterWrap.append(this.$filter); 119 this.$list.prepend(this.$filterWrap); 120 121 this.$filter.on({ 122 // keyboard controls should work even if the filter is in focus 123 'keydown.ikSelect keyup.ikSelect': $.proxy(this, '_elKeyUpDown'), 124 // actual filtering happens on keyup 125 'keyup.ikSelect': $.proxy(this, '_filterKeyup') 126 }); 127 } 128 129 this.$wrapper.addClass(this.options.customClass); 130 this.$link.addClass(this.options.linkCustomClass || (this.options.customClass && this.options.customClass + '-link')); 131 this.$dropdown.addClass(this.options.ddCustomClass || (this.options.customClass && this.options.customClass + '-dd')); 132 133 // creating fake option list 134 this.reset(); 135 136 // disable select if an option or attribute are truthy 137 this.toggle(!(this.options.isDisabled || this.$el.prop('disabled') || this.$el.hasClass('hidden'))); 138 139 // click event for fake select 140 this.$link.on('click.ikSelect', $.proxy(this, '_linkClick')); 141 142 this.$el.on({ 143 // when focus is on the original select add a focus class to the fake one 144 'focus.ikSelect': $.proxy(this, '_elFocus'), 145 // when focus lost remove the focus class from the fake one 146 'blur.ikSelect': $.proxy(this, '_elBlur'), 147 // make sure that the fake select shows correct data 148 'change.ikSelect': $.proxy(this, '_syncOriginalOption'), 149 // keyboard controls for the fake select 150 'keydown.ikSelect keyup.ikSelect': $.proxy(this, '_elKeyUpDown') 151 }); 152 153 this.$list.on({ 154 'click.ikSelect': $.proxy(this, '_optionClick'), 155 'mouseover.ikSelect': $.proxy(this, '_optionMouseover') 156 }, '.ik_select_option'); 157 158 // stop propagation of click events on the wrapper 159 this.$wrapper.on('click', function () { 160 return false; 161 }); 162 163 // appending the fake select right after the original one 164 this.$el.after(this.$wrapper); 165 166 // set correct dimensions 167 this.redraw(); 168 169 // move original select inside the wrapper 170 this.$el.appendTo(this.$wrapper); 171 172 this.options.onInit(this); 173 this.$el.trigger('ikinit', this); 174 }, 175 176 // click listener for fake select 177 _linkClick: function () { 178 if (this.isDisabled) { 179 return; 180 } 181 if (this === instOpened) { 182 this.hideDropdown(); 183 } else { 184 this.showDropdown(); 185 } 186 187 }, 188 189 // click listener for the fake options 190 _optionClick: function () { 191 this._makeOptionActive(this.searchIndexes ? this.$optionSetOriginal.index(this.$hover) : this.hoverIndex, true); 192 this.hideDropdown(); 193 this.$el.change().focus(); 194 }, 195 196 // hover listener for the fake options 197 _optionMouseover: function (event) { 198 var $option = $(event.currentTarget); 199 if ($option.hasClass('ik_select_option_disabled')) { 200 return; 201 } 202 this.$hover.removeClass('ik_select_hover'); 203 this.$hover = $option.addClass('ik_select_hover'); 204 this.hoverIndex = this.$optionSet.index(this.$hover); 205 }, 206 207 // makes fake option active without syncing the original select 208 _makeOptionActive: function (index, shouldSync) { 209 var $option = $(this.el.options[index]), 210 text=''; 211 if($option.data('img_src')){ 212 text+='<span class="icon_color" style="background: url('+$option.data('img_src')+')"></span>'; 213 this.$link.addClass('img'); 214 } 215 if($option.attr('title')){ 216 this.$link.attr('title', $option.attr('title')); 217 } 218 text+=$option.text(); 219 this.$linkText.html(text); 220 221 this.$link.toggleClass('ik_select_link_novalue', !$option.attr('value')); 222 223 this.$hover.removeClass('ik_select_hover'); 224 this.$active.removeClass('ik_select_active'); 225 this.$hover = this.$active = this.$optionSet.eq(index).addClass('ik_select_hover ik_select_active'); 226 this.hoverIndex = index; 227 228 if (shouldSync) { 229 this._syncFakeOption(); 230 } 231 }, 232 233 // keyboard controls for the fake select 234 _elKeyUpDown: function (event) { 235 var $handle = $(event.currentTarget); 236 var type = event.type; 237 var keycode = event.which; 238 var newTop; 239 240 switch (keycode) { 241 case 38: // up 242 if (type === 'keydown') { 243 event.preventDefault(); 244 this._moveToPrevActive(); 245 } 246 break; 247 case 40: // down 248 if (type === 'keydown') { 249 event.preventDefault(); 250 this._moveToNextActive(); 251 } 252 break; 253 case 33: // page up 254 if (type === 'keydown') { 255 event.preventDefault(); 256 newTop = this.$hover.position().top - this.$listInner.height(); 257 this._moveToPrevActive(function (optionTop) { 258 return optionTop <= newTop; 259 }); 260 } 261 break; 262 case 34: // page down 263 if (type === 'keydown') { 264 event.preventDefault(); 265 newTop = this.$hover.position().top + this.$listInner.height(); 266 this._moveToNextActive(function (optionTop) { 267 return optionTop >= newTop; 268 }); 269 } 270 break; 271 case 36: // home 272 if (type === 'keydown' && $handle.is(this.$el)) { 273 event.preventDefault(); 274 this._moveToFirstActive(); 275 } 276 break; 277 case 35: // end 278 if (type === 'keydown' && $handle.is(this.$el)) { 279 event.preventDefault(); 280 this._moveToLastActive(); 281 } 282 break; 283 case 32: // space 284 if (type === 'keydown' && $handle.is(this.$el)) { 285 event.preventDefault(); 286 if (! this.$dropdown.is(':visible')) { 287 this._linkClick(); 288 } else { 289 this.$hover.click(); 290 } 291 } 292 break; 293 case 13: // enter 294 if (type === 'keydown' && this.$dropdown.is(':visible')) { 295 event.preventDefault(); 296 this.$hover.click(); 297 } 298 break; 299 case 27: // esc 300 if (type === 'keydown' && this.$dropdown.is(':visible')) { 301 event.preventDefault(); 302 this.hideDropdown(); 303 } 304 break; 305 case 9: // tab 306 if (type === 'keydown') { 307 if ($.browser.webkit && this.$dropdown.is(':visible')) { 308 event.preventDefault(); 309 } else { 310 this.hideDropdown(); 311 } 312 } 313 break; 314 default: 315 if (type === 'keyup' && $handle.is(this.$el)) { 316 this._syncOriginalOption(); 317 } 318 break; 319 } 320 321 // mozilla ignores preventDefault for select navigation 322 // this ensures that original select is in sync 323 if (type === 'keyup' && $.browser.mozilla) { 324 this._syncFakeOption(); 325 } 326 327 if (type === 'keydown') { 328 this.options.onKeyDown(this, keycode); 329 this.$el.trigger('ikkeydown', [this, keycode]); 330 } 331 if (type === 'keyup') { 332 this.options.onKeyUp(this, keycode); 333 this.$el.trigger('ikkeyup', [this, keycode]); 334 } 335 }, 336 337 // controls hover/active states for options and makes selected with keyboard options fully visible 338 _moveTo: function (index) { 339 var optionTopLine, optionBottomLine; 340 var $optgroup; 341 342 if (!this.$dropdown.is(':visible') && $.browser.webkit) { 343 this.showDropdown(); 344 return this; 345 } 346 347 if (!this.$dropdown.is(':visible') || $.browser.mozilla) { 348 this._makeOptionActive(index, true); 349 } else { 350 this.$hover.removeClass('ik_select_hover'); 351 this.$hover = this.$optionSet.eq(index).addClass('ik_select_hover'); 352 this.hoverIndex = index; 353 } 354 355 // make option fully visible 356 optionTopLine = this.$hover.position().top; 357 optionBottomLine = optionTopLine + this.$active.outerHeight(); 358 // make optgroup label visible if first option witin this optgroup is hovered 359 if (!this.$hover.index()) { 360 $optgroup = this.$hover.closest('.ik_select_optgroup'); 361 if ($optgroup.length) { 362 optionTopLine = $optgroup.position().top; 363 } 364 } 365 if (optionBottomLine > this.$listInner.height()) { 366 this.$listInner.scrollTop(this.$listInner.scrollTop() + optionBottomLine - this.$listInner.height()); 367 } else if (optionTopLine < 0) { 368 this.$listInner.scrollTop(this.$listInner.scrollTop() + optionTopLine); 369 } 370 371 this.options.onHoverMove(this); 372 this.$el.trigger('ikhovermove', this); 373 }, 374 375 _moveToFirstActive: function () { 376 for (var i = 0; i < this.$optionSet.length; i++) { 377 if (!this.$optionSet.eq(i).hasClass('ik_select_option_disabled')) { 378 this._moveTo(i); 379 break; 380 } 381 } 382 }, 383 384 _moveToLastActive: function () { 385 for (var i = this.$optionSet.length - 1; i >= 0; i++) { 386 if (!this.$optionSet.eq(i).hasClass('ik_select_option_disabled')) { 387 this._moveTo(i); 388 break; 389 } 390 } 391 }, 392 393 _moveToPrevActive: function (condition) { 394 var $option; 395 for (var i = this.hoverIndex - 1; i >= 0; i--) { 396 $option = this.$optionSet.eq(i); 397 if (!$option.hasClass('ik_select_option_disabled') && (typeof condition === 'undefined' || condition($option.position().top))) { 398 this._moveTo(i); 399 break; 400 } 401 } 402 }, 403 404 _moveToNextActive: function (condition) { 405 var $option; 406 for (var i = this.hoverIndex + 1; i < this.$optionSet.length; i++) { 407 $option = this.$optionSet.eq(i); 408 if (!$option.hasClass('ik_select_option_disabled') && (typeof condition === 'undefined' || condition($option.position().top))) { 409 this._moveTo(i); 410 break; 411 } 412 } 413 }, 414
415 // when focus is on the original select add a focus class to the fake one 416 _elFocus: function () { 417 var wrapperOffsetTop, wrapperHeight, windowScrollTop, windowHeight; 418 if (this.isDisabled) { 419 return this; 420 } 421 this.$link.addClass('ik_select_link_focus'); 422 423 // scroll the window so that focused select is visible 424 wrapperOffsetTop = this.$wrapper.offset().top; 425 wrapperHeight = this.$wrapper.height(); 426 windowScrollTop = $window.scrollTop(); 427 windowHeight = $window.height(); 428 if ((wrapperOffsetTop + wrapperHeight > windowScrollTop + windowHeight) || 429 (wrapperOffsetTop < windowScrollTop)) { 430 $window.scrollTop(wrapperOffsetTop - windowHeight / 2); 431 } 432 }, 433 434 // when focus lost remove the focus class from the fake one 435 _elBlur: function () { 436 this.$link.removeClass('ik_select_link_focus'); 437 }, 438 439 // filtering on key up 440 _filterKeyup: function () { 441 var filterVal = $.trim(this.$filter.val()); 442 var filterValOld; 443 this.$listInner.show(); 444 445 if (typeof this.searchIndexes === 'undefined') { 446 this.$optionSetOriginal = this.$optionSet; 447 this.searchIndexes = $.makeArray(this.$optionSet.map(function (optionIndex, option) { 448 return $(option).text().toLowerCase(); 449 })); 450 } 451 452 if (filterVal !== filterValOld) { 453 if (filterVal === '') { 454 this.$optionSet = this.$optionSetOriginal.show(); 455 this.$optgroupSet.show(); 456 this.$nothingFoundText.remove(); 457 } else { 458 this.$optionSet = $([]); 459 this.$optgroupSet.show(); 460 this.$optionSetOriginal.each($.proxy(function (optionIndex, option) { 461 var $option = $(option); 462 if (this.searchIndexes[optionIndex].indexOf(filterVal.toLowerCase()) >= 0) { 463 this.$optionSet = this.$optionSet.add($option); 464 $option.show(); 465 } else { 466 $option.hide(); 467 } 468 }, this)); 469 470 if (this.$optionSet.length) { 471 this.$nothingFoundText.remove(); 472 this.$optgroupSet.each(function (optgroupIndex, optgroup) { 473 var $optgroup = $(optgroup); 474 if (!$('.ik_select_option:visible', $optgroup).length) { 475 $optgroup.hide(); 476 } 477 }); 478 479 if (!this.$hover.is(':visible')) { 480 this._moveToFirstActive(); 481 } 482 } else { 483 this.$listInner.hide(); 484 this.$list.append(this.$nothingFoundText); 485 } 486 } 487 488 filterValOld = filterVal; 489 } 490 }, 491 492 // sync selected option in the fake select with the original one 493 _syncFakeOption: function () { 494 this.el.selectedIndex = this.hoverIndex; 495 }, 496 497 // sync selected option in the original select with the fake one 498 _syncOriginalOption: function () { 499 this._makeOptionActive(this.el.selectedIndex); 500 }, 501 502 // sets fixed height to dropdown if it's bigger than ddMaxHeight 503 _fixHeight: function () { 504 this.$dropdown.show(); 505 this.$listInner.css('height', 'auto'); 506 if (this.$listInner.height() > this.options.ddMaxHeight) { 507 this.$listInner.css({ 508 overflow: 'auto', 509 height: this.options.ddMaxHeight, 510 position: 'relative' 511 }); 512 } 513 this.$dropdown.hide(); 514 }, 515 516 // calculates the needed data for showing the dropdown 517 redraw: function () { 518 var maxWidthOuter, scrollbarWidth, wrapperParentWidth; 519 520 if (this.options.filter) { 521 this.$filter.hide(); 522 } 523 524 this.$wrapper.css({ 525 position: 'relative' 526 }); 527 this.$dropdown.css({ 528 position: 'absolute', 529 zIndex: 9998, 530 width: '100%' 531 }); 532 this.$list.css({ 533 position: 'relative' 534 }); 535 536 this._fixHeight(); 537 538 // width calculations for the fake select when "autoWidth" is "true" 539 if (this.options.dynamicWidth || this.options.autoWidth || this.options.ddFullWidth) { 540 this.$wrapper.width(''); 541 542 this.$dropdown.show().width(9999); 543 this.$listInner.css('float', 'left'); 544 this.$list.css('float', 'left'); 545 maxWidthOuter = this.$list.outerWidth(true); 546 scrollbarWidth = this.$listInner.width() - this.$listInnerUl.width(); 547 this.$list.css('float', ''); 548 this.$listInner.css('float', ''); 549 this.$dropdown.css('width', '100%'); 550 551 if (this.options.ddFullWidth) { 552 this.$dropdown.width(maxWidthOuter + scrollbarWidth); 553 } 554 555 if (this.options.dynamicWidth) { 556 this.$wrapper.css({ 557 display: 'inline-block', 558 width: 'auto',
559 verticalAlign: 'top' 560 }); 561 } else if (this.options.autoWidth) { 562 this.$wrapper.width(maxWidthOuter + (this.options.equalWidths ? scrollbarWidth : 0)).addClass('ik_select_autowidth'); 563 } 564 565 wrapperParentWidth = this.$wrapper.parent().width(); 566 if (this.$wrapper.width() > wrapperParentWidth) { 567 this.$wrapper.width(wrapperParentWidth); 568 } 569 } 570 571 if (this.options.filter) { 572 this.$filter.show().outerWidth(this.$filterWrap.width()); 573 } 574 575 this.$dropdown.hide(); 576 577 // hide the original select 578 this.$el.css({ 579 position: 'absolute', 580 margin: 0, 581 padding: 0, 582 top: 0, 583 left: -9999 584 }); 585 586 // show the original select in mobile browsers 587 if ($.browser.mobile) { 588 this.$el.css({ 589 opacity: 0, 590 left: 0, 591 height: this.$wrapper.height(), 592 width: this.$wrapper.width() 593 }); 594 } 595 }, 596 597 // creates or recreates dropdown and sets selected options's text into fake select 598 reset: function () { 599 var html = ''; 600 601 // init fake select's text 602 this.$linkText.html(this.$el.val()); 603 604 this.$listInner.empty(); 605 606 // creating an ul>li list identical to the original dropdown 607 html = '<ul>'; 608 this.$el.children().each($.proxy(function (childIndex, child) { 609 var $child = $(child); 610 var tagName = child.tagName.toLowerCase(); 611 var options; 612 if (tagName === 'optgroup') { 613 options = $child.children().map($.proxy(function (optionIndex, option) { 614 return this._generateOptionObject($(option)); 615 }, this)); 616 options = $.makeArray(options); 617 html += this._renderListOptgroup({ 618 label: $child.attr('label') || ' ', 619 isDisabled: $child.is(':disabled'), 620 options: options 621 }); 622 } else if (tagName === 'option') { 623 html += this._renderListOption(this._generateOptionObject($child)); 624 } 625 }, this)); 626 html += '</ul>'; 627 this.$listInner.append(html); 628 this._syncOriginalOption(); 629 630 this.$listInnerUl = $('> ul', this.$listInner); 631 this.$optgroupSet = $('.ik_select_optgroup', this.$listInner); 632 this.$optionSet = $('.ik_select_option', this.$listInner); 633 }, 634 635 // hides dropdown 636 hideDropdown: function () { 637 if (this.options.filter) { 638 this.$filter.val(''); 639 this._filterKeyup(); 640 } 641 642 this.$dropdown.hide().appendTo(this.$wrapper).css({ 643 'left': '', 644 'top': '' 645 }); 646 if (this.options.extractLink) { 647 this.$wrapper.outerWidth(this.$wrapper.data('outerWidth')); 648 this.$wrapper.height(''); 649 this.$link.removeClass('ik_select_link_extracted').css({ 650 position: '', 651 top: '', 652 left: '', 653 zIndex: '' 654 }).prependTo(this.$wrapper); 655 } 656 instOpened = null; 657 this.$el.focus(); 658 659 this.options.onHide(this); 660 this.$el.trigger('ikhide', this); 661 }, 662 663 // shows dropdown 664 showDropdown: function () { 665 var dropdownOffset, dropdownOuterWidth, dropdownOuterHeight; 666 var wrapperOffset, wrapperOuterWidth; 667 var windowWidth, windowHeight, windowScroll; 668 var linkOffset; 669 670 if (instOpened === this || !this.$optionSet.length) { 671 return; 672 } else if (instOpened) { 673 instOpened.hideDropdown(); 674 } 675 676 this._syncOriginalOption(); 677 if($('.show_un_props').length) 678 this.$dropdown.addClass('show_un_props'); 679 else if($('.unshow_un_props').length) 680 this.$dropdown.addClass('unshow_un_props'); 681 this.$dropdown.show(); 682 683 dropdownOffset = this.$dropdown.offset(); 684 dropdownOuterWidth = this.$dropdown.outerWidth(true); 685 dropdownOuterHeight = this.$dropdown.outerHeight(true); 686 wrapperOffset = this.$wrapper.offset(); 687 windowWidth = $window.width(); 688 windowHeight = $window.height(); 689 windowScroll = $window.scrollTop(); 690 691 // if the dropdown's right border is beyond window's edge then move the dropdown to the left so that it fits 692 if (this.options.ddFullWidth && wrapperOffset.left + dropdownOuterWidth > windowWidth) { 693 dropdownOffset.left = windowWidth - dropdownOuterWidth; 694 } 695 696 // if the dropdown's bottom border is beyond window's edge then move the dropdown to the top so that it fits 697 if (dropdownOffset.top + dropdownOuterHeight > windowScroll + windowHeight) { 698 dropdownOffset.top = windowScroll + windowHeight - dropdownOuterHeight; 699 } 700 701 this.$dropdown.css({ 702 left: dropdownOffset.left, 703 top: dropdownOffset.top, 704 width: this.$dropdown.width() 705 }).appendTo('body'); 706 707 if (this.options.extractLink) { 708 linkOffset = this.$link.offset(); 709 wrapperOuterWidth = this.$wrapper.outerWidth(); 710 this.$wrapper.data('outerWidth', wrapperOuterWidth); 711 this.$wrapper.outerWidth(wrapperOuterWidth); 712 this.$wrapper.outerHeight(this.$wrapper.outerHeight()); 713 this.$link.outerWidth(this.$link.outerWidth()); 714 this.$link.addClass('ik_select_link_extracted').css({ 715 position: 'absolute', 716 top: linkOffset.top, 717 left: linkOffset.left, 718 zIndex: 9999 719 }).appendTo('body'); 720 } 721 722 this.$listInner.scrollTop(this.$active.position().top - this.$list.height() / 2); 723 724 if (this.options.filter) { 725 this.$filter.focus(); 726 } else { 727 this.$el.focus(); 728 } 729 730 instOpened = this; 731 732 this.options.onShow(this); 733 this.$el.trigger('ikshow', this); 734 }, 735 736 _generateOptionObject: function ($option) { 737 738 return { 739 value: $option.val(), 740 label: $option.html() || ' ', 741 isDisabled: $option.is(':disabled'), 742 dataParam: $option.data(), 743 className: $option.attr('class'), 744 title: $option.attr('title') 745 }; 746 }, 747 748 // generates option code for the fake dropdown 749 _renderListOption: function (option) { 750 var html; 751 var disabledClass = option.isDisabled ? ' ik_select_option_disabled' : ''; 752 var imgClass = option.dataParam.img_src ? ' img' : ''; 753 var missingClass = option.className=='hidden' ? ' missing' : ''; 754 var title = option.title; 755 html = '<li class="ik_select_option' + disabledClass + imgClass + missingClass + '" title="'+title+'" data-value="' + option.value + '">'; 756 html += '<span class="ik_select_option_label" title="'+title+'">'; 757 if(option.dataParam.img_src){ 758 html += '<span class="icon_color" style="background: url('+option.dataParam.img_src+')"></span>'; 759 } 760 html += option.label; 761 html += '</span>'; 762 html += '</li>'; 763 return html; 764 }, 765 766 // generates optgroup code for the fake dropdown 767 _renderListOptgroup: function (optgroup) { 768 var html; 769 var disabledClass = optgroup.isDisabled ? ' ik_select_optgroup_disabled' : ''; 770 html = '<li class="ik_select_optgroup' + disabledClass + '">'; 771 html += '<div class="ik_select_optgroup_label">' + optgroup.label + '</div>'; 772 html += '<ul>'; 773 if ($.isArray(optgroup.options)) { 774 $.each(optgroup.options, $.proxy(function (optionIndex, option) { 775 html += this._renderListOption({ 776 value: option.value, 777 label: option.label || ' ', 778 isDisabled: option.isDisabled 779 }); 780 }, this)); 781 } 782 html += '</ul>'; 783 html += '</li>'; 784 return html; 785 }, 786 787 // generates option code for the select 788 _renderOption: function (option) { 789 return '<option value="' + option.value + '">' + option.label + '</option>'; 790 }, 791 792 // generates optgroup code for the select 793 _renderOptgroup: function (optgroup) { 794 var html; 795 html = '<optgroup label="' + optgroup.label + '">'; 796 if ($.isArray(optgroup.options)) { 797 $.each(optgroup.options, $.proxy(function (optionIndex, option) { 798 html += this._renderOption(option); 799 }, this)); 800 } 801 html += '</option>'; 802 return html; 803 }, 804 805 addOptions: function (options, optionIndex, optgroupIndex) { 806 var listHtml = '', selectHtml = ''; 807 var $destinationUl = this.$listInnerUl; 808 var $destinationOptgroup = this.$el; 809 var $ulOptions, $optgroupOptions; 810 811 options = $.isArray(options) ? options : [options]; 812 813 $.each(options, $.proxy(function (index, option) { 814 listHtml += this._renderListOption(option); 815 selectHtml += this._renderOption(option); 816 }, this)); 817 818 if ($.isNumeric(optgroupIndex) && optgroupIndex < this.$optgroupSet.length) { 819 $destinationUl = this.$optgroupSet.eq(optgroupIndex); 820 $destinationOptgroup = $('optgroup', this.$el).eq(optgroupIndex); 821 } 822 if ($.isNumeric(optionIndex)) { 823 $ulOptions = $('.ik_select_option', $destinationUl); 824 if (optionIndex < $ulOptions.length) { 825 $ulOptions.eq(optionIndex).before(listHtml); 826 $optgroupOptions = $('option', $destinationOptgroup); 827 $optgroupOptions.eq(optionIndex).before(selectHtml); 828 } 829 } 830 if (!$optgroupOptions) { 831 $destinationUl.append(listHtml); 832 $destinationOptgroup.append(selectHtml); 833 } 834 835 this.$optionSet = $('.ik_select_option', this.$listInner); 836 this._fixHeight(); 837 }, 838 839 addOptgroups: function (optgroups, optgroupIndex) { 840 var listHtml = '', selectHtml = ''; 841 if (!optgroups) { 842 return; 843 } 844 optgroups = $.isArray(optgroups) ? optgroups : [optgroups]; 845 846 $.each(optgroups, $.proxy(function (optgroupIndex, optgroup) { 847 listHtml += this._renderListOptgroup(optgroup); 848 selectHtml += this._renderOptgroup(optgroup); 849 }, this)); 850 851 if ($.isNumeric(optgroupIndex) && optgroupIndex < this.$optgroupSet.length) { 852 this.$optgroupSet.eq(optgroupIndex).before(listHtml); 853 $('optgroup', this.$el).eq(optgroupIndex).before(selectHtml); 854 } else { 855 this.$listInnerUl.append(listHtml); 856 this.$el.append(selectHtml); 857 } 858 859 this.$optgroupSet = $('.ik_select_optgroup', this.$listInner); 860 this.$optionSet = $('.ik_select_option', this.$listInner); 861 this._fixHeight(); 862 }, 863 864 removeOptions: function (optionIndexes, optgroupIndex) { 865 var $removeList = $([]); 866 var $listContext; 867 var $selectContext; 868 869 if ($.isNumeric(optgroupIndex)) { 870 if (optgroupIndex < 0) { 871 $listContext = $('> .ik_select_option', this.$listInnerUl); 872 $selectContext = $('> option', this.$el); 873 } else if (optgroupIndex < this.$optgroupSet.length) { 874 $listContext = $('.ik_select_option', this.$optgroupSet.eq(optgroupIndex)); 875 $selectContext = $('optgroup', this.$el).eq(optgroupIndex).find('option'); 876 } 877 } 878 if (!$listContext) { 879 $listContext = this.$optionSet; 880 $selectContext = $(this.el.options); 881 } 882 883 if (!$.isArray(optionIndexes)) { 884 optionIndexes = [optionIndexes]; 885 } 886 887 $.each(optionIndexes, $.proxy(function (index, optionIndex) { 888 if (optionIndex < $listContext.length) { 889 $removeList = $removeList.add($listContext.eq(optionIndex)).add($selectContext.eq(optionIndex)); 890 } 891 }, this)); 892 893 $removeList.remove(); 894 this.$optionSet = $('.ik_select_option', this.$listInner); 895 this._syncOriginalOption(); 896 this._fixHeight(); 897 }, 898 899 removeOptgroups: function (optgroupIndexes) { 900 var $removeList = $([]); 901 var $selectOptgroupSet = $('optgroup', this.el); 902 903 if (!$.isArray(optgroupIndexes)) { 904 optgroupIndexes = [optgroupIndexes]; 905 } 906 907 $.each(optgroupIndexes, $.proxy(function (index, optgroupIndex) { 908 if (optgroupIndex < this.$optgroupSet.length) { 909 $removeList = $removeList.add(this.$optgroupSet.eq(optgroupIndex)).add($selectOptgroupSet.eq(optgroupIndex)); 910 } 911 }, this)); 912 913 $removeList.remove(); 914 this.$optionSet = $('.ik_select_option', this.$listInner); 915 this.$optgroupSet = $('.ik_select_optgroup', this.$listInner); 916 this._syncOriginalOption(); 917 this._fixHeight(); 918 }, 919 920 // disable select 921 disable: function () { 922 this.toggle(false); 923 }, 924 925 // enable select 926 enable: function () { 927 this.toggle(true); 928 }, 929 930 // toggle select 931 toggle: function (bool) { 932 this.isDisabled = typeof bool !== 'undefined' ? !bool : !this.isDisabled; 933 this.$el.prop('disabled', this.isDisabled); 934 this.$link.toggleClass('ik_select_link_disabled', this.isDisabled); 935 }, 936 937 // make option selected by value 938 select: function (value, isIndex) { 939 if (!isIndex) { 940 this.$el.val(value); 941 } else { 942 this.el.selectedIndex = value; 943 } 944 this._syncOriginalOption(); 945 }, 946 947 disableOptgroups: function (optgroupIndexes) { 948 this.toggleOptgroups(optgroupIndexes, false); 949 }, 950 951 enableOptgroups: function (optgroupIndexes) { 952 this.toggleOptgroups(optgroupIndexes, true); 953 }, 954 955 toggleOptgroups: function (optgroupIndexes, bool) { 956 if (!$.isArray(optgroupIndexes)) { 957 optgroupIndexes = [optgroupIndexes]; 958 } 959 960 $.each(optgroupIndexes, $.proxy(function (index, optgroupIndex) { 961 var isDisabled; 962 var $optionSet, indexes = [], indexFirst; 963 var $optgroup = $('optgroup', this.$el).eq(optgroupIndex); 964 isDisabled = typeof bool !== 'undefined' ? bool : $optgroup.prop('disabled'); 965 $optgroup.prop('disabled', !isDisabled); 966 this.$optgroupSet.eq(optgroupIndex).toggleClass('ik_select_optgroup_disabled', !isDisabled); 967 968 $optionSet = $('option', $optgroup); 969 indexFirst = $(this.el.options).index($optionSet.eq(0)); 970 for (var i = indexFirst; i < indexFirst + $optionSet.length; i++) { 971 indexes.push(i); 972 }
973 this.toggleOptions(indexes, true, isDisabled); 974 }, this)); 975 976 this._syncOriginalOption(); 977 }, 978 979 disableOptions: function (lookupValues, isIndex) { 980 this.toggleOptions(lookupValues, isIndex, false); 981 }, 982 983 enableOptions: function (lookupValues, isIndex) { 984 this.toggleOptions(lookupValues, isIndex, true); 985 }, 986 987 toggleOptions: function (lookupValues, isIndex, bool) { 988 var $selectOptionSet = $('option', this.el); 989 if (!$.isArray(lookupValues)) { 990 lookupValues = [lookupValues]; 991 } 992 993 var toggleOption = $.proxy(function ($option, optionIndex) { 994 var isDisabled = typeof bool !== 'undefined' ? bool : $option.prop('disabled'); 995 $option.prop('disabled', !isDisabled); 996 this.$optionSet.eq(optionIndex).toggleClass('ik_select_option_disabled', !isDisabled); 997 }, this); 998 999 $.each(lookupValues, function (index, lookupValue) { 1000 if (!isIndex) { 1001 $selectOptionSet.each(function (optionIndex, option) { 1002 var $option = $(option); 1003 if ($option.val() === lookupValue) { 1004 toggleOption($option, optionIndex); 1005 return this; 1006 } 1007 }); 1008 } else { 1009 toggleOption($selectOptionSet.eq(lookupValue), lookupValue); 1010 } 1011 }); 1012 1013 this._syncOriginalOption(); 1014 }, 1015 1016 // detach plugin from the orignal select 1017 detach: function () { 1018 this.$el.off('.ikSelect').css({ 1019 width: '', 1020 height: '', 1021 left: '', 1022 top: '', 1023 position: '', 1024 margin: '', 1025 padding: '' 1026 }); 1027 1028 this.$wrapper.before(this.$el); 1029 this.$wrapper.remove(); 1030 this.$el.removeData('plugin_ikSelect'); 1031 } 1032 }); 1033 1034 $.fn.ikSelect = function (options) { 1035 var args; 1036 //do nothing if opera mini 1037 if ($.browser.operamini) { 1038 return this; 1039 } 1040 1041 args = Array.prototype.slice.call(arguments, 1); 1042 1043 return this.each(function () { 1044 var plugin; 1045 if (!$.data(this, 'plugin_ikSelect')) { 1046 $.data(this, 'plugin_ikSelect', new IkSelect(this, options)); 1047 } else if (typeof options === 'string') { 1048 plugin = $.data(this, 'plugin_ikSelect'); 1049 if (typeof plugin[options] === 'function') { 1050 plugin[options].apply(plugin, args); 1051 } 1052 } 1053 }); 1054 }; 1055 1056 $.ikSelect = { 1057 extendDefaults: function (options) { 1058 $.extend(defaults, options); 1059 } 1060 }; 1061 1062 $(document).bind('click.ikSelect', function () { 1063 if (instOpened) { 1064 instOpened.hideDropdown(); 1065 } 1066 }); 1067})(jQuery, window, document);
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.