PageSourceSearch

https://mrcable.ru/bitrix/templates/aspro_mshop/js/jquery.ikSelect.js?175031920231829

js mrcable.ru collected 2026-10-05 07:16:38 UTC 31,829 bytes, 1,062 lines download raw bytes

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') || '&nbsp;',
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() || '&nbsp;',
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 || '&nbsp;',
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.