PageSourceSearch

https://9256601.ru/g/templates/shop2/2.141.2/js/fix_panel/fix_panel_shop_btns.js

js 9256601.ru collected 2026-10-08 17:26:52 UTC 21,539 bytes, 583 lines download raw bytes

1// Главная IIFE для инкапсуляции всей логики и предотвращения загрязнения глобальной области видимости
2
3(function($){
4	var iframesParent = window.parent.document;
5	var checkConstructor = iframesParent.getElementById('lp_constructor');
6	
7	if(checkConstructor != null) {
8		$('.shop-btns-fix-panel').addClass('hide');
9	}
10  
11  
12	// Точки перелома для адаптивного дизайна
13	const breakpoints = { xs: 480, sm: 768, md: 992 };
14
15	// Хелпер: Проверка, мобильная ли сейчас верстка
16	function isMobileLayout() {
17		return $(window).width() < breakpoints.md;
18	}
19	
20	
21	
22	function moveSearchBtn() {
23		let totalBtns = $('.shop-btns-fix-panel__item').length;
24		
25		if (totalBtns === 6) {
26			
27			let decorWrap = $(".gr-data-media-source");
28			let searchBtn = $('.shop-btns-fix-panel__item-search');
29			let searchBtnParent = searchBtn.parent('.shop-btns-fix-panel__group');
30			let searchBtnPrev = searchBtn.prev();
31			let searchPopup = $('.shop-popups-fix-panel__item--search');
32			let catalogPopup = $('.shop-popups-fix-panel__item--catalog');
33			
34			let searchPopupBl = $('.shop-popups-fix-panel__item').find('.search-wrapper');
35			let searchPopupAdv = $('.shop-popups-fix-panel__item').find('.search-advanced');
36		
37			$(window)
38				.on("resize", function () {
39					setTimeout(function(){
40						
41						let media = decorWrap.attr("data-media-source");
42					
43						if (media === 'media-xs') {
44							// скрываем поиск
45							if (!searchBtnParent.hasClass('search-detached')) {
46								searchBtn.detach();
47								searchBtnParent.addClass('search-detached');
48								if (searchBtnParent.hasClass('group-count-1')) {
49									searchBtnParent.addClass('hide');
50								}
51								
52								catalogPopup.prepend(searchPopupBl, searchPopupAdv);
53								
54							}
55						} else {
56							// возвращаем поиск на место
57							if (searchBtnParent.hasClass('search-detached')) {
58								if (searchBtnPrev.length > 0) {
59							      // Если был предыдущий брат, вставляем блок после него
60							      searchBtn.insertAfter(searchBtnPrev);
61							    } else {
62							      // Иначе (если блок был первым дочерним), вставляем его в начало родителя
63							      searchBtnParent.prepend(searchBtn);
64							    }
65							    
66								searchPopup.append(searchPopupBl, searchPopupAdv);
67								searchBtnParent.removeClass('search-detached hide');
68							}
69						}
70					}, 100);
71				})
72				.trigger("resize");
73		}
74	}
75	
76	
77	
78
79	// Логика всплывающей панели для десктопа
80	function popupsPanel(){
81		// Элементы панели
82		const $panel = $('.shop-popups-fix-panel');
83		const $overlay = $panel.find('.shop-popups-fix-panel__overlay');
84		const $body = $panel.find('.shop-popups-fix-panel__items');
85		const activeCls = 'active';
86
87		// Элементы поиска
88		const $searchItem = $panel.find('.shop-popups-fix-panel__item--search');
89		const $advBtn = $searchItem.find('.search-advanced-btn');
90		const $advBlock = $searchItem.find('.search-advanced');
91		const $advBtnClose = $searchItem.find('.search-advanced__close');
92		const advCls = 'search-advanced-active';
93
94		// По умолчанию скрываем расширенный поиск
95		$advBlock.hide();
96
97		// Открытие панели по клику на кнопку
98		$('.shop-btns-fix-panel__item-btn').on('click', function(){
99			const tabParent = $(this).parents('.shop-btns-fix-panel__item');
100			const tab = tabParent.data('tab-btn');
101			if (tab==3 || tab==4 || tab==6) {
102				closePanel();
103				return;
104			}
105			$body.children().hide();
106			// Удаляем классы popup-tab-*
107			$panel.removeClass(function(index, css){
108				return (css.match(/popup-tab-\d+/g) || []).join(' ');
109			});
110			$body.find('[data-popup-tab="'+ tab +'"]').show();
111			$panel.addClass(activeCls).addClass('popup-tab-' + tab);
112			$('html').addClass('fix-popup-open');
113		});
114
115		// Открытие/закрытие расширенного поиска
116		$advBtn.on('click', function(e){
117			e.stopPropagation();
118			$advBlock.stop(true).fadeIn(200).addClass(advCls);
119			$searchItem.addClass(advCls);
120		});
121		$advBtnClose.on('click', function(e){
122			e.stopPropagation();
123			$advBlock.stop(true).hide().removeClass(advCls);
124			$searchItem.removeClass(advCls);
125		});
126
127		// Логика закрытия панели
128		function closePanel(){
129			// Удаляем классы popup-tab-*
130			$panel.removeClass(function(index, css){
131				return (css.match(/popup-tab-\d+/g) || []).join(' ');
132			});
133			$advBlock.stop(true).hide().removeClass(advCls);
134			$searchItem.removeClass(advCls);
135			$body.children().hide();
136			$panel.removeClass(activeCls);
137			$('html').removeClass('fix-popup-open');
138		}
139
140		// Закрытие по кнопке закрытия
141		$body.on('click', '.shop-popups-fix-panel__close-btn', closePanel);
142
143		// Закрытие по клику на оверлей
144		$panel.on('click', function(e){
145			if ($(e.target).is($overlay) || !$(e.target).closest('.shop-popups-fix-panel__item').length) {
146				closePanel();
147			}
148		});
149
150		// Закрытие по клавише Esc
151		$(document).on('keyup.esc_keyup', function(keyUp){
152			if (keyUp.keyCode == 27) {
153				closePanel();
154				return false;
155			}
156		});
157	}
158
159	// Группировка кнопок в логические группы для верстки
160	function moveBtns(){
161		const groupTabs = ['2','3','4','5'];
162		const $items = $('.shop-btns-fix-panel__item');
163		let buffer = [];
164		$items.each(function(){
165			const $el = $(this);
166			const tab = String($el.data('tab-btn'));
167			if (groupTabs.indexOf(tab) !== -1) {
168				buffer.push($el);
169			} else {
170				if (buffer.length) {
171					wrap(buffer);
172					buffer = [];
173				}
174			}
175		});
176		if (buffer.length) wrap(buffer);
177
178		// Хелпер: обернуть группу
179		function wrap(arr) {
180			const $group = $(`<div class="shop-btns-fix-panel__group group-count-${arr.length}"></div>`);
181			arr[0].before($group);
182			$.each(arr, function(_, $item){
183				$group.append($item);
184			});
185		}
186	}
187
188	// Получить border-radius из LPC и установить как CSS-переменную
189	function getBtnsBorderRadiusFromLpc() {
190		let $blockForRadius = $('<div class="getBrsBtns lpc-block" style="display: none;"><div class="lpc-button--type-1"></div></div>');
191		if (!$('.getBrsBtns').length) {
192			$('body').append($blockForRadius);
193			let lpcBtnRadius = $('.getBrsBtns .lpc-button--type-1').css('border-radius');
194			document.documentElement.style.setProperty('--lpc-btn-1-brs', lpcBtnRadius);
195			$('.getBrsBtns').remove();
196		}
197	}
198
199	// Установить ширину контента из LPC для адаптивности
200	function getContentWidthFromLpc() {
201		$(window)
202			.on("resize", function () {
203				var contentWidth = $(".site-container").width() || $(".lpc-block-width").width();
204				if (!isMobileLayout()) {
205					$(".gr-data-media-source .lpc-block").css("max-width", contentWidth);
206				}
207				setMediaSource();
208			})
209			.trigger("resize");
210	}
211
212	// Обновить счетчик кнопки сравнения (для не-магазинных страниц)
213	function ajaxCompare() {
214		if (typeof shop2 !== 'undefined' && shop2.mode === "") {
215			$.post('/-/x-api/v1/public/?method=shop2/compare', {
216				action: 'get'
217			}, function(data) {
218				const count = data.result?.count || 0;
219				$('.gr-compare-btn .gr-compare-btn-amount').html(count);
220				if (+$('.gr-compare-btn .gr-compare-btn-amount').text() == 0) {
221					$('.gr-compare-btn').removeClass('active');
222				} else {
223					$('.gr-compare-btn').addClass('active');
224				}
225			});
226		}
227	}
228	
229	// Преобразовать строку цвета в массив RGB
230	const grConvertToRgb = (color) => {
231		if (color.startsWith('#')) {
232			let hex = color.replace('#', '');
233			if (hex.length === 3) {
234				hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
235			}
236			const r = parseInt(hex.substring(0, 2), 16);
237			const g = parseInt(hex.substring(2, 4), 16);
238			const b = parseInt(hex.substring(4, 6), 16);
239			return [r, g, b];
240		} else if (color.startsWith('rgb')) {
241			const parts = color.substring(color.indexOf('(') + 1, color.lastIndexOf(')')).split(',');
242			return parts.slice(0,3).map(x=>parseInt(x.trim(),10));
243		} else {
244			throw new Error('Некорректный формат цвета: ' + color);
245		}
246	};
247
248	// Хелпер: проверить прозрачность цвета
249	function isTransparent(color) {
250		if (color === 'transparent') return true;
251		if (color.startsWith('rgba(')) {
252			const parts = color.slice(5, -1).split(',');
253			return parts.length >= 4 && parseFloat(parts[3]) === 0;
254		}
255		return false;
256	}
257	
258	// Для каждой кнопки устанавливаем цвет текста/иконки
259	function btnsTextColors() {
260		$('.shop-btns-fix-panel__item').each(function() {
261			let $this = $(this);
262			let $btn = $this.find('.shop-btns-fix-panel__item-btn');
263			let $btnText = $this.find('.shop-btns-fix-panel__item-text');
264			let $btnIcon = $this.find('.shop-btns-fix-panel__item-icon .gr-mask-icon');
265			//let $btnSvg = $this.find('.shop-btns-fix-panel__item-icon svg, .shop-btns-fix-panel__item-icon svg path');
266			let bgColor = $btn.css('background-color').toLowerCase().replace(/\s/g, '');
267			if (isTransparent(bgColor)) return true;
268			let convertBg = grConvertToRgb($btn.css('background-color'));
269			let textColor = Math.round((convertBg[0] * 299 + convertBg[1] * 587 + convertBg[2] * 114) / 1000);
270			if (textColor > 150) {
271				$this.removeClass('textWhite').addClass('textDark');
272				$btnText.css('color', '#000');
273				$btnIcon.css('background', '#000');
274				//$btnSvg.css('fill', '#000');
275			} else {
276				$this.removeClass('textDark').addClass('textWhite');
277				$btnText.css('color', '#fff');
278				$btnIcon.css('background', '#fff');
279				//$btnSvg.css('fill', '#fff');
280			}
281		});
282	}
283	// Для каждого бейджа количества устанавливаем цвет текста
284	function btnsAmountColors() {
285		$('.shop-btns-fix-panel__item .shop-btns-fix-panel__item-amount').each(function() {
286			let $this = $(this);
287			let amountBgColor = $this.css('background-color').toLowerCase().replace(/\s/g, '');
288			if (isTransparent(amountBgColor)) return true;
289			let convertBg = grConvertToRgb($this.css('background-color'));
290			let bgColor = Math.round((convertBg[0] * 299 + convertBg[1] * 587 + convertBg[2] * 114) / 1000);
291			if (bgColor > 150) {
292				$this.removeClass('textWhite').addClass('textDark');
293				$this.css('color', '#000');
294			} else {
295				$this.removeClass('textDark').addClass('textWhite');
296				$this.css('color', '#fff');
297			}
298		});
299	}
300	// Для тутлипов устанавливаем цвет текста и фона
301	function bntsTooltipsColors() {
302		let $this = $('.shop-btns-fix-panel__item-btn');
303		let groupBg = $this.css('background-color').toLowerCase().replace(/\s/g, '');
304		if (isTransparent(groupBg)) return true;
305		let convertBg = grConvertToRgb($this.css('background-color'));
306		let bgColor = Math.round((convertBg[0] * 299 + convertBg[1] * 587 + convertBg[2] * 114) / 1000);
307		if (bgColor > 150) {
308			$('.shop-btns-fix-panel__item-tooltip').removeClass('whiteBg').addClass('darkBg');
309		} else {
310			$('.shop-btns-fix-panel__item-tooltip').removeClass('darkBg').addClass('whiteBg');
311		}
312	}
313
314	// Изменить цвет текста/иконки в зависимости от фона для доступности
315	function changeTextColor() {
316
317		setTimeout(function () {
318			btnsTextColors();
319			btnsAmountColors();
320			bntsTooltipsColors();
321		}, 10);
322	}
323
324	// Установить data-атрибуты для адаптации под разные разрешения
325	function setMediaSource() {
326		let decorWrap = $(".gr-data-media-source");
327		if(decorWrap.length) {
328			let decorWrapWidth = decorWrap.width();
329				
330				
331			if (decorWrapWidth < 480) {
332				decorWrap.attr("data-media-source", "media-xs");
333			} else if (decorWrapWidth < 768) {
334				decorWrap.attr("data-media-source", "media-sm");
335			} else if (decorWrapWidth < 992) {
336				decorWrap.attr("data-media-source", "media-md");
337			} else if (decorWrapWidth < 1280) {
338				decorWrap.attr("data-media-source", "media-lg");
339			} else if (decorWrapWidth >= 1280) {
340				decorWrap.attr("data-media-source", "media-xl");
341			}
342		}
343		changeTextColor();
344	}
345
346	// Логика мобильной панели (sheet) для мобильной верстки
347	function initMobileSheet() {
348		// Кэшируем DOM-элементы
349		const $html = $('html');
350		const $sheet = $('#shop-popups-fix-panel');
351		const $sheetContents = $sheet.find('.shop-popups-fix-panel__inner');
352		const $draggableArea = $sheet.find('.draggable-area');
353		const $openSheetButtons = $('.shop-btns-fix-panel__item');
354		const $panelWins = $('.shop-popups-fix-panel__item');
355		const $closeSheetBtn = $sheet.find('.close-sheet');
356		const $overlay = $sheet.find('.shop-popups-fix-panel__overlay');
357
358		// Состояния
359		let sheetHeight = 0;
360		let dragPosition = null;
361		let handlersInitialized = false;
362
363		// Установить высоту панели в vh
364		const setSheetHeight = (value) => {
365			sheetHeight = Math.max(0, Math.min(100, value));
366			$sheetContents.css('height', `${sheetHeight}vh`);
367			$sheetContents.toggleClass('fullscreen', sheetHeight === 100);
368		};
369
370		// Показать/скрыть панель
371		const setIsSheetShown = (value) => {
372			$sheet.attr('aria-hidden', String(!value));
373		};
374
375		// Установить обработчики открытия/закрытия панели
376		const setupPanelHandlers = () => {
377			if (handlersInitialized) return;
378			handlersInitialized = true;
379
380			// Открытие панели
381			$openSheetButtons.off('click.panel').on('click.panel', function () {
382				const $btn = $(this);
383				const activeBtn = $btn.data('tab-btn');
384				if (activeBtn==3 || activeBtn==4 || activeBtn==6) {
385					closeSheet();
386					return;
387				}
388				setSheetHeight(Math.min(70, 720 / window.innerHeight * 100));
389				setIsSheetShown(true);
390				$html.addClass('fix-popup-open');
391				setupTabSpecificLogic(activeBtn);
392				handleActiveTab(activeBtn);
393			});
394
395			// Закрытие панели
396			const closeSheet = () => {
397				setIsSheetShown(false);
398				resetPanelState();
399			};
400			$closeSheetBtn.add($overlay).off('click.panel').on('click.panel', closeSheet);
401		};
402
403		// Подсветить активную вкладку
404		const handleActiveTab = (activeBtn) => {
405			$openSheetButtons.removeClass('current-active');
406			$panelWins.removeClass('current-active');
407			$(`[data-tab-btn="${activeBtn}"]`).addClass('current-active');
408			$(`[data-popup-tab="${activeBtn}"]`).addClass('current-active');
409		};
410
411		// Сбросить состояние панели
412		const resetPanelState = () => {
413			$html.removeClass('fix-popup-open');
414			$('.search-advanced').removeClass('active');
415			$('.search-wrapper').removeClass('no_vis');
416			$('.search_back').removeClass('active');
417			$openSheetButtons.removeClass('current-active');
418			$panelWins.removeClass('current-active');
419			$('.folders-block__content').removeClass('hide-overflow');
420			$('.shop-popups-fix-panel__controls .folder-btn-back').removeClass('active');
421			$('.folders-block__items ul').removeClass('active current-active');
422		};
423
424		// Логика для конкретных вкладок
425		const setupTabSpecificLogic = (activeBtn) => {
426			// Сброс панели поиска
427			$('.search-advanced').removeClass('active');
428			$('.search-wrapper').removeClass('no_vis');
429			$('.search_back').removeClass('active');
430			setSheetHeight(100);
431			if (activeBtn == 1) {
432				initCatalogMenu();
433			}
434		};
435
436		// Логика меню каталога
437		const initCatalogMenu = () => {
438			$('.folders-block__content')
439				.off('click.catalog')
440				.on('click.catalog', '.folders-block__items li.has > a', handleSubmenuOpen);
441			$('.shop-popups-fix-panel__controls')
442				.off('click.folderBack')
443				.on('click.folderBack', '.folder-btn-back', handleSubmenuClose);
444		};
445
446		// Открыть подменю
447		const handleSubmenuOpen = function (e) {
448			e.preventDefault();
449			const $ul = $(this).next('ul');
450			const $container = $(this).closest('.folders-block__content');
451			$container.find('ul').removeClass('current-active');
452			$ul.addClass('active current-active').css({ top: $container.scrollTop(), height: '100vh' });
453			$container.addClass('hide-overflow');
454			$('.shop-popups-fix-panel__controls .folder-btn-back').addClass('active');
455		};
456
457		// Закрыть подменю
458		const handleSubmenuClose = () => {
459			const $container = $('.folders-block__content');
460			const $currentActive = $('.folders-block__items ul.current-active');
461			$currentActive.removeClass('active current-active');
462			$currentActive.closest('ul.active').addClass('current-active');
463			if (!$('.folders-block__items ul.active').length) {
464				$container.removeClass('hide-overflow');
465				$('.shop-popups-fix-panel__controls .folder-btn-back').removeClass('active');
466				$('.folders-block__items ul').removeClass('current-active');
467			}
468			if (!$('.folders-block__items ul.active').length) {
469				$container.removeClass('hide-overflow');
470				$('.shop-popups-fix-panel__controls .folder-btn-back').removeClass('active');
471			}
472		};
473
474		// Drag-and-drop для мобильной панели
475		const initDragAndDrop = () => {
476			const getTouchPosition = (event) => event.touches ? event.touches[0] : event;
477			const onDragStart = (event) => {
478				const originalEvent = event.originalEvent || event;
479				dragPosition = getTouchPosition(originalEvent).pageY;
480				$sheetContents.addClass("not-selectable");
481			};
482			const onDragMove = (event) => {
483				if (dragPosition === null) return;
484				if (event.type === 'touchmove') event.preventDefault();
485				const originalEvent = event.originalEvent || event;
486				const touchPoint = getTouchPosition(originalEvent);
487				if (!touchPoint) return;
488				const y = touchPoint.pageY;
489				const deltaY = dragPosition - y;
490				const deltaHeight = (deltaY / window.innerHeight) * 100;
491				setSheetHeight(sheetHeight + deltaHeight);
492				dragPosition = y;
493			};
494			const onDragEnd = () => {
495				dragPosition = null;
496				$sheetContents.removeClass("not-selectable");
497				if (sheetHeight < 40) {
498					setIsSheetShown(false);
499					$overlay.trigger('click');
500				} else if (sheetHeight > 75) {
501					setSheetHeight(100);
502				} else {
503					setSheetHeight(70);
504				}
505			};
506			$draggableArea[0].addEventListener('touchstart', onDragStart, { passive: false });
507			document.addEventListener('touchmove', onDragMove, { passive: false });
508			document.addEventListener('touchend', onDragEnd);
509			$draggableArea.on('mousedown', onDragStart);
510			$(document)
511				.on('mousemove', onDragMove)
512				.on('mouseup', onDragEnd);
513		};
514
515		// Дополнительные обработчики для панели поиска
516		const initAdditionalHandlers = () => {
517			$('.search-advanced-btn').on('click', () => {
518				$('.search-advanced').addClass('active');
519				$('.search-wrapper').addClass('no_vis');
520				$('.search_back').addClass('active');
521			});
522			$('.search_back').on('click', () => {
523				$('.search-advanced').removeClass('active');
524				$('.search-wrapper').removeClass('no_vis');
525				$('.search_back').removeClass('active');
526			});
527		};
528
529		// Инициализация всех обработчиков
530		setupPanelHandlers();
531		initDragAndDrop();
532		initAdditionalHandlers();
533	}
534
535	// Подсчитать количество кнопок и установить класс для верстки
536	function countItems() {
537		// Удаляем старые классы btns-count-*
538		const $btnsCount = $('.shop-btns-fix-panel__item').length;
539		const $btnsParent = $('.shop-btns-fix-panel__body');
540		$btnsParent.removeClass(function (index, className) {
541			return (className.match(/btns-count-\d+/g) || []).join(' ');
542		});
543		$btnsParent.addClass('btns-count-' + $btnsCount);
544	}
545	
546	// Последовательность инициализации
547	setTimeout(function(){
548		moveBtns(); // Группировка кнопок
549		getBtnsBorderRadiusFromLpc(); // Получение радиуса скругления
550		getContentWidthFromLpc(); // Установка ширины контента
551		countItems(); // Подсчет кнопок
552		if (isMobileLayout()) {
553			initMobileSheet(); // Мобильная панель
554		} else {
555			popupsPanel(); // Десктопная панель
556		}
557		ajaxCompare(); // Счетчик сравнения
558		moveSearchBtn();
559	}, 40);
560
561	// Удалить класс загрузки после небольшой задержки
562	setTimeout(function(){
563		$('.shop-btns-fix-panel').removeClass('hide-till-load');
564		
565		if (isMobileLayout()) {
566			if ($('.mosaic-mega-copyright').length) {
567				let panelsHeight = $('.shop-btns-fix-panel').innerHeight();
568				$('.mosaic-mega-copyright').css('margin-bottom', panelsHeight + 'px');
569			}
570		}
571	}, 80);
572	
573	shop2.on("afterCartAddItem", function(d, status) {
574		let totalCartAmount = $('#shop2-cart-preview .gr-cart-total-amount').text();
575		
576		if (totalCartAmount>0) {
577        	$('.shop-btns-fix-panel__item-cart .shop-btns-fix-panel__item-btn').addClass('active');
578        } else {
579        	$('.shop-btns-fix-panel__item-cart .shop-btns-fix-panel__item-btn').removeClass('active');
580        };
581    });
582
583})(jQuery);

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.