// Страница «Отзывы» (/otzyvy): вкладки, карусель, лайтбокс, форма отзыва (function () { var MARKET_STEP = 10; var section = document.querySelector('.rv-reviews'); if (!section) { return; } var tabs = section.querySelectorAll('.rv-tab'); var track = section.querySelector('.rv-track'); var cards = Array.prototype.slice.call(track.querySelectorAll('.rv-card')); var prev = section.querySelector('.rv-arrow--prev'); var next = section.querySelector('.rv-arrow--next'); var thumb = section.querySelector('.rv-progress__thumb'); var dots = section.querySelector('.rv-dots'); var title = section.querySelector('.rv-group-title'); var moreWrap = section.querySelector('.rv-more-wrap'); var moreBtn = section.querySelector('.rv-more'); var empty = section.querySelector('.rv-empty'); var filter = 'all'; var marketShown = MARKET_STEP; function visibleCards() { return cards.filter(function (card) { return !card.classList.contains('is-hidden'); }); } function step() { var card = visibleCards()[0]; if (!card) { return track.clientWidth; } var gap = parseFloat(getComputedStyle(track).columnGap) || 0; return card.offsetWidth + gap; } function perView() { return Math.max(1, Math.round((track.clientWidth + 1) / step())); } function applyFilter() { var marketIndex = 0; cards.forEach(function (card) { var group = card.getAttribute('data-group'); var hide = filter !== 'all' && group !== filter; if (!hide && filter === 'market') { hide = marketIndex >= marketShown; marketIndex++; } card.classList.toggle('is-hidden', hide); }); if (filter === 'market') { moreWrap.hidden = marketIndex <= marketShown; } empty.hidden = visibleCards().length > 0; track.style.scrollBehavior = 'auto'; track.scrollLeft = 0; track.style.scrollBehavior = ''; buildDots(); updateNav(); } function buildDots() { var pages = Math.ceil(visibleCards().length / perView()); dots.innerHTML = ''; dots.setAttribute('data-pages', pages); for (var i = 0; i < pages; i++) { var dot = document.createElement('button'); dot.type = 'button'; dot.className = 'rv-dot'; dot.setAttribute('aria-label', 'Страница ' + (i + 1)); dot.setAttribute('data-page', i); dots.appendChild(dot); } } function updateNav() { var max = track.scrollWidth - track.clientWidth; var pos = track.scrollLeft; prev.disabled = pos <= 2; next.disabled = pos >= max - 2; var ratio = max > 0 ? pos / max : 0; thumb.style.left = 'calc(' + (ratio * 100) + '% - ' + (ratio * thumb.offsetWidth) + 'px)'; var page = Math.round(pos / (step() * perView())); if (pos >= max - 2) { page = dots.children.length - 1; } Array.prototype.forEach.call(dots.children, function (dot, i) { dot.classList.toggle('is-active', i === page); }); } Array.prototype.forEach.call(tabs, function (tab) { tab.addEventListener('click', function () { filter = tab.getAttribute('data-filter'); marketShown = MARKET_STEP; Array.prototype.forEach.call(tabs, function (t) { var active = t === tab; t.classList.toggle('is-active', active); t.setAttribute('aria-selected', active ? 'true' : 'false'); }); section.setAttribute('data-filter', filter); title.textContent = tab.getAttribute('data-title'); applyFilter(); tab.scrollIntoView({block: 'nearest', inline: 'nearest'}); section.scrollIntoView({behavior: 'smooth', block: 'start'}); }); }); prev.addEventListener('click', function () { track.scrollBy({left: -step() * perView(), behavior: 'smooth'}); }); next.addEventListener('click', function () { track.scrollBy({left: step() * perView(), behavior: 'smooth'}); }); dots.addEventListener('click', function (e) { var dot = e.target.closest('.rv-dot'); if (dot) { track.scrollTo({left: step() * perView() * dot.getAttribute('data-page'), behavior: 'smooth'}); } }); track.addEventListener('scroll', updateNav, {passive: true}); window.addEventListener('resize', function () { buildDots(); updateNav(); }); moreBtn.addEventListener('click', function () { marketShown += MARKET_STEP; var marketIndex = 0; cards.forEach(function (card) { if (card.getAttribute('data-group') === 'market') { card.classList.toggle('is-hidden', marketIndex >= marketShown); marketIndex++; } }); moreWrap.hidden = marketIndex <= marketShown; }); applyFilter(); // --- Лайтбокс --- var lightbox = document.querySelector('.rv-lightbox'); var lightboxImg = lightbox.querySelector('.rv-lightbox__img'); document.addEventListener('click', function (e) { var link = e.target.closest('.js-rv-lightbox'); if (!link) { return; } e.preventDefault(); lightboxImg.src = link.getAttribute('href'); lightboxImg.alt = (link.querySelector('img') || {}).alt || ''; lightbox.hidden = false; document.body.style.overflow = 'hidden'; }); lightbox.addEventListener('click', function () { lightbox.hidden = true; lightboxImg.src = ''; document.body.style.overflow = ''; }); // --- Форма «Оставить отзыв» --- var modal = document.querySelector('.rv-modal'); var form = modal.querySelector('.rv-form'); var done = modal.querySelector('.rv-form__done'); var error = modal.querySelector('.rv-form__error'); var stars = modal.querySelectorAll('.rv-rating__star'); var ratingInput = form.querySelector('[name=rating]'); function paintStars(value) { Array.prototype.forEach.call(stars, function (star) { star.classList.toggle('is-on', +star.getAttribute('data-value') <= value); }); } function openModal() { form.reset(); ratingInput.value = ''; paintStars(0); error.hidden = true; form.hidden = false; done.hidden = true; modal.hidden = false; document.body.style.overflow = 'hidden'; if (typeof resetCaptcha === 'function') { resetCaptcha(); } } function closeModal() { modal.hidden = true; document.body.style.overflow = ''; } Array.prototype.forEach.call(stars, function (star) { star.addEventListener('click', function () { ratingInput.value = star.getAttribute('data-value'); paintStars(+ratingInput.value); }); star.addEventListener('mouseenter', function () { paintStars(+star.getAttribute('data-value')); }); }); modal.querySelector('.rv-rating').addEventListener('mouseleave', function () { paintStars(+ratingInput.value || 0); }); document.querySelector('.js-rv-form-open').addEventListener('click', openModal); modal.querySelector('.js-rv-form-close').addEventListener('click', closeModal); modal.addEventListener('click', function (e) { if (e.target === modal) { closeModal(); } }); // reCAPTCHA подгружается при первом открытии формы var captchaBox = form.querySelector('.rv-form__captcha'); var captchaId = null; window.rvCaptchaReady = function () { captchaId = window.grecaptcha.render(captchaBox, {sitekey: captchaBox.getAttribute('data-sitekey')}); }; function loadCaptcha() { if (captchaId !== null || document.getElementById('rv-captcha-api')) { return; } var script = document.createElement('script'); script.id = 'rv-captcha-api'; script.src = 'https://www.google.com/recaptcha/api.js?onload=rvCaptchaReady&render=explicit&hl=ru'; script.async = true; document.head.appendChild(script); } function resetCaptcha() { if (captchaId !== null) { window.grecaptcha.reset(captchaId); } } document.querySelector('.js-rv-form-open').addEventListener('click', loadCaptcha); function showError(message) { error.textContent = message; error.hidden = !message; } form.addEventListener('submit', function (e) { e.preventDefault(); var message = ''; if (!ratingInput.value) { message = 'Выберите оценку от 1 до 5 звёзд.'; } else if (form.elements['name'].value.trim() === '') { message = 'Укажите компанию или имя.'; form.elements['name'].focus(); } else if (form.elements['text'].value.trim() === '') { message = 'Напишите текст отзыва.'; form.elements['text'].focus(); } showError(message); if (message) { return; } var submit = form.querySelector('.rv-form__submit'); var data = new FormData(form); if (captchaId !== null) { data.set('g-recaptcha-response', window.grecaptcha.getResponse(captchaId)); } submit.disabled = true; var xhr = new XMLHttpRequest(); xhr.open('POST', form.getAttribute('action') || location.pathname); xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); xhr.onload = function () { submit.disabled = false; var res = null; try { res = JSON.parse(xhr.responseText); } catch (err) { res = null; } if (res && res.ok) { done.textContent = res.message; form.hidden = true; done.hidden = false; } else { showError(res && res.message ? res.message : 'Не удалось отправить отзыв. Попробуйте позже.'); resetCaptcha(); } }; xhr.onerror = function () { submit.disabled = false; showError('Нет связи с сервером. Попробуйте ещё раз.'); resetCaptcha(); }; xhr.send(data); }); document.addEventListener('keydown', function (e) { if (e.key === 'Escape') { if (!lightbox.hidden) { lightbox.click(); } if (!modal.hidden) { closeModal(); } } }); })();