PageSourceSearch

https://srs.myrusakov.ru/js/jquery.formstyler.js

js myrusakov.ru collected 2026-09-24 18:25:15 UTC 39,773 bytes, 1,108 lines download raw bytes

vendor: 39,768 bytes, lines 1-1106
1/*
2 * jQuery Form Styler v2.0.1
3 * https://github.com/Dimox/jQueryFormStyler
4 *
5 * Copyright 2012-2017 Dimox (http://dimox.name/)
6 * Released under the MIT license.
7 *
8 * Date: 2017.08.29
9 *
10 */
11
12;(function(factory) {
13	if (typeof define === 'function' && define.amd) {
14		// AMD
15		define(['jquery'], factory);
16	} else if (typeof exports === 'object') {
17		// CommonJS
18		module.exports = factory($ || require('jquery'));
19	} else {
20		factory(jQuery);
21	}
22}(function($) {
23
24	'use strict';
25
26	var pluginName = 'styler',
27			defaults = {
28				idSuffix: '-styler',
29				filePlaceholder: 'Файл не выбран',
30				fileBrowse: 'Обзор...',
31				fileNumber: 'Выбрано файлов: %s',
32				selectPlaceholder: 'Выберите...',
33				selectSearch: false,
34				selectSearchLimit: 10,
35				selectSearchNotFound: 'Совпадений не найдено',
36				selectSearchPlaceholder: 'Поиск...',
37				selectVisibleOptions: 0,
38				selectSmartPositioning: true,
39				locale: 'ru',
40				locales: {
41					'en': {
42						filePlaceholder: 'No file selected',
43						fileBrowse: 'Browse...',
44						fileNumber: 'Selected files: %s',
45						selectPlaceholder: 'Select...',
46						selectSearchNotFound: 'No matches found',
47						selectSearchPlaceholder: 'Search...'
48					}
49				},
50				onSelectOpened: function() {},
51				onSelectClosed: function() {},
52				onFormStyled: function() {}
53			};
54
55	function Plugin(element, options) {
56		this.element = element;
57		this.options = $.extend({}, defaults, options);
58		var locale = this.options.locale;
59		if (this.options.locales[locale] !== undefined) {
60			$.extend(this.options, this.options.locales[locale]);
61		}
62		this.init();
63	}
64
65	Plugin.prototype = {
66
67		// инициализация
68		init: function() {
69
70			var el = $(this.element);
71			var opt = this.options;
72
73			var iOS = (navigator.userAgent.match(/(iPad|iPhone|iPod)/i) && !navigator.userAgent.match(/(Windows\sPhone)/i)) ? true : false;
74			var Android = (navigator.userAgent.match(/Android/i) && !navigator.userAgent.match(/(Windows\sPhone)/i)) ? true : false;
75
76			function Attributes() {
77				if (el.attr('id') !== undefined && el.attr('id') !== '') {
78					this.id = el.attr('id') + opt.idSuffix;
79				}
80				this.title = el.attr('title');
81				this.classes = el.attr('class');
82				this.data = el.data();
83			}
84
85			// checkbox
86			if (el.is(':checkbox')) {
87
88				var checkboxOutput = function() {
89
90					var att = new Attributes();
91					var checkbox = $('<div class="jq-checkbox"><div class="jq-checkbox__div"></div></div>')
92						.attr({
93							id: att.id,
94							title: att.title
95						})
96						.addClass(att.classes)
97						.data(att.data)
98					;
99
100					el.after(checkbox).prependTo(checkbox);
101					if (el.is(':checked')) checkbox.addClass('checked');
102					if (el.is(':disabled')) checkbox.addClass('disabled');
103
104					// клик на псевдочекбокс
105					checkbox.click(function(e) {
106						e.preventDefault();
107						el.triggerHandler('click');
108						if (!checkbox.is('.disabled')) {
109							if (el.is(':checked')) {
110								el.prop('checked', false);
111								checkbox.removeClass('checked');
112							} else {
113								el.prop('checked', true);
114								checkbox.addClass('checked');
115							}
116							el.focus().change();
117						}
118					});
119					// клик на label
120					el.closest('label').add('label[for="' + el.attr('id') + '"]').on('click.styler', function(e) {
121						if (!$(e.target).is('a') && !$(e.target).closest(checkbox).length) {
122							checkbox.triggerHandler('click');
123							e.preventDefault();
124						}
125					});
126					// переключение по Space или Enter
127					el.on('change.styler', function() {
128						if (el.is(':checked')) checkbox.addClass('checked');
129						else checkbox.removeClass('checked');
130					})
131					// чтобы переключался чекбокс, который находится в теге label
132					.on('keydown.styler', function(e) {
133						if (e.which == 32) checkbox.click();
134					})
135					.on('focus.styler', function() {
136						if (!checkbox.is('.disabled')) checkbox.addClass('focused');
137					})
138					.on('blur.styler', function() {
139						checkbox.removeClass('focused');
140					});
141
142				}; // end checkboxOutput()
143
144				checkboxOutput();
145
146				// обновление при динамическом изменении
147				el.on('refresh', function() {
148					el.closest('label').add('label[for="' + el.attr('id') + '"]').off('.styler');
149					el.off('.styler').parent().before(el).remove();
150					checkboxOutput();
151				});
152
153			// end checkbox
154
155			// radio
156			} else if (el.is(':radio')) {
157
158				var radioOutput = function() {
159
160					var att = new Attributes();
161					var radio = $('<div class="jq-radio"><div class="jq-radio__div"></div></div>')
162						.attr({
163							id: att.id,
164							title: att.title
165						})
166						.addClass(att.classes)
167						.data(att.data)
168					;
169
170					el.after(radio).prependTo(radio);
171					if (el.is(':checked')) radio.addClass('checked');
172					if (el.is(':disabled')) radio.addClass('disabled');
173
174					// определяем общего родителя у радиокнопок с одинаковым name
175					// http://stackoverflow.com/a/27733847
176					$.fn.commonParents = function() {
177						var cachedThis = this;
178						return cachedThis.first().parents().filter(function() {
179							return $(this).find(cachedThis).length === cachedThis.length;
180						});
181					};
182					$.fn.commonParent = function() {
183						return $(this).commonParents().first();
184					};
185
186					// клик на псевдорадиокнопке
187					radio.click(function(e) {
188						e.preventDefault();
189						el.triggerHandler('click');
190						if (!radio.is('.disabled')) {
191							var inputName = $('input[name="' + el.attr('name') + '"]');
192							inputName.commonParent().find(inputName).prop('checked', false).parent().removeClass('checked');
193							el.prop('checked', true).parent().addClass('checked');
194							el.focus().change();
195						}
196					});
197					// клик на label
198					el.closest('label').add('label[for="' + el.attr('id') + '"]').on('click.styler', function(e) {
199						if (!$(e.target).is('a') && !$(e.target).closest(radio).length) {
200							radio.triggerHandler('click');
201							e.preventDefault();
202						}
203					});
204					// переключение стрелками
205					el.on('change.styler', function() {
206						el.parent().addClass('checked');
207					})
208					.on('focus.styler', function() {
209						if (!radio.is('.disabled')) radio.addClass('focused');
210					})
211					.on('blur.styler', function() {
212						radio.removeClass('focused');
213					});
214
215				}; // end radioOutput()
216
217				radioOutput();
218
219				// обновление при динамическом изменении
220				el.on('refresh', function() {
221					el.closest('label').add('label[for="' + el.attr('id') + '"]').off('.styler');
222					el.off('.styler').parent().before(el).remove();
223					radioOutput();
224				});
225
226			// end radio
227
228			// file
229			} else if (el.is(':file')) {
230
231				var fileOutput = function() {
232
233					var att = new Attributes();
234					var placeholder = el.data('placeholder');
235					if (placeholder === undefined) placeholder = opt.filePlaceholder;
236					var browse = el.data('browse');
237					if (browse === undefined || browse === '') browse = opt.fileBrowse;
238
239					var file =
240						$('<div class="jq-file">' +
241								'<div class="jq-file__name">' + placeholder + '</div>' +
242								'<div class="jq-file__browse">' + browse + '</div>' +
243							'</div>')
244						.attr({
245							id: att.id,
246							title: att.title
247						})
248						.addClass(att.classes)
249						.data(att.data)
250					;
251
252					el.after(file).appendTo(file);
253					if (el.is(':disabled')) file.addClass('disabled');
254
255					var value = el.val();
256					var name = $('div.jq-file__name', file);
257
258					// чтобы при динамическом изменении имя файла не сбрасывалось
259					if (value) name.text(value.replace(/.+[\\\/]/, ''));
260
261					el.on('change.styler', function() {
262						var value = el.val();
263						if (el.is('[multiple]')) {
264							value = '';
265							var files = el[0].files.length;
266							if (files > 0) {
267								var number = el.data('number');
268								if (number === undefined) number = opt.fileNumber;
269								number = number.replace('%s', files);
270								value = number;
271							}
272						}
273						name.text(value.replace(/.+[\\\/]/, ''));
274						if (value === '') {
275							name.text(placeholder);
276							file.removeClass('changed');
277						} else {
278							file.addClass('changed');
279						}
280					})
281					.on('focus.styler', function() {
282						file.addClass('focused');
283					})
284					.on('blur.styler', function() {
285						file.removeClass('focused');
286					})
287					.on('click.styler', function() {
288						file.removeClass('focused');
289					});
290
291				}; // end fileOutput()
292
293				fileOutput();
294
295				// обновление при динамическом изменении
296				el.on('refresh', function() {
297					el.off('.styler').parent().before(el).remove();
298					fileOutput();
299				});
300
301			// end file
302
303			// number
304			} else if (el.is('input[type="number"]')) {
305
306				var numberOutput = function() {
307
308					var att = new Attributes();
309					var number =
310						$('<div class="jq-number">' +
311								'<div class="jq-number__spin minus"></div>' +
312								'<div class="jq-number__spin plus"></div>' +
313							'</div>')
314						.attr({
315							id: att.id,
316							title: att.title
317						})
318						.addClass(att.classes)
319						.data(att.data)
320					;
321
322					el.after(number).prependTo(number).wrap('<div class="jq-number__field"></div>');
323					if (el.is(':disabled')) number.addClass('disabled');
324
325					var min,
326							max,
327							step,
328							timeout = null,
329							interval = null;
330					if (el.attr('min') !== undefined) min = el.attr('min');
331					if (el.attr('max') !== undefined) max = el.attr('max');
332					if (el.attr('step') !== undefined && $.isNumeric(el.attr('step')))
333						step = Number(el.attr('step'));
334					else
335						step = Number(1);
336
337					var changeValue = function(spin) {
338						var value = el.val(),
339								newValue;
340
341						if (!$.isNumeric(value)) {
342							value = 0;
343							el.val('0');
344						}
345
346						if (spin.is('.minus')) {
347							newValue = Number(value) - step;
348						} else if (spin.is('.plus')) {
349							newValue = Number(value) + step;
350						}
351
352						// определяем количество десятичных знаков после запятой в step
353						var decimals = (step.toString().split('.')[1] || []).length;
354						if (decimals > 0) {
355							var multiplier = '1';
356							while (multiplier.length <= decimals) multiplier = multiplier + '0';
357							// избегаем появления лишних знаков после запятой
358							newValue = Math.round(newValue * multiplier) / multiplier;
359						}
360
361						if ($.isNumeric(min) && $.isNumeric(max)) {
362							if (newValue >= min && newValue <= max) el.val(newValue);
363						} else if ($.isNumeric(min) && !$.isNumeric(max)) {
364							if (newValue >= min) el.val(newValue);
365						} else if (!$.isNumeric(min) && $.isNumeric(max)) {
366							if (newValue <= max) el.val(newValue);
367						} else {
368							el.val(newValue);
369						}
370					};
371
372					if (!number.is('.disabled')) {
373						number.on('mousedown', 'div.jq-number__spin', function() {
374							var spin = $(this);
375							changeValue(spin);
376							timeout = setTimeout(function(){
377								interval = setInterval(function(){ changeValue(spin); }, 40);
378							}, 350);
379						}).on('mouseup mouseout', 'div.jq-number__spin', function() {
380							clearTimeout(timeout);
381							clearInterval(interval);
382						}).on('mouseup', 'div.jq-number__spin', function() {
383							el.change().trigger('input');
384						});
385						el.on('focus.styler', function() {
386							number.addClass('focused');
387						})
388						.on('blur.styler', function() {
389							number.removeClass('focused');
390						});
391					}
392
393				}; // end numberOutput()
394
395				numberOutput();
396
397				// обновление при динамическом изменении
398				el.on('refresh', function() {
399					el.off('.styler').closest('.jq-number').before(el).remove();
400					numberOutput();
401				});
402
403			// end number
404
405			// select
406			} else if (el.is('select')) {
407
408				var selectboxOutput = function() {
409
410					// запрещаем прокрутку страницы при прокрутке селекта
411					function preventScrolling(selector) {
412
413						var scrollDiff = selector.prop('scrollHeight') - selector.outerHeight(),
414								wheelDelta = null,
415								scrollTop = null;
416
417						selector.off('mousewheel DOMMouseScroll').on('mousewheel DOMMouseScroll', function(e) {
418
419							/**
420							 * нормализация направления прокрутки
421							 * (firefox < 0 || chrome etc... > 0)
422							 * (e.originalEvent.detail < 0 || e.originalEvent.wheelDelta > 0)
423							 */
424							wheelDelta = (e.originalEvent.detail < 0 || e.originalEvent.wheelDelta > 0) ? 1 : -1; // направление прокрутки (-1 вниз, 1 вверх)
425							scrollTop = selector.scrollTop(); // позиция скролла
426
427							if ((scrollTop >= scrollDiff && wheelDelta < 0) || (scrollTop <= 0 && wheelDelta > 0)) {
428								e.stopPropagation();
429								e.preventDefault();
430							}
431
432						});
433					}
434
435					var option = $('option', el);
436					var list = '';
437					// формируем список селекта
438					function makeList() {
439						for (var i = 0; i < option.length; i++) {
440							var op = option.eq(i);
441							var li = '',
442									liClass = '',
443									liClasses = '',
444									id = '',
445									title = '',
446									dataList = '',
447									optionClass = '',
448									optgroupClass = '',
449									dataJqfsClass = '';
450							var disabled = 'disabled';
451							var selDis = 'selected sel disabled';
452							if (op.prop('selected')) liClass = 'selected sel';
453							if (op.is(':disabled')) liClass = disabled;
454							if (op.is(':selected:disabled')) liClass = selDis;
455							if (op.attr('id') !== undefined && op.attr('id') !== '') id = ' id="' + op.attr('id') + opt.idSuffix + '"';
456							if (op.attr('title') !== undefined && option.attr('title') !== '') title = ' title="' + op.attr('title') + '"';
457							if (op.attr('class') !== undefined) {
458								optionClass = ' ' + op.attr('class');
459								dataJqfsClass = ' data-jqfs-class="' + op.attr('class') + '"';
460							}
461
462							var data = op.data();
463							for (var k in data) {
464								if (data[k] !== '') dataList += ' data-' + k + '="' + data[k] + '"';
465							}
466
467							if ( (liClass + optionClass) !== '' )   liClasses = ' class="' + liClass + optionClass + '"';
468							li = '<li' + dataJqfsClass + dataList + liClasses + title + id + '>'+ op.html() +'</li>';
469
470							// если есть optgroup
471							if (op.parent().is('optgroup')) {
472								if (op.parent().attr('class') !== undefined) optgroupClass = ' ' + op.parent().attr('class');
473								li = '<li' + dataJqfsClass + dataList + ' class="' + liClass + optionClass + ' option' + optgroupClass + '"' + title + id + '>'+ op.html() +'</li>';
474								if (op.is(':first-child')) {
475									li = '<li class="optgroup' + optgroupClass + '">' + op.parent().attr('label') + '</li>' + li;
476								}
477							}
478
479							list += li;
480						}
481					} // end makeList()
482
483					// одиночный селект
484					function doSelect() {
485
486						var att = new Attributes();
487						var searchHTML = '';
488						var selectPlaceholder = el.data('placeholder');
489						var selectSearch = el.data('search');
490						var selectSearchLimit = el.data('search-limit');
491						var selectSearchNotFound = el.data('search-not-found');
492						var selectSearchPlaceholder = el.data('search-placeholder');
493						var selectSmartPositioning = el.data('smart-positioning');
494
495						if (selectPlaceholder === undefined) selectPlaceholder = opt.selectPlaceholder;
496						if (selectSearch === undefined || selectSearch === '') selectSearch = opt.selectSearch;
497						if (selectSearchLimit === undefined || selectSearchLimit === '') selectSearchLimit = opt.selectSearchLimit;
498						if (selectSearchNotFound === undefined || selectSearchNotFound === '') selectSearchNotFound = opt.selectSearchNotFound;
499						if (selectSearchPlaceholder === undefined) selectSearchPlaceholder = opt.selectSearchPlaceholder;
500						if (selectSmartPositioning === undefined || selectSmartPositioning === '') selectSmartPositioning = opt.selectSmartPositioning;
501
502						var selectbox =
503							$('<div class="jq-selectbox jqselect">' +
504									'<div class="jq-selectbox__select">' +
505										'<div class="jq-selectbox__select-text"></div>' +
506										'<div class="jq-selectbox__trigger">' +
507											'<div class="jq-selectbox__trigger-arrow"></div></div>' +
508									'</div>' +
509								'</div>')
510							.attr({
511								id: att.id,
512								title: att.title
513							})
514							.addClass(att.classes)
515							.data(att.data)
516						;
517
518						el.after(selectbox).prependTo(selectbox);
519
520						var selectzIndex = selectbox.css('z-index');
521						selectzIndex = (selectzIndex > 0 ) ? selectzIndex : 1;
522						var divSelect = $('div.jq-selectbox__select', selectbox);
523						var divText = $('div.jq-selectbox__select-text', selectbox);
524						var optionSelected = option.filter(':selected');
525
526						makeList();
527
528						if (selectSearch) searchHTML =
529							'<div class="jq-selectbox__search"><input type="search" autocomplete="off" placeholder="' + selectSearchPlaceholder + '"></div>' +
530							'<div class="jq-selectbox__not-found">' + selectSearchNotFound + '</div>';
531						var dropdown =
532							$('<div class="jq-selectbox__dropdown">' +
533									searchHTML + '<ul>' + list + '</ul>' +
534								'</div>');
535						selectbox.append(dropdown);
536						var ul = $('ul', dropdown);
537						var li = $('li', dropdown);
538						var search = $('input', dropdown);
539						var notFound = $('div.jq-selectbox__not-found', dropdown).hide();
540						if (li.length < selectSearchLimit) search.parent().hide();
541
542						// показываем опцию по умолчанию
543						// если у 1-й опции нет текста, она выбрана по умолчанию и параметр selectPlaceholder не false, то показываем плейсхолдер
544						if (option.first().text() === '' && option.first().is(':selected') && selectPlaceholder !== false) {
545							divText.text(selectPlaceholder).addClass('placeholder');
546						} else {
547							divText.text(optionSelected.text());
548						}
549
550						// определяем самый широкий пункт селекта
551						var liWidthInner = 0,
552								liWidth = 0;
553						li.css({'display': 'inline-block'});
554						li.each(function() {
555							var l = $(this);
556							if (l.innerWidth() > liWidthInner) {
557								liWidthInner = l.innerWidth();
558								liWidth = l.width();
559							}
560						});
561						li.css({'display': ''});
562
563						// подстраиваем ширину свернутого селекта в зависимости
564						// от ширины плейсхолдера или самого широкого пункта
565						if (divText.is('.placeholder') && (divText.width() > liWidthInner)) {
566							divText.width(divText.width());
567						} else {
568							var selClone = selectbox.clone().appendTo('body').width('auto');
569							var selCloneWidth = selClone.outerWidth();
570							selClone.remove();
571							if (selCloneWidth == selectbox.outerWidth()) {
572								divText.width(liWidth);
573							}
574						}
575
576						// подстраиваем ширину выпадающего списка в зависимости от самого широкого пункта
577						if (liWidthInner > selectbox.width()) dropdown.width(liWidthInner);
578
579						// прячем 1-ю пустую опцию, если она есть и если атрибут data-placeholder не пустой
580						// если все же нужно, чтобы первая пустая опция отображалась, то указываем у селекта: data-placeholder=""
581						if (option.first().text() === '' && el.data('placeholder') !== '') {
582							li.first().hide();
583						}
584
585						var selectHeight = selectbox.outerHeight(true);
586						var searchHeight = search.parent().outerHeight(true) || 0;
587						var isMaxHeight = ul.css('max-height');
588						var liSelected = li.filter('.selected');
589						if (liSelected.length < 1) li.first().addClass('selected sel');
590						if (li.data('li-height') === undefined) {
591							var liOuterHeight = li.outerHeight();
592							if (selectPlaceholder !== false) liOuterHeight = li.eq(1).outerHeight();
593							li.data('li-height', liOuterHeight);
594						}
595						var position = dropdown.css('top');
596						if (dropdown.css('left') == 'auto') dropdown.css({left: 0});
597						if (dropdown.css('top') == 'auto') {
598							dropdown.css({top: selectHeight});
599							position = selectHeight;
600						}
601						dropdown.hide();
602
603						// если выбран не дефолтный пункт
604						if (liSelected.length) {
605							// добавляем класс, показывающий изменение селекта
606							if (option.first().text() != optionSelected.text()) {
607								selectbox.addClass('changed');
608							}
609							// передаем селекту класс выбранного пункта
610							selectbox.data('jqfs-class', liSelected.data('jqfs-class'));
611							selectbox.addClass(liSelected.data('jqfs-class'));
612						}
613
614						// если селект неактивный
615						if (el.is(':disabled')) {
616							selectbox.addClass('disabled');
617							return false;
618						}
619
620						// при клике на псевдоселекте
621						divSelect.click(function() {
622
623							// колбек при закрытии селекта
624							if ($('div.jq-selectbox').filter('.opened').length) {
625								opt.onSelectClosed.call($('div.jq-selectbox').filter('.opened'));
626							}
627
628							el.focus();
629
630							// если iOS, то не показываем выпадающий список,
631							// т.к. отображается нативный и неизвестно, как его спрятать
632							if (iOS) return;
633
634							// умное позиционирование
635							var win = $(window);
636							var liHeight = li.data('li-height');
637							var topOffset = selectbox.offset().top;
638							var bottomOffset = win.height() - selectHeight - (topOffset - win.scrollTop());
639							var visible = el.data('visible-options');
640							if (visible === undefined || visible === '') visible = opt.selectVisibleOptions;
641							var minHeight = liHeight * 5;
642							var newHeight = liHeight * visible;
643							if (visible > 0 && visible < 6) minHeight = newHeight;
644							if (visible === 0) newHeight = 'auto';
645
646							var dropDown = function() {
647								dropdown.height('auto').css({bottom: 'auto', top: position});
648								var maxHeightBottom = function() {
649									ul.css('max-height', Math.floor((bottomOffset - 20 - searchHeight) / liHeight) * liHeight);
650								};
651								maxHeightBottom();
652								ul.css('max-height', newHeight);
653								if (isMaxHeight != 'none') {
654									ul.css('max-height', isMaxHeight);
655								}
656								if (bottomOffset < (dropdown.outerHeight() + 20)) {
657									maxHeightBottom();
658								}
659							};
660
661							var dropUp = function() {
662								dropdown.height('auto').css({top: 'auto', bottom: position});
663								var maxHeightTop = function() {
664									ul.css('max-height', Math.floor((topOffset - win.scrollTop() - 20 - searchHeight) / liHeight) * liHeight);
665								};
666								maxHeightTop();
667								ul.css('max-height', newHeight);
668								if (isMaxHeight != 'none') {
669									ul.css('max-height', isMaxHeight);
670								}
671								if ((topOffset - win.scrollTop() - 20) < (dropdown.outerHeight() + 20)) {
672									maxHeightTop();
673								}
674							};
675
676							if (selectSmartPositioning === true || selectSmartPositioning === 1) {
677								// раскрытие вниз
678								if (bottomOffset > (minHeight + searchHeight + 20)) {
679									dropDown();
680									selectbox.removeClass('dropup').addClass('dropdown');
681								// раскрытие вверх
682								} else {
683									dropUp();
684									selectbox.removeClass('dropdown').addClass('dropup');
685								}
686							} else if (selectSmartPositioning === false || selectSmartPositioning === 0) {
687								// раскрытие вниз
688								if (bottomOffset > (minHeight + searchHeight + 20)) {
689									dropDown();
690									selectbox.removeClass('dropup').addClass('dropdown');
691								}
692							} else {
693								// если умное позиционирование отключено
694								dropdown.height('auto').css({bottom: 'auto', top: position});
695								ul.css('max-height', newHeight);
696								if (isMaxHeight != 'none') {
697									ul.css('max-height', isMaxHeight);
698								}
699							}
700
701							// если выпадающий список выходит за правый край окна браузера,
702							// то меняем позиционирование с левого на правое
703							if (selectbox.offset().left + dropdown.outerWidth() > win.width()) {
704								dropdown.css({left: 'auto', right: 0});
705							}
706							// конец умного позиционирования
707
708							$('div.jqselect').css({zIndex: (selectzIndex - 1)}).removeClass('opened');
709							selectbox.css({zIndex: selectzIndex});
710							if (dropdown.is(':hidden')) {
711								$('div.jq-selectbox__dropdown:visible').hide();
712								dropdown.show();
713								selectbox.addClass('opened focused');
714								// колбек при открытии селекта
715								opt.onSelectOpened.call(selectbox);
716							} else {
717								dropdown.hide();
718								selectbox.removeClass('opened dropup dropdown');
719								// колбек при закрытии селекта
720								if ($('div.jq-selectbox').filter('.opened').length) {
721									opt.onSelectClosed.call(selectbox);
722								}
723							}
724
725							// поисковое поле
726							if (search.length) {
727								search.val('').keyup();
728								notFound.hide();
729								search.keyup(function() {
730									var query = $(this).val();
731									li.each(function() {
732										if (!$(this).html().match(new RegExp('.*?' + query + '.*?', 'i'))) {
733											$(this).hide();
734										} else {
735											$(this).show();
736										}
737									});
738									// прячем 1-ю пустую опцию
739									if (option.first().text() === '' && el.data('placeholder') !== '') {
740										li.first().hide();
741									}
742									if (li.filter(':visible').length < 1) {
743										notFound.show();
744									} else {
745										notFound.hide();
746									}
747								});
748							}
749
750							// прокручиваем до выбранного пункта при открытии списка
751							if (li.filter('.selected').length) {
752								if (el.val() === '') {
753									ul.scrollTop(0);
754								} else {
755									// если нечетное количество видимых пунктов,
756									// то высоту пункта делим пополам для последующего расчета
757									if ( (ul.innerHeight() / liHeight) % 2 !== 0 ) liHeight = liHeight / 2;
758									ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - ul.innerHeight() / 2 + liHeight);
759								}
760							}
761
762							preventScrolling(ul);
763
764						}); // end divSelect.click()
765
766						// при наведении курсора на пункт списка
767						li.hover(function() {
768							$(this).siblings().removeClass('selected');
769						});
770						var selectedText = li.filter('.selected').text();
771
772						// при клике на пункт списка
773						li.filter(':not(.disabled):not(.optgroup)').click(function() {
774							el.focus();
775							var t = $(this);
776							var liText = t.text();
777							if (!t.is('.selected')) {
778								var index = t.index();
779								index -= t.prevAll('.optgroup').length;
780								t.addClass('selected sel').siblings().removeClass('selected sel');
781								option.prop('selected', false).eq(index).prop('selected', true);
782								selectedText = liText;
783								divText.text(liText);
784
785								// передаем селекту класс выбранного пункта
786								if (selectbox.data('jqfs-class')) selectbox.removeClass(selectbox.data('jqfs-class'));
787								selectbox.data('jqfs-class', t.data('jqfs-class'));
788								selectbox.addClass(t.data('jqfs-class'));
789
790								el.change();
791							}
792							dropdown.hide();
793							selectbox.removeClass('opened dropup dropdown');
794							// колбек при закрытии селекта
795							opt.onSelectClosed.call(selectbox);
796
797						});
798						dropdown.mouseout(function() {
799							$('li.sel', dropdown).addClass('selected');
800						});
801
802						// изменение селекта
803						el.on('change.styler', function() {
804							divText.text(option.filter(':selected').text()).removeClass('placeholder');
805							li.removeClass('selected sel').not('.optgroup').eq(el[0].selectedIndex).addClass('selected sel');
806							// добавляем класс, показывающий изменение селекта
807							if (option.first().text() != li.filter('.selected').text()) {
808								selectbox.addClass('changed');
809							} else {
810								selectbox.removeClass('changed');
811							}
812						})
813						.on('focus.styler', function() {
814							selectbox.addClass('focused');
815							$('div.jqselect').not('.focused').removeClass('opened dropup dropdown').find('div.jq-selectbox__dropdown').hide();
816						})
817						.on('blur.styler', function() {
818							selectbox.removeClass('focused');
819						})
820						// изменение селекта с клавиатуры
821						.on('keydown.styler keyup.styler', function(e) {
822							var liHeight = li.data('li-height');
823							if (el.val() === '') {
824								divText.text(selectPlaceholder).addClass('placeholder');
825							} else {
826								divText.text(option.filter(':selected').text());
827							}
828							li.removeClass('selected sel').not('.optgroup').eq(el[0].selectedIndex).addClass('selected sel');
829							// вверх, влево, Page Up, Home
830							if (e.which == 38 || e.which == 37 || e.which == 33 || e.which == 36) {
831								if (el.val() === '') {
832									ul.scrollTop(0);
833								} else {
834									ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top);
835								}
836							}
837							// вниз, вправо, Page Down, End
838							if (e.which == 40 || e.which == 39 || e.which == 34 || e.which == 35) {
839								ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - ul.innerHeight() + liHeight);
840							}
841							// закрываем выпадающий список при нажатии Enter
842							if (e.which == 13) {
843								e.preventDefault();
844								dropdown.hide();
845								selectbox.removeClass('opened dropup dropdown');
846								// колбек при закрытии селекта
847								opt.onSelectClosed.call(selectbox);
848							}
849						}).on('keydown.styler', function(e) {
850							// открываем выпадающий список при нажатии Space
851							if (e.which == 32) {
852								e.preventDefault();
853								divSelect.click();
854							}
855						});
856
857						// прячем выпадающий список при клике за пределами селекта
858						if (!onDocumentClick.registered) {
859							$(document).on('click', onDocumentClick);
860							onDocumentClick.registered = true;
861						}
862
863					} // end doSelect()
864
865					// мультиселект
866					function doMultipleSelect() {
867
868						var att = new Attributes();
869						var selectbox =
870							$('<div class="jq-select-multiple jqselect"></div>')
871							.attr({
872								id: att.id,
873								title: att.title
874							})
875							.addClass(att.classes)
876							.data(att.data)
877						;
878
879						el.after(selectbox);
880
881						makeList();
882						selectbox.append('<ul>' + list + '</ul>');
883						var ul = $('ul', selectbox);
884						var li = $('li', selectbox);
885						var size = el.attr('size');
886						var ulHeight = ul.outerHeight();
887						var liHeight = li.outerHeight();
888						if (size !== undefined && size > 0) {
889							ul.css({'height': liHeight * size});
890						} else {
891							ul.css({'height': liHeight * 4});
892						}
893						if (ulHeight > selectbox.height()) {
894							ul.css('overflowY', 'scroll');
895							preventScrolling(ul);
896							// прокручиваем до выбранного пункта
897							if (li.filter('.selected').length) {
898								ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top);
899							}
900						}
901
902						// прячем оригинальный селект
903						el.prependTo(selectbox);
904
905						// если селект неактивный
906						if (el.is(':disabled')) {
907							selectbox.addClass('disabled');
908							option.each(function() {
909								if ($(this).is(':selected')) li.eq($(this).index()).addClass('selected');
910							});
911
912						// если селект активный
913						} else {
914
915							// при клике на пункт списка
916							li.filter(':not(.disabled):not(.optgroup)').click(function(e) {
917								el.focus();
918								var clkd = $(this);
919								if(!e.ctrlKey && !e.metaKey) clkd.addClass('selected');
920								if(!e.shiftKey) clkd.addClass('first');
921								if(!e.ctrlKey && !e.metaKey && !e.shiftKey) clkd.siblings().removeClass('selected first');
922
923								// выделение пунктов при зажатом Ctrl
924								if(e.ctrlKey || e.metaKey) {
925									if (clkd.is('.selected')) clkd.removeClass('selected first');
926										else clkd.addClass('selected first');
927									clkd.siblings().removeClass('first');
928								}
929
930								// выделение пунктов при зажатом Shift
931								if(e.shiftKey) {
932									var prev = false,
933											next = false;
934									clkd.siblings().removeClass('selected').siblings('.first').addClass('selected');
935									clkd.prevAll().each(function() {
936										if ($(this).is('.first')) prev = true;
937									});
938									clkd.nextAll().each(function() {
939										if ($(this).is('.first')) next = true;
940									});
941									if (prev) {
942										clkd.prevAll().each(function() {
943											if ($(this).is('.selected')) return false;
944												else $(this).not('.disabled, .optgroup').addClass('selected');
945										});
946									}
947									if (next) {
948										clkd.nextAll().each(function() {
949											if ($(this).is('.selected')) return false;
950												else $(this).not('.disabled, .optgroup').addClass('selected');
951										});
952									}
953									if (li.filter('.selected').length == 1) clkd.addClass('first');
954								}
955
956								// отмечаем выбранные мышью
957								option.prop('selected', false);
958								li.filter('.selected').each(function() {
959									var t = $(this);
960									var index = t.index();
961									if (t.is('.option')) index -= t.prevAll('.optgroup').length;
962									option.eq(index).prop('selected', true);
963								});
964								el.change();
965
966							});
967
968							// отмечаем выбранные с клавиатуры
969							option.each(function(i) {
970								$(this).data('optionIndex', i);
971							});
972							el.on('change.styler', function() {
973								li.removeClass('selected');
974								var arrIndexes = [];
975								option.filter(':selected').each(function() {
976									arrIndexes.push($(this).data('optionIndex'));
977								});
978								li.not('.optgroup').filter(function(i) {
979									return $.inArray(i, arrIndexes) > -1;
980								}).addClass('selected');
981							})
982							.on('focus.styler', function() {
983								selectbox.addClass('focused');
984							})
985							.on('blur.styler', function() {
986								selectbox.removeClass('focused');
987							});
988
989							// прокручиваем с клавиатуры
990							if (ulHeight > selectbox.height()) {
991								el.on('keydown.styler', function(e) {
992									// вверх, влево, PageUp
993									if (e.which == 38 || e.which == 37 || e.which == 33) {
994										ul.scrollTop(ul.scrollTop() + li.filter('.selected').position().top - liHeight);
995									}
996									// вниз, вправо, PageDown
997									if (e.which == 40 || e.which == 39 || e.which == 34) {
998										ul.scrollTop(ul.scrollTop() + li.filter('.selected:last').position().top - ul.innerHeight() + liHeight * 2);
999									}
1000								});
1001							}
1002
1003						}
1004					} // end doMultipleSelect()
1005
1006					if (el.is('[multiple]')) {
1007
1008						// если Android или iOS, то мультиселект не стилизуем
1009						// причина для Android - в стилизованном селекте нет возможности выбрать несколько пунктов
1010						// причина для iOS - в стилизованном селекте неправильно отображаются выбранные пункты
1011						if (Android || iOS) return;
1012
1013						doMultipleSelect();
1014					} else {
1015						doSelect();
1016					}
1017
1018				}; // end selectboxOutput()
1019
1020				selectboxOutput();
1021
1022				// обновление при динамическом изменении
1023				el.on('refresh', function() {
1024					el.off('.styler').parent().before(el).remove();
1025					selectboxOutput();
1026				});
1027
1028			// end select
1029
1030			// reset
1031			} else if (el.is(':reset')) {
1032				el.on('click', function() {
1033					setTimeout(function() {
1034						el.closest('form').find('input, select').trigger('refresh');
1035					}, 1);
1036				});
1037			} // end reset
1038
1039		}, // init: function()
1040
1041		// деструктор
1042		destroy: function() {
1043
1044			var el = $(this.element);
1045
1046			if (el.is(':checkbox') || el.is(':radio')) {
1047				el.removeData('_' + pluginName).off('.styler refresh').removeAttr('style').parent().before(el).remove();
1048				el.closest('label').add('label[for="' + el.attr('id') + '"]').off('.styler');
1049			} else if (el.is('input[type="number"]')) {
1050				el.removeData('_' + pluginName).off('.styler refresh').closest('.jq-number').before(el).remove();
1051			} else if (el.is(':file') || el.is('select')) {
1052				el.removeData('_' + pluginName).off('.styler refresh').removeAttr('style').parent().before(el).remove();
1053			}
1054
1055		} // destroy: function()
1056
1057	}; // Plugin.prototype
1058
1059	$.fn[pluginName] = function(options) {
1060		var args = arguments;
1061		if (options === undefined || typeof options === 'object') {
1062			this.each(function() {
1063				if (!$.data(this, '_' + pluginName)) {
1064					$.data(this, '_' + pluginName, new Plugin(this, options));
1065				}
1066			})
1067			// колбек после выполнения плагина
1068			.promise()
1069			.done(function() {
1070				var opt = $(this[0]).data('_' + pluginName);
1071				if (opt) opt.options.onFormStyled.call();
1072			});
1073			return this;
1074		} else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
1075			var returns;
1076			this.each(function() {
1077				var instance = $.data(this, '_' + pluginName);
1078				if (instance instanceof Plugin && typeof instance[options] === 'function') {
1079					returns = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
1080				}
1081			});
1082			return returns !== undefined ? returns : this;
1083		}
1084	};
1085
1086	// прячем выпадающий список при клике за пределами селекта
1087	function onDocumentClick(e) {
1088		// e.target.nodeName != 'OPTION' - добавлено для обхода бага в Opera на движке Presto
1089		// (при изменении селекта с клавиатуры срабатывает событие onclick)
1090		if (!$(e.target).parents().hasClass('jq-selectbox') && e.target.nodeName != 'OPTION') {
1091			if ($('div.jq-selectbox.opened').length) {
1092				var selectbox = $('div.jq-selectbox.opened'),
1093						search = $('div.jq-selectbox__search input', selectbox),
1094						dropdown = $('div.jq-selectbox__dropdown', selectbox),
1095						opt = selectbox.find('select').data('_' + pluginName).options;
1096
1097				// колбек при закрытии селекта
1098				opt.onSelectClosed.call(selectbox);
1099
1100				if (search.length) search.val('').keyup();
1101				dropdown.hide().find('li.sel').addClass('selected');
1102				selectbox.removeClass('focused opened dropup dropdown');
1103			}
1104		}
1105	}
1106	onDocumentClick.registered = false;
1107
1108}));

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.