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