File: /home/fliyingp/domains/fliyingpigeon.hk/public_html/wp-content/themes/ocean-theme/js/main.js
(function () {
if (window.__cf7HandlersAttached) {
console.warn('CF7 handlers already attached');
return;
}
window.__cf7HandlersAttached = true;
// ВЕСЬ твой код НИЖЕ
console.log('CF7 handlers init');
function adjustBodyMarginForFooter() {
const footerWrapper = document.querySelector('.footer-wrapper');
if (!footerWrapper) return;
if (document.querySelector('.error404')) return;
const width = window.innerWidth;
if (width <= 1024) {
document.body.style.marginBottom = ''; // reset on mobile
return;
}
const footerHeight = footerWrapper.offsetHeight;
document.body.style.marginBottom = `${footerHeight}px`;
}
adjustBodyMarginForFooter()
// Run on load
window.addEventListener('DOMContentLoaded', adjustBodyMarginForFooter);
// Run on resize
window.addEventListener('resize', adjustBodyMarginForFooter);
document.addEventListener('DOMContentLoaded', () => {
const sticky = document.querySelector('.sticky-social');
const revealContent = document.querySelector('main.reveal-content');
const offsetFromTop = window.innerHeight * 0.35;
const offsetFromBottom = 20;
const allowedSections = ['hero', 'about', 'big-header', 'top-title'];
if (!sticky || !revealContent) {
console.warn('sticky або revealContent не знайдені!');
return;
}
// Скидання стилів
sticky.style.top = 'unset';
sticky.style.transform = 'unset';
sticky.style.position = 'unset';
// Знайти останній блок із дозволених перед чужим
const sections = Array.from(document.querySelectorAll('main.reveal-content > *'));
let lastAllowedBlockBottom = null;
for (let i = 0; i < sections.length - 1; i++) {
const current = sections[i];
const next = sections[i + 1];
const isAllowed = allowedSections.some(cls => current.classList.contains(cls));
const nextIsAllowed = allowedSections.some(cls => next.classList.contains(cls));
if (isAllowed && !nextIsAllowed) {
const rect = current.getBoundingClientRect();
const scrollY = window.scrollY || window.pageYOffset;
lastAllowedBlockBottom = scrollY + rect.bottom;
break;
}
}
function onScroll() {
const stickyHeight = sticky.offsetHeight;
console.log(stickyHeight)
const revealRect = revealContent.getBoundingClientRect();
// Значення за замовчуванням
let stopAt = window.scrollY + revealRect.bottom;
console.log(stopAt)
if (lastAllowedBlockBottom !== null) {
stopAt = lastAllowedBlockBottom;
}
const stickyBottomInViewport = offsetFromTop + stickyHeight;
const stickyBottomInDocument = window.scrollY + stickyBottomInViewport;
const shouldStop = stickyBottomInDocument >= stopAt - stickyHeight;
console.log(shouldStop)
if (shouldStop) {
const absoluteTop = stopAt - stickyHeight * 2; // 🛠️ віднімаємо offsetFromBottom
sticky.style.position = 'absolute';
sticky.style.top = absoluteTop + 'px';
} else {
sticky.style.position = 'fixed';
sticky.style.top = offsetFromTop + 'px';
}
}
window.addEventListener('scroll', onScroll);
window.addEventListener('resize', () => {
// Перерахувати при ресайзі
lastAllowedBlockBottom = null;
for (let i = 0; i < sections.length - 1; i++) {
const current = sections[i];
const next = sections[i + 1];
const isAllowed = allowedSections.some(cls => current.classList.contains(cls));
const nextIsAllowed = allowedSections.some(cls => next.classList.contains(cls));
if (isAllowed && !nextIsAllowed) {
const rect = current.getBoundingClientRect();
const scrollY = window.scrollY || window.pageYOffset;
lastAllowedBlockBottom = scrollY + rect.bottom;
break;
}
}
onScroll();
});
onScroll(); // initial
sticky.style.opacity = '1';
});
document.addEventListener("DOMContentLoaded", () => {
const accordions = document.querySelectorAll(".accordion");
let openFill;
let closedFill;
let isTechnologies = false;
if (document.querySelector(".technologies__body-accordion")) {
openFill = "#001C36";
closedFill = "#001C36";
isTechnologies = true;
} else {
openFill = "#F0F2F5";
closedFill = "#F0F2F5";
}
const iconOpen = `
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M3.04688 8.63996V7.35156H12.9574V8.63996H3.04688Z" fill="${openFill}"/>
</svg>`;
const iconClosed = `
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M7.36428 8.64013H3.04688V7.35173H7.36428V3.02637H8.65267V7.35173H12.9574V8.64013H8.65267V12.9409H7.36428V8.64013Z"
fill="${closedFill}"/>
</svg>`;
accordions.forEach((accordion) => {
accordion.addEventListener("click", () => {
accordion.classList.toggle("active");
const panel = accordion.nextElementSibling;
const iconContainer = accordion.querySelector(".faq__button");
if (accordion.classList.contains("active")) {
panel.style.maxHeight = `${panel.scrollHeight + 84}px`;
if (isTechnologies) {
panel.style.marginBottom = `84px`;
} else {
panel.style.paddingBottom = `84px`;
}
iconContainer.innerHTML = iconOpen;
} else {
panel.style.maxHeight = "0px";
if (isTechnologies) {
panel.style.marginBottom = `0`;
} else {
panel.style.paddingBottom = `0`;
}
iconContainer.innerHTML = iconClosed;
}
});
});
});
document.addEventListener("DOMContentLoaded", function () {
if (document.querySelector(".project-gallery-slider")) {
const gallerySlider = new Swiper(".project-gallery-slider", {
loop: false,
slidesPerView: 1.5,
spaceBetween: 8,
pagination: {
el: ".project-gallery__pagination",
clickable: true,
},
navigation: {
nextEl: ".project-gallery__next",
prevEl: ".project-gallery__prev",
},
breakpoints: {
768: {
slidesPerView: 2.2,
},
1024: {
slidesPerView: 3,
},
},
});
}
});
// document.addEventListener("DOMContentLoaded", function () {
// const menuBtn = document.querySelector("#menuBtn");
// const fullMenu = document.querySelector(".header__upper");
// const exitBtn = document.querySelector("#exitBtn");
// menuBtn.addEventListener("click", function () {
// fullMenu.classList.toggle("opened");
// });
// exitBtn.addEventListener("click", function () {
// fullMenu.classList.toggle("opened");
// });
// });
// document.addEventListener("DOMContentLoaded", function () {
// const langBtn = document.querySelector("#langBtn");
// const langList = document.querySelector("#langList");
// const langContainer = document.querySelector("#langContainer");
// const currentLang = document.querySelector("#currentLang");
// const fullMenuLangs = document.querySelector("#fullMenuLang");
// langBtn?.addEventListener("click", function () {
// const listStatus = langList.classList.contains("opened");
// listStatus ? closeList() : openList();
// });
// document.addEventListener("click", function (e) {
// if (!langContainer.contains(e.target)) {
// closeList();
// }
// });
// function openList() {
// langList.classList.add("opened");
// }
// function closeList() {
// langList.classList.remove("opened");
// }
// function updateLangContainer(currentLangContainer, langListContainer) {
// const translatePressFloater = document.querySelector("#trp-floater-ls");
// if (!translatePressFloater) return;
// const currentObj = getCurrentLang();
// currentLangContainer.textContent = currentObj.title;
// langListContainer.innerHTML = getListLang();
// function getCurrentLang() {
// const markup = translatePressFloater.querySelector(
// "#trp-floater-ls-current-language"
// );
// return {
// title: markup?.querySelector("a")?.textContent.trim() ?? "—",
// flag: markup?.querySelector("img")?.src ?? "",
// };
// }
// function getListLang() {
// const links = translatePressFloater.querySelectorAll(
// "#trp-floater-ls-language-list .trp-language-wrap a"
// );
// const result = [];
// links.forEach((item) => {
// const isDisabled = item.classList.contains(
// "trp-floater-ls-disabled-language"
// );
// const flag = item.querySelector("img")?.src ?? "";
// const title = item.textContent.trim();
// const url = item.href;
// result.push(
// `<a class="header-lang-list-item${
// isDisabled ? " active" : ""
// }" href="${url}">
// <p class="header-lang-list-item-title">${title}</p>
// </a>`
// );
// });
// return result.join("");
// }
// }
// updateLangContainer(currentLang, langList);
// updateLangContainer(currentLang, fullMenuLangs);
// });
// document.addEventListener("DOMContentLoaded", function () {
// const langList = document.querySelector("#langList");
// const langContainer = document.querySelector("#langContainer");
// function toggleOpenLangList() {
// langList.classList.toggle("opened");
// }
// function closeLangListOnClickOutside(e) {
// if (!langContainer.contains(e.target)) {
// langList.classList.remove("opened");
// }
// }
// langContainer.addEventListener("click", toggleOpenLangList);
// document.addEventListener("click", closeLangListOnClickOutside);
// });
document.addEventListener("DOMContentLoaded", () => {
const popup = document.querySelector(".consult-popup");
const closeBtn = document.querySelector("#closeConsult");
const consultationBtn = document.querySelector("#consultationBtn");
const consultationFullBtn = document.querySelector(".header__upper-consult-btn");
if (!popup) return;
const open = () => popup.classList.add("opened");
const close = () => popup.classList.remove("opened");
consultationBtn?.addEventListener("click", open);
consultationFullBtn?.addEventListener("click", open);
closeBtn?.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
close();
});
document.addEventListener("click", (e) => {
if (!popup.classList.contains("opened")) return;
const insideContent = e.target.closest(".consult-popup__content");
const onOpenBtn =
e.target.closest("#consultationBtn") ||
e.target.closest(".header__upper-consult-btn");
if (!insideContent && !onOpenBtn) close();
});
});
document.addEventListener("DOMContentLoaded", function () {
const customSubmit = document.querySelector(".footer__custom-submit");
const hiddenSubmit = document.querySelector(".footer__submit");
if (customSubmit && hiddenSubmit) {
customSubmit.addEventListener("click", function () {
hiddenSubmit.click();
});
}
});
document.addEventListener("DOMContentLoaded", function () {
const upBtn = document.querySelector(".footer__up-btn");
if (upBtn) {
upBtn.addEventListener("click", function () {
window.scrollTo({
top: 0,
behavior: "smooth",
});
});
}
});
document.addEventListener("DOMContentLoaded", function () {
const header = document.querySelector(".header");
setTimeout(() => {
header.classList.add("visible");
}, 100);
});
document.addEventListener("DOMContentLoaded", function () {
const title = document.querySelector(".hero__title");
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
title.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{
threshold: 0.5,
}
);
if (title) observer.observe(title);
});
document.addEventListener("DOMContentLoaded", () => {
const subTextsWrapper = document.querySelector(".about__sub-text-wrapper");
if (!subTextsWrapper) return;
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
subTextsWrapper.classList.add("visible");
observer.disconnect();
}
});
},
{ threshold: 0.2 }
);
observer.observe(subTextsWrapper);
});
document.addEventListener("DOMContentLoaded", () => {
const items = document.querySelectorAll(".advatages__item");
if (!items.length) return;
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const el = entry.target;
const index = Array.from(items).indexOf(el);
const direction = index % 2 === 0 ? "-100%" : "100%";
el.style.setProperty("--translate-x", direction);
el.classList.add("visible");
observer.unobserve(el);
}
});
},
{
threshold: 0.2,
}
);
items.forEach((item) => observer.observe(item));
});
document.addEventListener("DOMContentLoaded", () => {
const items = document.querySelectorAll(".services__item");
if (items.length) {
const itemObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const el = entry.target;
const index = Array.from(items).indexOf(el);
setTimeout(() => {
el.classList.add("visible");
}, index * 200);
observer.unobserve(el);
}
});
},
{
threshold: 0.2,
}
);
items.forEach((item) => itemObserver.observe(item));
}
});
document.addEventListener("DOMContentLoaded", () => {
const subTextsWrapper = document.querySelector(".done-projects__body");
if (!subTextsWrapper) return;
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
subTextsWrapper.classList.add("visible");
observer.disconnect();
}
});
},
{ threshold: 0.3 }
);
observer.observe(subTextsWrapper);
});
document.addEventListener("DOMContentLoaded", () => {
const items = document.querySelectorAll(".partners__item");
if (items.length) {
const itemObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const el = entry.target;
const index = Array.from(items).indexOf(el);
setTimeout(() => {
el.classList.add("visible");
}, index * 200);
observer.unobserve(el);
}
});
},
{
threshold: 0.2,
}
);
items.forEach((item) => itemObserver.observe(item));
}
});
document.addEventListener("DOMContentLoaded", () => {
const subTextsWrapper = document.querySelector(".reviews__slider-container");
if (!subTextsWrapper) return;
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
subTextsWrapper.classList.add("visible");
observer.disconnect();
}
});
},
{ threshold: 0.1 }
);
observer.observe(subTextsWrapper);
});
document.addEventListener("DOMContentLoaded", () => {
const subTextsWrapper = document.querySelector(".news__body");
if (!subTextsWrapper) return;
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
subTextsWrapper.classList.add("visible");
observer.disconnect();
}
});
},
{ threshold: 0.1 }
);
observer.observe(subTextsWrapper);
});
document.addEventListener("DOMContentLoaded", () => {
const items = document.querySelectorAll(".advantages-list__item");
if (items.length) {
const itemObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const el = entry.target;
const index = Array.from(items).indexOf(el);
setTimeout(() => {
el.classList.add("visible");
}, index * 200);
observer.unobserve(el);
}
});
},
{
threshold: 0.2,
}
);
items.forEach((item) => itemObserver.observe(item));
}
});
document.addEventListener("DOMContentLoaded", () => {
const mainTexts = document.querySelectorAll(".catalog__title");
const subTextsWrapper = document.querySelector(".catalog__image");
if (!mainTexts.length || !subTextsWrapper) return;
const observer = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
mainTexts.forEach((title) => title.classList.add("visible"));
subTextsWrapper.classList.add("visible");
observer.disconnect();
}
});
},
{ threshold: 0.2 }
);
observer.observe(mainTexts[0]);
});
function observeAllTitles() {
const titles = document.querySelectorAll(".anim-title");
if (!titles.length) return;
const titleObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.2 }
);
titles.forEach((title) => titleObserver.observe(title));
}
document.addEventListener("DOMContentLoaded", () => {
observeAllTitles();
});
function observeAllMiniTitles() {
const titles = document.querySelectorAll(".mini-title");
if (!titles.length) return;
const titleObserver = new IntersectionObserver(
(entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.2 }
);
titles.forEach((title) => titleObserver.observe(title));
}
document.addEventListener("DOMContentLoaded", () => {
observeAllMiniTitles();
});
document.addEventListener('DOMContentLoaded', () => {
const backLinks = document.querySelectorAll('[data-go-back]');
backLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
history.back();
});
});
console.log(backLinks)
});
document.addEventListener('DOMContentLoaded', function () {
const FIELD_CONTAINERS = [
'.footer__input-container',
'.consultation__input-container',
'.consultation__double-input',
'.consultation__select-container',
'.consultation__textarea-container',
'.consultation__checkbox-container',
'.consultation__file-container'
];
const FIELD_CONTAINER_SELECTOR = FIELD_CONTAINERS.join(', ');
/**
* Позначити/зняти помилки у конкретній формі CF7.
* @param {HTMLFormElement} form
*/
function markFormErrors(form) {
if (!form) return;
// Очистити старі стани
form.querySelectorAll('.has-error').forEach(el => el.classList.remove('has-error'));
// Знайти всі невалідні поля CF7
const invalidFields = form.querySelectorAll('.wpcf7-not-valid, [aria-invalid="true"]');
if (invalidFields.length) {
invalidFields.forEach(field => {
// Знайти контейнер для цього поля
let container = field.closest(FIELD_CONTAINER_SELECTOR);
// Якщо поле всередині wrap-а, спробувати піднятися вище
if (!container) {
// Часто CF7 обгортає input у span.wpcf7-form-control-wrap → беремо батьківський div
const wrap = field.closest('.wpcf7-form-control-wrap');
if (wrap) {
container = wrap.closest(FIELD_CONTAINER_SELECTOR);
}
}
// Якщо нічого не знайшли — fallback на найближчий .footer__form або .consultation__form
if (!container) {
container = field.closest('.footer__form, .consultation__form, .consultation__form-container');
}
if (container) {
container.classList.add('has-error');
}
});
// Якщо є помилки — пофарбувати відповідну кнопку
const submitBtn = form.querySelector('.footer__submit, .custom-submit, .consultation__submit');
if (submitBtn) {
submitBtn.classList.add('has-error');
}
} else {
// Немає помилок — прибрати зі Submit
const submitBtn = form.querySelector('.footer__submit, .custom-submit, .consultation__submit');
if (submitBtn) {
submitBtn.classList.remove('has-error');
}
}
}
/**
* Отримати форму з події CF7.
* Подія йде або з <form.wpcf7-form>, або з wrapper .wpcf7.
*/
function getFormFromCF7Event(e) {
// Якщо target вже форма
if (e.target && e.target.tagName && e.target.tagName.toLowerCase() === 'form') {
return e.target;
}
// Якщо target wrapper .wpcf7
if (e.target && e.target.classList && e.target.classList.contains('wpcf7')) {
return e.target.querySelector('form.wpcf7-form');
}
// Спробуємо через unitTag (доступно у detail)
if (e.detail && e.detail.unitTag) {
const wrapper = document.getElementById(e.detail.unitTag);
if (wrapper) {
const f = wrapper.querySelector('form.wpcf7-form');
if (f) return f;
}
}
return null;
}
/**
* Обробники подій CF7 (всі ключові стани).
*/
['wpcf7invalid','wpcf7spam','wpcf7mailfailed','wpcf7mailsent','wpcf7submit'].forEach(evtName => {
document.addEventListener(evtName, function (e) {
const form = getFormFromCF7Event(e);
// Дочекаємось, поки CF7 розставить класи
requestAnimationFrame(() => markFormErrors(form));
});
});
/**
* Live-валідація: при вводі/зміні знімаємо помилку з конкретного поля
* і перевіряємо чи лишилися ще якісь помилки у формі.
*/
document.addEventListener('input', handleFieldInput, true);
document.addEventListener('change', handleFieldInput, true);
document.addEventListener('blur', handleFieldInput, true);
function handleFieldInput(e) {
const field = e.target;
if (!field.classList || !field.classList.contains('wpcf7-form-control')) return;
const form = field.closest('form.wpcf7-form');
if (!form) return;
// Зняти клас невалідного з поля (як UX-підказка; CF7 переоцінить при submit)
field.classList.remove('wpcf7-not-valid');
field.removeAttribute('aria-invalid');
// Зняти помилку з контейнера
let container = field.closest(FIELD_CONTAINER_SELECTOR);
if (!container) {
const wrap = field.closest('.wpcf7-form-control-wrap');
if (wrap) container = wrap.closest(FIELD_CONTAINER_SELECTOR);
}
if (container) {
container.classList.remove('has-error');
}
// Перевірити, чи лишилися інші невалідні поля
requestAnimationFrame(() => markFormErrors(form));
}
});
document.addEventListener("DOMContentLoaded", function () {
const galleryImages = document.querySelectorAll('.project-gallery__body .swiper-slide img');
galleryImages.forEach((img, index) => {
const link = document.createElement('a');
link.href = img.src;
link.setAttribute('data-fancybox', 'project-gallery');
img.parentNode.insertBefore(link, img);
link.appendChild(img);
});
});
const LenisConstructor = window.Lenis?.default || window.Lenis;
if (typeof LenisConstructor === 'function') {
const lenis = new LenisConstructor({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smooth: true,
smoothTouch: false
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
// 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();
lenis.scrollTo(target);
}
});
});
} else {
console.error('Lenis is not available');
}
})();