2026-10-30 21:59:08 +03:00
|
|
|
// ===== Cookie Banner =====
|
|
|
|
|
(function() {
|
|
|
|
|
const banner = document.getElementById('cookieBanner');
|
|
|
|
|
const acceptBtn = document.getElementById('cookieAccept');
|
|
|
|
|
if (!banner || !acceptBtn) return;
|
|
|
|
|
|
|
|
|
|
const consent = localStorage.getItem('olyapka_cookies_consent');
|
|
|
|
|
if (!consent) {
|
|
|
|
|
setTimeout(() => banner.classList.add('show'), 800);
|
|
|
|
|
} else {
|
|
|
|
|
banner.classList.add('hidden');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
acceptBtn.addEventListener('click', function() {
|
|
|
|
|
localStorage.setItem('olyapka_cookies_consent', '1');
|
|
|
|
|
banner.classList.remove('show');
|
|
|
|
|
setTimeout(() => banner.classList.add('hidden'), 400);
|
|
|
|
|
// Goal for Yandex.Metrika
|
|
|
|
|
if (typeof ym !== 'undefined') ym(110606131, 'reachGoal', 'cookie_accept');
|
|
|
|
|
if (typeof gtag !== 'undefined') gtag('event', 'cookie_accept');
|
|
|
|
|
});
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
// ===== Mobile Menu =====
|
|
|
|
|
(function() {
|
|
|
|
|
const burger = document.getElementById('burger');
|
|
|
|
|
const nav = document.getElementById('nav');
|
|
|
|
|
if (!burger || !nav) return;
|
|
|
|
|
|
|
|
|
|
burger.addEventListener('click', function() {
|
|
|
|
|
burger.classList.toggle('open');
|
|
|
|
|
nav.classList.toggle('open');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Close on link click
|
|
|
|
|
nav.querySelectorAll('.nav-link').forEach(link => {
|
|
|
|
|
link.addEventListener('click', () => {
|
|
|
|
|
burger.classList.remove('open');
|
|
|
|
|
nav.classList.remove('open');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
// ===== Goal Tracking (Yandex.Metrika + GA) =====
|
|
|
|
|
(function() {
|
|
|
|
|
document.addEventListener('click', function(e) {
|
|
|
|
|
const target = e.target.closest('[data-goal]');
|
|
|
|
|
if (!target) return;
|
|
|
|
|
const goal = target.getAttribute('data-goal');
|
|
|
|
|
if (!goal) return;
|
|
|
|
|
|
|
|
|
|
// Yandex.Metrika
|
|
|
|
|
if (typeof ym !== 'undefined') {
|
|
|
|
|
try { ym(110606131, 'reachGoal', goal); } catch(err) {}
|
|
|
|
|
}
|
|
|
|
|
// Google Analytics
|
|
|
|
|
if (typeof gtag !== 'undefined') {
|
|
|
|
|
try { gtag('event', goal); } catch(err) {}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
// ===== Smooth scroll for anchor links =====
|
|
|
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
|
|
|
anchor.addEventListener('click', function(e) {
|
|
|
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
|
|
|
if (target) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-10-30 22:31:58 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ===== Portfolio Video Lightbox =====
|
|
|
|
|
(function() {
|
|
|
|
|
const modal = document.getElementById('videoModal');
|
|
|
|
|
const player = document.getElementById('videoModalPlayer');
|
|
|
|
|
const title = document.getElementById('videoModalTitle');
|
|
|
|
|
const fsBtn = document.getElementById('videoModalFullscreen');
|
|
|
|
|
const triggers = document.querySelectorAll('.work-video[data-video]');
|
|
|
|
|
if (!modal || !player || !triggers.length) return;
|
|
|
|
|
|
|
|
|
|
let lastFocus = null;
|
|
|
|
|
|
|
|
|
|
function open(src, label) {
|
|
|
|
|
lastFocus = document.activeElement;
|
|
|
|
|
title.textContent = label || '';
|
|
|
|
|
player.src = src;
|
|
|
|
|
modal.classList.add('open');
|
|
|
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
|
|
|
document.body.classList.add('video-modal-open');
|
|
|
|
|
const p = player.play();
|
|
|
|
|
if (p && typeof p.catch === 'function') p.catch(() => {});
|
|
|
|
|
player.focus({ preventScroll: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function close() {
|
|
|
|
|
if (document.fullscreenElement) {
|
|
|
|
|
document.exitFullscreen().catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
player.pause();
|
|
|
|
|
modal.classList.remove('open');
|
|
|
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
|
|
|
document.body.classList.remove('video-modal-open');
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (!modal.classList.contains('open')) {
|
|
|
|
|
player.removeAttribute('src');
|
|
|
|
|
player.load();
|
|
|
|
|
}
|
|
|
|
|
}, 300);
|
|
|
|
|
if (lastFocus && typeof lastFocus.focus === 'function') lastFocus.focus({ preventScroll: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleFullscreen() {
|
|
|
|
|
if (document.fullscreenElement) {
|
|
|
|
|
document.exitFullscreen().catch(() => {});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (player.requestFullscreen) {
|
|
|
|
|
player.requestFullscreen().catch(() => {});
|
|
|
|
|
} else if (player.webkitEnterFullscreen) {
|
|
|
|
|
player.webkitEnterFullscreen(); // iOS Safari
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
triggers.forEach(btn => {
|
|
|
|
|
btn.addEventListener('click', function(e) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
open(btn.getAttribute('data-video'), btn.getAttribute('data-title'));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Превью: когда карточка попадает в зону видимости, гарантируем загрузку первого кадра
|
|
|
|
|
const previews = document.querySelectorAll('.work-video-preview');
|
|
|
|
|
if ('IntersectionObserver' in window && previews.length) {
|
|
|
|
|
const io = new IntersectionObserver((entries, obs) => {
|
|
|
|
|
entries.forEach(entry => {
|
|
|
|
|
if (!entry.isIntersecting) return;
|
|
|
|
|
const v = entry.target;
|
|
|
|
|
obs.unobserve(v);
|
|
|
|
|
if (v.readyState < 2) {
|
|
|
|
|
v.preload = 'auto';
|
|
|
|
|
v.load();
|
|
|
|
|
}
|
|
|
|
|
v.addEventListener('loadeddata', () => {
|
|
|
|
|
if (v.currentTime === 0) { try { v.currentTime = 0.1; } catch (err) {} }
|
|
|
|
|
v.preload = 'metadata';
|
|
|
|
|
}, { once: true });
|
|
|
|
|
});
|
|
|
|
|
}, { rootMargin: '200px 0px' });
|
|
|
|
|
previews.forEach(v => io.observe(v));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
modal.querySelectorAll('[data-video-close]').forEach(el => {
|
|
|
|
|
el.addEventListener('click', close);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Клик по тёмной области вокруг плеера тоже закрывает окно
|
|
|
|
|
modal.querySelectorAll('.video-modal-dialog, .video-modal-stage, .video-modal-bar').forEach(el => {
|
|
|
|
|
el.addEventListener('click', function(e) {
|
|
|
|
|
if (e.target === el) close();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (fsBtn) fsBtn.addEventListener('click', toggleFullscreen);
|
|
|
|
|
|
|
|
|
|
player.addEventListener('dblclick', toggleFullscreen);
|
|
|
|
|
|
|
|
|
|
document.addEventListener('keydown', function(e) {
|
|
|
|
|
if (!modal.classList.contains('open')) return;
|
|
|
|
|
if (e.key === 'Escape') { close(); return; }
|
|
|
|
|
if (e.key === ' ' && document.activeElement === player) return; // native handling
|
|
|
|
|
if (e.key === 'f' || e.key === 'F') toggleFullscreen();
|
|
|
|
|
});
|
|
|
|
|
})();
|