PageSourceSearch

https://i-sosna.ru/bitrix/templates/adaptive/js/jcf.select.js?178544501614901

js i-sosna.ru collected 2026-10-04 16:08:11 UTC 14,901 bytes, 430 lines download raw bytes

1// custom select module
2jcf.addModule({
3	name:'select',
4	selector:'select',
5	defaultOptions: {
6		useNativeDropOnMobileDevices: true,
7		hideDropOnScroll: true,
8		showNativeDrop: false,
9		handleDropPosition: true,
10		selectDropPosition: 'bottom', // or 'top'
11		wrapperClass:'select-area',
12		focusClass:'select-focus',
13		dropActiveClass:'select-active',
14		selectedClass:'item-selected',
15		currentSelectedClass:'current-selected',
16		disabledClass:'select-disabled',
17		valueSelector:'span.center',
18		optGroupClass:'optgroup',
19		openerSelector:'a.select-opener',
20		selectStructure:'<span class="left"></span><span class="center"></span><a class="select-opener"></a>',
21		wrapperTag: 'span',
22		classPrefix:'select-',
23		dropMaxHeight: 200,
24		dropFlippedClass: 'select-options-flipped',
25		dropHiddenClass:'options-hidden',
26		dropScrollableClass:'options-overflow',
27		dropClass:'select-options',
28		dropClassPrefix:'drop-',
29		dropStructure:'<div class="drop-holder"><div class="drop-list"></div></div>',
30		dropSelector:'div.drop-list'
31	},
32	checkElement: function(el){
33	    if ($(el).hasClass('js-select-styler-filter') || $(el).hasClass('js-select-styler')) return false;
34		return (!el.size && !el.multiple);
35	},
36	setupWrapper: function(){
37		jcf.lib.addClass(this.fakeElement, this.options.wrapperClass);
38		this.realElement.parentNode.insertBefore(this.fakeElement, this.realElement);
39		this.fakeElement.innerHTML = this.options.selectStructure;
40		this.fakeElement.style.width = (this.realElement.offsetWidth > 0 ? this.realElement.offsetWidth + 'px' : 'auto');
41
42		// show native drop if specified in options
43		// if(this.options.useNativeDropOnMobileDevices && (jcf.isTouchDevice || jcf.isWinPhoneDevice)) {
44		// 	this.options.showNativeDrop = true;
45		// }
46		if(this.options.showNativeDrop) {
47			this.fakeElement.appendChild(this.realElement);
48			jcf.lib.removeClass(this.realElement, this.options.hiddenClass);
49			jcf.lib.setStyles(this.realElement, {
50				top:0,
51				left:0,
52				margin:0,
53				padding:0,
54				opacity:0,
55				border:'none',
56				position:'absolute',
57				width: jcf.lib.getInnerWidth(this.fakeElement) - 1,
58				height: jcf.lib.getInnerHeight(this.fakeElement) - 1
59			});
60			jcf.lib.event.add(this.realElement, jcf.eventPress, function(){
61				this.realElement.title = '';
62			}, this)
63		}
64		
65		// create select body
66		this.opener = jcf.lib.queryBySelector(this.options.openerSelector, this.fakeElement)[0];
67		this.valueText = jcf.lib.queryBySelector(this.options.valueSelector, this.fakeElement)[0];
68		jcf.lib.disableTextSelection(this.valueText);
69		this.opener.jcf = this;
70
71		if(!this.options.showNativeDrop) {
72			this.createDropdown();
73			this.refreshState();
74			this.onControlReady(this);
75			this.hideDropdown(true);
76		} else {
77			this.refreshState();
78		}
79		this.addEvents();
80	},
81	addEvents: function(){
82		if(this.options.showNativeDrop) {
83			jcf.lib.event.add(this.realElement, 'click', this.onChange, this);
84		} else {
85			jcf.lib.event.add(this.fakeElement, 'click', this.toggleDropdown, this);
86		}
87		jcf.lib.event.add(this.realElement, 'change', this.onChange, this);
88	},
89	onFakeClick: function() {
90		// do nothing (drop toggles by toggleDropdown method)
91	},
92	onFocus: function(){
93		jcf.modules[this.name].superclass.onFocus.apply(this, arguments);
94		if(!this.options.showNativeDrop) {
95			// Mac Safari Fix
96			if(jcf.lib.browser.safariMac) {
97				this.realElement.setAttribute('size','2');
98			}
99			jcf.lib.event.add(this.realElement, 'keydown', this.onKeyDown, this);
100			if(jcf.activeControl && jcf.activeControl != this) {
101				jcf.activeControl.hideDropdown();
102				jcf.activeControl = this;
103			}
104		}
105	},
106	onBlur: function(){
107		if(!this.options.showNativeDrop) {
108			// Mac Safari Fix
109			if(jcf.lib.browser.safariMac) {
110				this.realElement.removeAttribute('size');
111			}
112			if(!this.isActiveDrop() || !this.isOverDrop()) {
113				jcf.modules[this.name].superclass.onBlur.apply(this);
114				if(jcf.activeControl === this) jcf.activeControl = null;
115				if(!jcf.isTouchDevice) {
116					this.hideDropdown();
117				}
118			}
119			jcf.lib.event.remove(this.realElement, 'keydown', this.onKeyDown);
120		} else {
121			jcf.modules[this.name].superclass.onBlur.apply(this);
122		}
123	},
124	onChange: function() {
125		this.refreshState();
126	},
127	onKeyDown: function(e){
128		this.dropOpened = true;
129		jcf.tmpFlag = true;
130		setTimeout(function(){jcf.tmpFlag = false},100);
131		var context = this;
132		context.keyboardFix = true;
133		setTimeout(function(){
134			context.refreshState();
135		},10);
136		if(e.keyCode == 13) {
137			context.toggleDropdown.apply(context);
138			return false;
139		}
140	},
141	onResizeWindow: function(e){
142		if(this.isActiveDrop()) {
143			this.hideDropdown();
144		}
145	},
146	onScrollWindow: function(e){
147		if(this.options.hideDropOnScroll) {
148			this.hideDropdown();
149		} else if(this.isActiveDrop()) {
150			this.positionDropdown();
151		}
152	},
153	onOptionClick: function(e){
154		var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li');
155		if(opener) {
156			this.dropOpened = true;
157			this.realElement.selectedIndex = parseInt(opener.getAttribute('rel'));
158			if(jcf.isTouchDevice) {
159				this.onFocus();
160			} else {
161				this.realElement.focus();
162			}
163			this.refreshState();
164			this.hideDropdown();
165			jcf.lib.fireEvent(this.realElement, 'change');
166		}
167		return false;
168	},
169	onClickOutside: function(e){
170		if(jcf.tmpFlag) {
171			jcf.tmpFlag = false;
172			return;
173		}
174		if(!jcf.lib.isParent(this.fakeElement, e.target) && !jcf.lib.isParent(this.selectDrop, e.target)) {
175			this.hideDropdown();
176		}
177	},
178	onDropHover: function(e){
179		if(!this.keyboardFix) {
180			this.hoverFlag = true;
181			var opener = e.target && e.target.tagName && e.target.tagName.toLowerCase() == 'li' ? e.target : jcf.lib.getParent(e.target, 'li');
182			if(opener) {
183				this.realElement.selectedIndex = parseInt(opener.getAttribute('rel'));
184				this.refreshSelectedClass(parseInt(opener.getAttribute('rel')));
185			}
186		} else {
187			this.keyboardFix = false;
188		}
189	},
190	onDropLeave: function(){
191		this.hoverFlag = false;
192	},
193	isActiveDrop: function(){
194		return !jcf.lib.hasClass(this.selectDrop, this.options.dropHiddenClass);
195	},
196	isOverDrop: function(){
197		return this.hoverFlag;
198	},
199	createDropdown: function(){
200		// remove old dropdown if exists
201		if(this.selectDrop) {
202			this.selectDrop.parentNode.removeChild(this.selectDrop);
203		}
204
205		// create dropdown holder
206		this.selectDrop = document.createElement('div');
207		this.selectDrop.className = this.options.dropClass;
208		this.selectDrop.innerHTML = this.options.dropStructure;
209		jcf.lib.setStyles(this.selectDrop, {position:'absolute'});
210		this.selectList = jcf.lib.queryBySelector(this.options.dropSelector,this.selectDrop)[0];
211		jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass);
212		document.body.appendChild(this.selectDrop);
213		this.selectDrop.jcf = this;
214		jcf.lib.event.add(this.selectDrop, 'click', this.onOptionClick, this);
215		jcf.lib.event.add(this.selectDrop, 'mouseover', this.onDropHover, this);
216		jcf.lib.event.add(this.selectDrop, 'mouseout', this.onDropLeave, this);
217		this.buildDropdown();
218	},
219	buildDropdown: function() {
220		// build select options / optgroups
221		this.buildDropdownOptions();
222
223		// position and resize dropdown
224		this.positionDropdown();
225
226		// cut dropdown if height exceedes
227		this.buildDropdownScroll();
228	},
229	buildDropdownOptions: function() {
230		this.resStructure = '';
231		this.optNum = 0;
232		for(var i = 0; i < this.realElement.children.length; i++) {
233			this.resStructure += this.buildElement(this.realElement.children[i], i) +'\n';
234		}
235		this.selectList.innerHTML = this.resStructure;
236	},
237	buildDropdownScroll: function() {
238		jcf.lib.addClass(this.selectDrop, jcf.lib.getAllClasses(this.realElement.className, this.options.dropClassPrefix, jcf.baseOptions.hiddenClass));
239		if(this.options.dropMaxHeight) {
240			if(this.selectDrop.offsetHeight > this.options.dropMaxHeight) {
241				this.selectList.style.height = this.options.dropMaxHeight+'px';
242				this.selectList.style.overflow = 'auto';
243				this.selectList.style.overflowX = 'hidden';
244				jcf.lib.addClass(this.selectDrop, this.options.dropScrollableClass);
245			}
246		}
247	},
248	parseOptionTitle: function(optTitle) {
249		return (typeof optTitle === 'string' && /\.(jpg|gif|png|bmp|jpeg)(.*)?$/i.test(optTitle)) ? optTitle : '';
250	},
251	buildElement: function(obj, index){
252		// build option
253		var res = '', optImage;
254		if(obj.tagName.toLowerCase() == 'option') {
255			if(!jcf.lib.prevSibling(obj) || jcf.lib.prevSibling(obj).tagName.toLowerCase() != 'option') {
256				res += '<ul>';
257			}
258			
259			optImage = this.parseOptionTitle(obj.title);
260			res += '<li rel="'+(this.optNum++)+'" class="'+(obj.className? obj.className + ' ' : '')+(index % 2 ? 'option-even ' : '')+'jcfcalc"><a href="#">'+(optImage ? '<img src="'+optImage+'" alt="" />
260' : '')+'<span>' + obj.innerHTML + '</span></a></li>';
261			if(!jcf.lib.nextSibling(obj) || jcf.lib.nextSibling(obj).tagName.toLowerCase() != 'option') {
262				res += '</ul>';
263			}
264			return res;
265		}
266		// build option group with options
267		else if(obj.tagName.toLowerCase() == 'optgroup' && obj.label) {
268			res += '<div class="'+this.options.optGroupClass+'">';
269			res += '<strong class="jcfcalc"><em>'+(obj.label)+'</em></strong>';
270			for(var i = 0; i < obj.children.length; i++) {
271				res += this.buildElement(obj.children[i], i);
272			}
273			res += '</div>';
274			return res;
275		}
276	},
277	positionDropdown: function(){
278		var ofs = jcf.lib.getOffset(this.fakeElement), selectAreaHeight = this.fakeElement.offsetHeight, selectDropHeight = this.selectDrop.offsetHeight;
279		var fitInTop = ofs.top - selectDropHeight >= jcf.lib.getScrollTop() && jcf.lib.getScrollTop() + jcf.lib.getWindowHeight() < ofs.top + selectAreaHeight + selectDropHeight;
280		
281		
282		if((this.options.handleDropPosition && fitInTop) || this.options.selectDropPosition === 'top') {
283			this.selectDrop.style.top = (ofs.top - selectDropHeight)+'px';
284			jcf.lib.addClass(this.fakeElement, this.options.dropFlippedClass);
285			jcf.lib.addClass(this.selectDrop, this.options.dropFlippedClass);
286		} else {
287			this.selectDrop.style.top = (ofs.top + selectAreaHeight)+'px';
288			jcf.lib.removeClass(this.fakeElement, this.options.dropFlippedClass);
289			jcf.lib.removeClass(this.selectDrop, this.options.dropFlippedClass);
290		}
291		this.selectDrop.style.left = ofs.left+'px';
292		this.selectDrop.style.width = this.fakeElement.offsetWidth+'px';
293	},
294	showDropdown: function(){
295		document.body.appendChild(this.selectDrop);
296		jcf.lib.removeClass(this.selectDrop, this.options.dropHiddenClass);
297		jcf.lib.addClass(this.fakeElement,this.options.dropActiveClass);
298		this.positionDropdown();
299
300		// highlight current active item
301		var activeItem = this.getFakeActiveOption();
302		this.removeClassFromItems(this.options.currentSelectedClass);
303		jcf.lib.addClass(activeItem, this.options.currentSelectedClass);
304		
305		// show current dropdown
306		jcf.lib.event.add(window, 'resize', this.onResizeWindow, this);
307		jcf.lib.event.add(window, 'scroll', this.onScrollWindow, this);
308		jcf.lib.event.add(document, jcf.eventPress, this.onClickOutside, this);
309		this.positionDropdown();
310	},
311	hideDropdown: function(partial){
312		if(this.selectDrop.parentNode) {
313			if(this.selectDrop.offsetWidth) {
314				this.selectDrop.parentNode.removeChild(this.selectDrop);
315			}
316			if(partial) {
317				return;
318			}
319		}
320		if(typeof this.origSelectedIndex === 'number') {
321			this.realElement.selectedIndex = this.origSelectedIndex;
322		}
323		jcf.lib.removeClass(this.fakeElement,this.options.dropActiveClass);
324		jcf.lib.addClass(this.selectDrop, this.options.dropHiddenClass);
325		jcf.lib.event.remove(window, 'resize', this.onResizeWindow);
326		jcf.lib.event.remove(window, 'scroll', this.onScrollWindow);
327		jcf.lib.event.remove(document.documentElement, jcf.eventPress, this.onClickOutside);
328		if(jcf.isTouchDevice) {
329			this.onBlur();
330		}
331	},
332	toggleDropdown: function(){
333		if(!this.realElement.disabled) {
334			if(jcf.isTouchDevice) {
335				this.onFocus();
336			} else {
337				this.realElement.focus();
338			}
339			if(this.isActiveDrop()) {
340				this.hideDropdown();
341			} else {
342				this.showDropdown();
343			}
344			this.refreshState();
345		}
346	},
347	scrollToItem: function(){
348		if(this.isActiveDrop()) {
349			var dropHeight = this.selectList.offsetHeight;
350			var offsetTop = this.calcOptionOffset(this.getFakeActiveOption());
351			var sTop = this.selectList.scrollTop;
352			var oHeight = this.getFakeActiveOption().offsetHeight;
353			//offsetTop+=sTop;
354
355			if(offsetTop >= sTop + dropHeight) {
356				this.selectList.scrollTop = offsetTop - dropHeight + oHeight;
357			} else if(offsetTop < sTop) {
358				this.selectList.scrollTop = offsetTop;
359			}
360		}
361	},
362	getFakeActiveOption: function(c) {
363		return jcf.lib.queryBySelector('li[rel="'+(typeof c === 'number' ? c : this.realElement.selectedIndex) +'"]',this.selectList)[0];
364	},
365	calcOptionOffset: function(fake) {
366		var h = 0;
367		var els = jcf.lib.queryBySelector('.jcfcalc',this.selectList);
368		for(var i = 0; i < els.length; i++) {
369			if(els[i] == fake) break;
370			h+=els[i].offsetHeight;
371		}
372		return h;
373	},
374	childrenHasItem: function(hold,item) {
375		var items = hold.getElementsByTagName('*');
376		for(i = 0; i < items.length; i++) {
377			if(items[i] == item) return true;
378		}
379		return false;
380	},
381	removeClassFromItems: function(className){
382		var children = jcf.lib.queryBySelector('li',this.selectList);
383		for(var i = children.length - 1; i >= 0; i--) {
384			jcf.lib.removeClass(children[i], className);
385		}
386	},
387	setSelectedClass: function(c){
388		jcf.lib.addClass(this.getFakeActiveOption(c), this.options.selectedClass);
389	},
390	refreshSelectedClass: function(c){
391		if(!this.options.showNativeDrop) {
392			this.removeClassFromItems(this.options.selectedClass);
393			this.setSelectedClass(c);
394		}
395		if(this.realElement.disabled) {
396			jcf.lib.addClass(this.fakeElement, this.options.disabledClass);
397			if(this.labelFor) {
398				jcf.lib.addClass(this.labelFor, this.options.labelDisabledClass);
399			}
400		} else {
401			jcf.lib.removeClass(this.fakeElement, this.options.disabledClass);
402			if(this.labelFor) {
403				jcf.lib.removeClass(this.labelFor, this.options.labelDisabledClass);
404			}
405		}
406	},
407	refreshSelectedText: function() {
408		if(!this.dropOpened && this.realElement.title) {
409			this.valueText.innerHTML = this.realElement.title;
410		} else {
411			if(this.realElement.options[this.realElement.selectedIndex].title) {
412				var optImage = this.parseOptionTitle(this.realElement.options[this.realElement.selectedIndex].title);
413				this.valueText.innerHTML = (optImage ? '<img src="'+optImage+'" alt="" />' : '') + this.realElement.options[this.realElement.selectedIndex].innerHTML;
414			} else {
415				this.valueText.innerHTML = this.realElement.options[this.realElement.selectedIndex].innerHTML;
416			}
417		}
418	},
419	refreshState: function(){
420		this.origSelectedIndex = this.realElement.selectedIndex;
421		this.refreshSelectedClass();
422		this.refreshSelectedText();
423		if(!this.options.showNativeDrop) {
424			this.positionDropdown();
425			if(this.selectDrop.offsetWidth) {
426				this.scrollToItem();
427			}
428		}
429	}
430});

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.