PageSourceSearch

https://flior.ru/bitrix/templates/aspro_next/js/jquery.ikSelect.js

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