PROGRAMME / DAY BY DAY

ΠΑΡΑΣΚΕΥΗ 11 ΙΟΥΝΙΟΥ

Δες το πρόγραμμα της ημέρας με χρονολογική σειρά, τους χώρους και τις δράσεις που απαιτούν συμμετοχή.

00 ΕΚΔΗΛΩΣΕΙΣ
ΗΜΕΡΟΜΗΝΙΑ 11 / 06 / 2027
ΠΡΟΓΡΑΜΜΑ ΗΜΕΡΑΣ

Σε χρονολογική
σειρά.

Πάτησε σε μια εκδήλωση για αναλυτικές πληροφορίες και δήλωση συμμετοχής.

Δεν υπάρχουν εκδηλώσεις στην επιλεγμένη κατηγορία

Επίλεξε άλλη ημερομηνία από τη γραμμή ημερών ή χρησιμοποίησε το ημερολόγιο.

Επόμενη ημέρα

Please publish modules in offcanvas position.

(function () { "use strict"; const SOURCE_SELECTOR = ".press-library-source"; const GRID_SELECTOR = "#press-books-grid"; const COUNTER_SELECTOR = "#press-visible-count"; const MAX_BOOKS = 6; const MAX_ATTEMPTS = 40; const RETRY_DELAY = 300; let attempts = 0; let finished = false; function cleanText(value) { return String(value || "") .replace(/\s+/g, " ") .trim(); } function escapeHTML(value) { return String(value || "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function formatNumber(number) { return String(number).padStart(2, "0"); } function getOriginalImageURL(coverElement) { if (!coverElement) { return ""; } const image = coverElement.matches("img") ? coverElement : coverElement.querySelector("img"); if (!image) { return ""; } /* * Παίρνουμε αυτούσιο το αρχικό attribute. * Δεν χρησιμοποιούμε image.src και δεν προσθέτουμε domain. */ let imageURL = image.getAttribute("src") || image.getAttribute("data-src") || image.getAttribute("data-lazy-src") || ""; imageURL = cleanText(imageURL); /* * Διορθώνει μόνο παλιό λανθασμένο αποτέλεσμα: * /https://chaniartoonfest.gr/images/... */ if (imageURL.startsWith("/https://")) { imageURL = imageURL.substring(1); } if (imageURL.startsWith("/http://")) { imageURL = imageURL.substring(1); } return imageURL; } function getOriginalLinkURL(linkElement) { if (!linkElement) { return "#"; } let anchor = null; if (linkElement.matches("a[href]")) { anchor = linkElement; } else { anchor = linkElement.querySelector("a[href]"); } if (anchor) { return anchor.getAttribute("href") || "#"; } const textURL = cleanText(linkElement.textContent); return textURL || "#"; } function hideSource(source) { if (!source) { return; } source.style.setProperty( "display", "none", "important" ); source.style.setProperty( "visibility", "hidden", "important" ); source.style.setProperty( "height", "0", "important" ); source.style.setProperty( "max-height", "0", "important" ); source.style.setProperty( "overflow", "hidden", "important" ); source.style.setProperty( "margin", "0", "important" ); source.style.setProperty( "padding", "0", "important" ); source.setAttribute("aria-hidden", "true"); } function findSource() { const sources = Array.from( document.querySelectorAll(SOURCE_SELECTOR) ); return sources.find(function (source) { return ( source.querySelectorAll(".press-source-cover").length > 0 && source.querySelectorAll(".press-source-title").length > 0 ); }) || null; } function createBookElement(book, index) { const article = document.createElement("article"); article.className = "press-book press-book--visible"; article.setAttribute( "data-press-generated", "true" ); article.style.setProperty( "display", "block", "important" ); article.style.setProperty( "opacity", "1", "important" ); article.style.setProperty( "visibility", "visible", "important" ); article.style.setProperty( "transform", "none", "important" ); const title = escapeHTML(book.title); const creators = escapeHTML(book.creators); const image = escapeHTML(book.image); const link = escapeHTML(book.link); article.innerHTML = `
${formatNumber(index + 1)}
${title}

${title}

${creators}

`; return article; } function initializePressBooks() { if (finished) { return; } const source = findSource(); const grid = document.querySelector(GRID_SELECTOR); if (!source || !grid) { attempts++; if (attempts < MAX_ATTEMPTS) { window.setTimeout( initializePressBooks, RETRY_DELAY ); } return; } const covers = Array.from( source.querySelectorAll(".press-source-cover") ); const titles = Array.from( source.querySelectorAll(".press-source-title") ); const writers = Array.from( source.querySelectorAll(".press-source-writer") ); const artists = Array.from( source.querySelectorAll(".press-source-artist") ); const links = Array.from( source.querySelectorAll(".press-source-link") ); if (!covers.length || !titles.length) { attempts++; if (attempts < MAX_ATTEMPTS) { window.setTimeout( initializePressBooks, RETRY_DELAY ); } return; } /* * Πρώτα διαβάζουμε όλα τα δεδομένα. */ const books = []; const total = Math.min( covers.length, titles.length, MAX_BOOKS ); for (let index = 0; index < total; index++) { const title = cleanText( titles[index] ? titles[index].textContent : "" ); const writer = cleanText( writers[index] ? writers[index].textContent : "" ); const artist = cleanText( artists[index] ? artists[index].textContent : "" ); const image = getOriginalImageURL( covers[index] ); const link = getOriginalLinkURL( links[index] ); const creators = [writer, artist] .filter(Boolean) .filter(function (value, position, array) { return array.indexOf(value) === position; }) .join(" — "); console.log( "Βιβλίο " + (index + 1), { title: title, image: image, link: link } ); if (!title || !image) { continue; } books.push({ title: title, creators: creators || "Chaniartoon Press", image: image, link: link || "#" }); } /* * Κρύβουμε το αρχικό Dynamic Collection. */ hideSource(source); /* * Σημαντικό: * Διαγράφουμε ολόκληρο το περιεχόμενο του grid. * Έτσι αφαιρούνται και οι επάνω διακοσμητικοί τίτλοι. */ grid.innerHTML = ""; books.forEach(function (book, index) { grid.appendChild( createBookElement(book, index) ); }); const counter = document.querySelector( COUNTER_SELECTOR ); if (counter) { counter.textContent = formatNumber(books.length); } finished = true; console.log( "Chaniartoon Press: εμφανίστηκαν", books.length, "βιβλία." ); } initializePressBooks(); })(); (function () { "use strict"; function repairPressCoverURLs() { const sourceImages = Array.from( document.querySelectorAll( ".press-library-source .press-source-cover img" ) ); const generatedImages = Array.from( document.querySelectorAll( "#press-books-grid .press-book__cover" ) ); if (!sourceImages.length || !generatedImages.length) { window.setTimeout(repairPressCoverURLs, 300); return; } generatedImages.forEach(function (generatedImage, index) { const sourceImage = sourceImages[index]; if (!sourceImage) { return; } let correctURL = sourceImage.getAttribute("src") || sourceImage.getAttribute("data-src") || sourceImage.getAttribute("data-lazy-src") || ""; correctURL = String(correctURL).trim(); if (!correctURL) { return; } /* * Διορθώνει μόνο την πιθανή μορφή: * /https://domain/path */ if ( correctURL.startsWith("/https://") || correctURL.startsWith("/http://") ) { correctURL = correctURL.substring(1); } /* * Αν η εικόνα είναι images/..., προσθέτει αρχικό slash. */ if ( !correctURL.startsWith("/") && !correctURL.startsWith("http://") && !correctURL.startsWith("https://") ) { correctURL = "/" + correctURL; } generatedImage.setAttribute("src", correctURL); generatedImage.removeAttribute("srcset"); console.log( "Διορθώθηκε εξώφυλλο " + (index + 1) + ":", correctURL ); }); } /* * Εκτελείται μετά τον renderer. */ window.setTimeout(repairPressCoverURLs, 500); window.setTimeout(repairPressCoverURLs, 1500); })(); (function () { "use strict"; function initializeWorkshopSections() { const sections = document.querySelectorAll( "[data-workshops-section]" ); sections.forEach(function (section) { if (section.dataset.workshopsInitialized === "true") { return; } section.dataset.workshopsInitialized = "true"; const filters = Array.from( section.querySelectorAll("[data-workshop-filter]") ); const days = Array.from( section.querySelectorAll("[data-workshop-day]") ); filters.forEach(function (filterButton) { filterButton.addEventListener("click", function () { const selectedFilter = filterButton.dataset.workshopFilter; filters.forEach(function (button) { const isSelected = button === filterButton; button.classList.toggle( "is-active", isSelected ); button.setAttribute( "aria-pressed", isSelected ? "true" : "false" ); }); days.forEach(function (day) { const dayDate = day.dataset.workshopDay; const shouldShow = selectedFilter === "all" || selectedFilter === dayDate; day.classList.toggle( "is-hidden", !shouldShow ); }); }); }); }); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeWorkshopSections ); } else { initializeWorkshopSections(); } })(); (function () { "use strict"; function initializeChaniartoonLatestNews() { const newsSections = document.querySelectorAll( ".chaniartoon-latest-news" ); newsSections.forEach(function (newsSection) { const collection = newsSection.querySelector( ".sppb-dynamic-content-collection" ); if (!collection) { return; } const cards = collection.querySelectorAll( ".sppb-dynamic-content-collection__item" ); cards.forEach(function (card) { if (card.dataset.newsCardInitialized === "true") { return; } const directTextAddons = Array.from( card.children ).filter(function (child) { return child.classList.contains( "addon-root-dynamic-content-text" ); }); if (directTextAddons.length === 0) { return; } /* * Το URL είναι το τελευταίο Dynamic Text Addon. */ const urlAddon = directTextAddons[directTextAddons.length - 1]; const existingLink = urlAddon.querySelector("a[href]"); let articleUrl = ""; if (existingLink) { articleUrl = existingLink.getAttribute("href") || ""; } else { articleUrl = urlAddon.textContent.trim(); } if (!articleUrl) { return; } /* * Αποθήκευση του URL και ενεργοποίηση της κάρτας. */ card.dataset.newsCardInitialized = "true"; card.dataset.articleUrl = articleUrl; card.setAttribute("role", "link"); card.setAttribute("tabindex", "0"); /* * Click με ποντίκι. */ card.addEventListener("click", function (event) { /* * Δεν παρεμβαίνουμε όταν ο χρήστης πατά ήδη * κάποιο πραγματικό link ή button. */ if (event.target.closest("a, button")) { return; } window.location.href = card.dataset.articleUrl; }); /* * Πρόσβαση με πληκτρολόγιο. */ card.addEventListener("keydown", function (event) { if ( event.key !== "Enter" && event.key !== " " ) { return; } event.preventDefault(); window.location.href = card.dataset.articleUrl; }); }); }); } /* * Αρχική φόρτωση. */ if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeChaniartoonLatestNews ); } else { initializeChaniartoonLatestNews(); } /* * Επανεκκίνηση σε περίπτωση που το SP Page Builder * φορτώσει δυναμικά τη συλλογή λίγο αργότερα. */ window.addEventListener( "load", initializeChaniartoonLatestNews ); })(); (function () { 'use strict'; const BREAKPOINT = 1100; const DEFAULT_MENU_IMAGE = "https://www.chaniartoonfest.gr/images/history/chaniartoon-2026.jpg"; let initialized = false; function initChaniartoonHeader() { if (initialized) return; const header = document.getElementById('ctHeader'); const mobileMenu = document.getElementById('ctMobileMenu'); const searchOverlay = document.getElementById('ctSearchOverlay'); if (!header || !mobileMenu || !searchOverlay) return; initialized = true; /* Helix often clips module contents with overflow/transform. Fullscreen layers therefore become direct children of body. */ if (mobileMenu.parentElement !== document.body) { document.body.appendChild(mobileMenu); } if (searchOverlay.parentElement !== document.body) { document.body.appendChild(searchOverlay); } const navItems = Array.from(header.querySelectorAll('.ct-nav-item[data-menu]')); const megaMenus = Array.from(header.querySelectorAll('.ct-mega-menu[data-mega]')); const mobileToggle = header.querySelector('.ct-mobile-toggle'); const searchToggle = header.querySelector('.ct-search-toggle'); /* The header itself can create a stacking context because of backdrop-filter, transform or template wrappers. Move the mobile toggle directly under body so it can genuinely sit above the fullscreen mobile menu. */ if (mobileToggle && mobileToggle.parentElement !== document.body) { document.body.appendChild(mobileToggle); } const searchClose = searchOverlay.querySelector('.ct-search-close'); const searchInput = searchOverlay.querySelector('#ctSearchInput'); let megaTimer = 0; const isDesktop = () => window.innerWidth > BREAKPOINT; function syncBodyState() { const mobileOpen = mobileMenu.classList.contains('is-open'); const searchOpen = searchOverlay.classList.contains('is-open'); const locked = mobileOpen || searchOpen; document.documentElement.classList.toggle('ct-overlay-open', locked); document.body.classList.toggle('ct-overlay-open', locked); document.body.classList.toggle('ct-mobile-open', mobileOpen); } function closeMegaMenus() { window.clearTimeout(megaTimer); navItems.forEach((item) => { item.classList.remove('is-active'); const trigger = item.querySelector('.ct-nav-link'); if (trigger) trigger.setAttribute('aria-expanded', 'false'); }); megaMenus.forEach((menu) => { menu.classList.remove('is-open'); menu.setAttribute('aria-hidden', 'true'); }); } function openMegaMenu(name) { if (!isDesktop()) return; const item = header.querySelector('.ct-nav-item[data-menu="' + name + '"]'); const menu = header.querySelector('.ct-mega-menu[data-mega="' + name + '"]'); if (!item || !menu) return; closeMegaMenus(); item.classList.add('is-active'); menu.classList.add('is-open'); menu.setAttribute('aria-hidden', 'false'); const trigger = item.querySelector('.ct-nav-link'); if (trigger) trigger.setAttribute('aria-expanded', 'true'); } function closeSearch(updateState = true) { searchOverlay.classList.remove('is-open'); searchOverlay.setAttribute('aria-hidden', 'true'); if (searchToggle) searchToggle.setAttribute('aria-expanded', 'false'); if (updateState) syncBodyState(); } function openSearch() { closeMegaMenus(); closeMobile(false); searchOverlay.classList.add('is-open'); searchOverlay.setAttribute('aria-hidden', 'false'); if (searchToggle) searchToggle.setAttribute('aria-expanded', 'true'); syncBodyState(); window.setTimeout(() => { if (searchInput) searchInput.focus(); }, 100); } function closeMobile(updateState = true) { mobileMenu.classList.remove('is-open'); mobileMenu.setAttribute('aria-hidden', 'true'); header.classList.remove('mobile-open'); if (mobileToggle) { mobileToggle.setAttribute('aria-expanded', 'false'); mobileToggle.setAttribute('aria-label', 'Άνοιγμα μενού'); } if (updateState) syncBodyState(); } function openMobile() { closeMegaMenus(); closeSearch(false); mobileMenu.classList.add('is-open'); mobileMenu.setAttribute('aria-hidden', 'false'); header.classList.add('mobile-open'); if (mobileToggle) { mobileToggle.setAttribute('aria-expanded', 'true'); mobileToggle.setAttribute('aria-label', 'Κλείσιμο μενού'); } syncBodyState(); window.setTimeout(() => mobileMenu.focus({ preventScroll: true }), 50); } function swapPreview(link) { const menu = link.closest('.ct-mega-menu'); const visual = menu ? menu.querySelector('.ct-mega-visual') : null; const image = visual ? visual.querySelector('img') : null; const kicker = visual ? visual.querySelector('.ct-mega-visual-overlay span') : null; const title = visual ? visual.querySelector('.ct-mega-visual-overlay strong') : null; const nextSrc = link.getAttribute('data-image') || DEFAULT_MENU_IMAGE; if (!visual || !image) return; if (!image.dataset.originalSrc) { image.dataset.originalSrc = image.getAttribute('src') || ''; } visual.classList.add('is-changing'); image.onload = function () { visual.classList.remove('is-changing'); image.onload = null; image.onerror = null; }; image.onerror = function () { image.onerror = null; image.onload = null; image.removeAttribute('srcset'); image.setAttribute('src', DEFAULT_MENU_IMAGE); visual.classList.remove('is-changing'); }; /* Set immediately. This also works with cached images and does not depend on a separate preloader event. */ image.setAttribute('src', nextSrc); image.setAttribute('alt', link.dataset.visualTitle || link.textContent.trim()); if (kicker && link.dataset.kicker) kicker.textContent = link.dataset.kicker; if (title) title.textContent = link.dataset.visualTitle || link.textContent.trim(); if (image.complete && image.naturalWidth > 0) { requestAnimationFrame(() => visual.classList.remove('is-changing')); } } function updateScrollState() { header.classList.toggle('is-scrolled', window.scrollY > 50); } updateScrollState(); window.addEventListener('scroll', updateScrollState, { passive: true }); navItems.forEach((item) => { const name = item.dataset.menu; const trigger = item.querySelector('.ct-nav-link'); item.addEventListener('mouseenter', () => { window.clearTimeout(megaTimer); openMegaMenu(name); }); item.addEventListener('focusin', () => openMegaMenu(name)); if (trigger) { trigger.addEventListener('click', (event) => { if (!isDesktop()) return; event.preventDefault(); const menu = header.querySelector('.ct-mega-menu[data-mega="' + name + '"]'); if (menu && menu.classList.contains('is-open')) closeMegaMenus(); else openMegaMenu(name); }); } }); megaMenus.forEach((menu) => { menu.addEventListener('mouseenter', () => window.clearTimeout(megaTimer)); menu.addEventListener('mouseleave', () => { megaTimer = window.setTimeout(closeMegaMenus, 170); }); /* * Base mega-menu image fallback. * This also covers a broken parent image such as images/menu/visit.jpg. */ const baseVisual = menu.querySelector('.ct-mega-visual'); const baseImage = baseVisual ? baseVisual.querySelector(':scope > img') : null; if (baseImage) { baseImage.addEventListener('error', () => { /* During a hover swap, swapPreview owns the error handling. */ if (baseVisual.classList.contains('is-changing')) return; const currentSrc = baseImage.getAttribute('src') || ''; if (currentSrc === DEFAULT_MENU_IMAGE) return; baseImage.removeAttribute('srcset'); baseImage.setAttribute('src', DEFAULT_MENU_IMAGE); }); } /* * Ordinary submenu links use their data-image when present. * If data-image is missing, swapPreview uses DEFAULT_MENU_IMAGE. * Dynamic links are handled separately by V8 below. */ menu.querySelectorAll('.ct-mega-column a:not([data-dynamic-visual])').forEach((link) => { link.addEventListener('mouseenter', () => swapPreview(link)); link.addEventListener('focusin', () => swapPreview(link)); }); }); if (searchToggle) { searchToggle.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if (searchOverlay.classList.contains('is-open')) closeSearch(); else openSearch(); }); } if (searchClose) { searchClose.addEventListener('click', (event) => { event.preventDefault(); closeSearch(); }); } searchOverlay.addEventListener('click', (event) => { if (event.target === searchOverlay) closeSearch(); }); searchOverlay.querySelectorAll('[data-search-term]').forEach((button) => { button.addEventListener('click', () => { if (!searchInput) return; searchInput.value = button.dataset.searchTerm || ''; searchInput.focus(); }); }); if (mobileToggle) { mobileToggle.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); if (mobileMenu.classList.contains('is-open')) closeMobile(); else openMobile(); }); } mobileMenu.querySelectorAll('.ct-mobile-item').forEach((item) => { const toggle = item.querySelector('.ct-mobile-accordion-toggle'); const submenu = item.querySelector('.ct-mobile-submenu'); if (!toggle || !submenu) return; toggle.addEventListener('click', (event) => { event.preventDefault(); const shouldOpen = !item.classList.contains('is-open'); mobileMenu.querySelectorAll('.ct-mobile-item').forEach((other) => { other.classList.remove('is-open'); const otherToggle = other.querySelector('.ct-mobile-accordion-toggle'); const otherSubmenu = other.querySelector('.ct-mobile-submenu'); if (otherToggle) otherToggle.setAttribute('aria-expanded', 'false'); if (otherSubmenu) otherSubmenu.setAttribute('aria-hidden', 'true'); }); item.classList.toggle('is-open', shouldOpen); toggle.setAttribute('aria-expanded', String(shouldOpen)); submenu.setAttribute('aria-hidden', String(!shouldOpen)); }); }); mobileMenu.querySelectorAll('a').forEach((link) => { link.addEventListener('click', () => closeMobile()); }); document.addEventListener('keydown', (event) => { if (event.key !== 'Escape') return; closeMegaMenus(); closeSearch(false); closeMobile(false); syncBodyState(); }); document.addEventListener('click', (event) => { if (!isDesktop()) return; if (!event.target.closest('.ct-nav-item') && !event.target.closest('.ct-mega-menu')) { closeMegaMenus(); } }); window.addEventListener('resize', () => { if (isDesktop()) closeMobile(false); else closeMegaMenus(); syncBodyState(); }); } function boot() { initChaniartoonHeader(); if (!initialized) { const observer = new MutationObserver(() => { initChaniartoonHeader(); if (initialized) observer.disconnect(); }); observer.observe(document.documentElement, { childList: true, subtree: true }); window.setTimeout(() => observer.disconnect(), 10000); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', boot, { once: true }); } else { boot(); } })(); (function () { "use strict"; function initFooter(footer) { if (!footer || footer.dataset.ctFooterInitialized === "true") { return; } footer.dataset.ctFooterInitialized = "true"; const year = footer.querySelector("[data-ct-year]"); if (year) { year.textContent = new Date().getFullYear(); } const columns = Array.from( footer.querySelectorAll(".ct-footer-column") ); function syncColumns() { const mobile = window.matchMedia( "(max-width: 760px)" ).matches; columns.forEach(function (column, index) { const button = column.querySelector( ".ct-footer-column-title" ); if (!button) { return; } if (mobile) { if (!column.dataset.ctInitialized) { column.classList.toggle( "is-open", index === 0 ); column.dataset.ctInitialized = "true"; } button.setAttribute( "aria-expanded", column.classList.contains("is-open") ? "true" : "false" ); } else { column.classList.remove("is-open"); delete column.dataset.ctInitialized; button.setAttribute( "aria-expanded", "true" ); } }); } columns.forEach(function (column) { const button = column.querySelector( ".ct-footer-column-title" ); if (!button) { return; } button.addEventListener("click", function (event) { const mobile = window.matchMedia( "(max-width: 760px)" ).matches; if (!mobile) { return; } event.preventDefault(); const shouldOpen = !column.classList.contains("is-open"); columns.forEach(function (otherColumn) { const otherButton = otherColumn.querySelector( ".ct-footer-column-title" ); otherColumn.classList.remove("is-open"); if (otherButton) { otherButton.setAttribute( "aria-expanded", "false" ); } }); column.classList.toggle( "is-open", shouldOpen ); button.setAttribute( "aria-expanded", shouldOpen ? "true" : "false" ); }); }); syncColumns(); let resizeTimer = 0; window.addEventListener("resize", function () { window.clearTimeout(resizeTimer); resizeTimer = window.setTimeout( syncColumns, 120 ); }); } function initializeFooters() { document .querySelectorAll(".ct-footer") .forEach(initFooter); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeFooters, { once: true } ); } else { initializeFooters(); } window.addEventListener( "load", initializeFooters ); })(); (function () { "use strict"; const root = document.querySelector( ".ct-history-page" ); if (!root) { return; } const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; const revealItems = root.querySelectorAll( ".ct-history-reveal" ); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const observer = new IntersectionObserver( function (entries, currentObserver) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add( "is-visible" ); currentObserver.unobserve( entry.target ); }); }, { threshold: 0.14, rootMargin: "0px 0px -8% 0px" } ); revealItems.forEach(function (item) { observer.observe(item); }); } if (!reduceMotion) { const interludes = Array.from( root.querySelectorAll("[data-interlude]") ); let ticking = false; function updateInterludes() { const viewportHeight = window.innerHeight; interludes.forEach(function (section) { const title = section.querySelector("h2"); if (!title) { return; } const rect = section.getBoundingClientRect(); if ( rect.bottom < 0 || rect.top > viewportHeight ) { return; } const offset = rect.top + rect.height / 2 - viewportHeight / 2; const translateY = Math.max( -45, Math.min( 45, offset * -0.06 ) ); title.style.transform = "translate3d(0, " + translateY + "px, 0)"; }); ticking = false; } function requestTick() { if (ticking) { return; } ticking = true; window.requestAnimationFrame( updateInterludes ); } window.addEventListener( "scroll", requestTick, { passive: true } ); window.addEventListener( "resize", requestTick ); requestTick(); } root .querySelectorAll('a[href^="#"]') .forEach(function (link) { link.addEventListener( "click", function (event) { const selector = link.getAttribute("href"); if ( !selector || selector === "#" ) { return; } let target = null; try { target = document.querySelector(selector); } catch (error) { return; } if (!target) { return; } event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); } ); }); })(); /* ========================================================= CHANIARTOON PRESS Dynamic Content → Επίκεντρο / Επιλεγμένες / Νέες κυκλοφορίες ========================================================= */ (function () { "use strict"; /* ===================================================== ΡΥΘΜΙΣΕΙΣ ===================================================== */ const CONFIG = { /* Κρυφή συλλογή Dynamic Content */ sourceSelector: ".press-page-library-source", /* Πεδία κάθε βιβλίου */ coverSelector: ".press-page-source-cover", titleSelector: ".press-page-source-title", writerSelector: ".press-page-source-writer", artistSelector: ".press-page-source-artist", linkSelector: ".press-page-source-link", dateSelector: ".press-page-source-date", selectedSelector: ".press-page-source-selected", focusSelector: ".press-page-source-focus", /* Σημεία εμφάνισης στη σελίδα */ featuredTarget: "#press-page-featured-book", libraryTarget: "#press-page-books-grid", latestTarget: "#press-page-latest-grid", countTarget: "#press-page-visible-count", countLabelTarget: "#press-page-count-label", /* Πόσες νέες κυκλοφορίες εμφανίζονται */ latestLimit: 4, retryDelay: 350, maximumRetries: 30, /* true για μηνύματα ελέγχου στην Console */ debug: true }; let initialized = false; let retryCount = 0; let retryTimer = null; let observer = null; /* ===================================================== ΒΑΣΙΚΕΣ ΒΟΗΘΗΤΙΚΕΣ ΣΥΝΑΡΤΗΣΕΙΣ ===================================================== */ function cleanText(value) { return String(value || "") .replace(/\u00a0/g, " ") .replace(/\s+/g, " ") .trim(); } function normalizeName(value) { return cleanText(value) .toLocaleLowerCase("el-GR") .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .replace(/[.,;:·•|/\\()[\]{}'"«»“”‘’–—_-]/g, "") .replace(/\s+/g, " ") .trim(); } function escapeHTML(value) { return String(value || "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function escapeAttribute(value) { return escapeHTML(value); } function debugLog() { if (!CONFIG.debug) { return; } const args = Array.from(arguments); args.unshift("Chaniartoon Press:"); console.log.apply(console, args); } /* ===================================================== ΔΙΟΡΘΩΣΗ URL ΕΙΚΟΝΩΝ ===================================================== */ function normalizeMediaURL(value) { let rawURL = cleanText(value); if (!rawURL) { return ""; } rawURL = rawURL .replace(/^['"]+|['"]+$/g, "") .replace(/&/g, "&") .replace(/\\/g, "/") .trim(); if (/%(?:2f|3a)/i.test(rawURL)) { try { rawURL = decodeURIComponent(rawURL); } catch (error) { /* Κρατάμε την αρχική τιμή. */ } } if ( rawURL.startsWith("data:") || rawURL.startsWith("blob:") ) { return rawURL; } const malformedImagesHost = rawURL.match( /^(?:https?:)?\/\/images(?:\/|$)(.*)$/i ); if (malformedImagesHost) { const remainingPath = String( malformedImagesHost[1] || "" ).replace(/^\/+/, ""); return remainingPath ? "/images/" + remainingPath : "/images/"; } const lowerURL = rawURL.toLowerCase(); const imagesPosition = lowerURL.lastIndexOf("/images/"); if (imagesPosition !== -1) { return rawURL.substring(imagesPosition); } if (/^images\//i.test(rawURL)) { return "/" + rawURL; } if (/^\.\/images\//i.test(rawURL)) { return "/" + rawURL.substring(2); } if (/^https?:\/\//i.test(rawURL)) { return rawURL; } if (rawURL.startsWith("//")) { return window.location.protocol + rawURL; } if (rawURL.startsWith("/")) { return rawURL; } return "/" + rawURL.replace(/^\.?\//, ""); } /* ===================================================== ΔΙΟΡΘΩΣΗ URL ΣΥΝΔΕΣΜΩΝ ===================================================== */ function isUsableURL(value) { const url = cleanText(value); if (!url) { return false; } const lowerURL = url.toLowerCase(); return !( url === "#" || lowerURL.startsWith("javascript:") || lowerURL.startsWith("mailto:") || lowerURL.startsWith("tel:") ); } function normalizeLinkURL(value) { let rawURL = cleanText(value); if (!isUsableURL(rawURL)) { return ""; } rawURL = rawURL .replace(/&/g, "&") .replace(/\\/g, "/") .trim(); if (/%(?:2f|3a)/i.test(rawURL)) { try { rawURL = decodeURIComponent(rawURL); } catch (error) { /* Κρατάμε την αρχική τιμή. */ } } const schemeMatches = Array.from( rawURL.matchAll(/https?:\/\//gi) ); if (schemeMatches.length) { const lastScheme = schemeMatches[ schemeMatches.length - 1 ]; if (lastScheme.index > 0) { rawURL = rawURL.substring(lastScheme.index); } } if ( rawURL.startsWith("#") || /^https?:\/\//i.test(rawURL) ) { return rawURL; } if (rawURL.startsWith("//")) { return window.location.protocol + rawURL; } if (/^www\./i.test(rawURL)) { return "https://" + rawURL; } if (rawURL.startsWith("/")) { return rawURL; } return "/" + rawURL.replace(/^\.?\//, ""); } /* ===================================================== ΤΕΛΙΚΟΣ ΕΛΕΓΧΟΣ URL ΜΕΤΑ ΤΟ RENDER ===================================================== */ function repairRenderedURLs() { const targetSelectors = [ CONFIG.featuredTarget, CONFIG.libraryTarget, CONFIG.latestTarget ].join(", "); const targets = document.querySelectorAll(targetSelectors); targets.forEach(function (target) { target.querySelectorAll("img").forEach(function (image) { const currentValue = image.getAttribute("src") || image.currentSrc || image.src || ""; const correctedValue = normalizeMediaURL(currentValue); if ( correctedValue && image.getAttribute("src") !== correctedValue ) { image.setAttribute("src", correctedValue); } image.removeAttribute("srcset"); }); target.querySelectorAll("a[href]").forEach(function (anchor) { const currentValue = anchor.getAttribute("href") || anchor.href || ""; const correctedValue = normalizeLinkURL(currentValue); if ( correctedValue && anchor.getAttribute("href") !== correctedValue ) { anchor.setAttribute("href", correctedValue); } }); }); } function scheduleRenderedURLRepairs() { [0, 150, 500, 1200, 2500].forEach(function (delay) { window.setTimeout(repairRenderedURLs, delay); }); } /* ===================================================== ΑΝΑΓΝΩΣΗ ΠΕΔΙΩΝ ===================================================== */ function getTextFromField(item, selector) { const field = item.querySelector(selector); if (!field) { return ""; } const formElement = field.querySelector( "input, select, textarea" ); if (formElement && cleanText(formElement.value)) { return cleanText(formElement.value); } const preferredElement = field.querySelector( ".sppb-addon-content, " + ".sppb-addon-text-block, " + ".sppb-dynamic-content-text, " + "time, h1, h2, h3, h4, h5, h6, p, span" ); if (preferredElement) { const dateTimeValue = preferredElement.getAttribute && preferredElement.getAttribute("datetime"); if (dateTimeValue) { return cleanText(dateTimeValue); } return cleanText(preferredElement.textContent); } return cleanText(field.textContent); } function getImageAttribute(image) { if (!image) { return ""; } return ( image.getAttribute("data-src") || image.getAttribute("data-lazy-src") || image.getAttribute("data-original") || image.getAttribute("data-image") || image.getAttribute("src") || image.currentSrc || "" ); } function getFirstSrcsetURL(srcset) { const cleanSrcset = cleanText(srcset); if (!cleanSrcset) { return ""; } return cleanSrcset .split(",")[0] .trim() .split(/\s+/)[0]; } function getImageFromField(item) { const field = item.querySelector(CONFIG.coverSelector); if (!field) { return { src: "", alt: "", original: "" }; } const image = field.matches("img") ? field : field.querySelector("img"); if (image) { const originalURL = getImageAttribute(image); return { src: normalizeMediaURL(originalURL), alt: cleanText(image.getAttribute("alt") || ""), original: originalURL }; } const sourceElement = field.querySelector( "picture source[srcset], source[srcset]" ); if (sourceElement) { const originalURL = getFirstSrcsetURL( sourceElement.getAttribute("srcset") ); if (originalURL) { return { src: normalizeMediaURL(originalURL), alt: "", original: originalURL }; } } const backgroundCandidates = [ field, ...Array.from(field.querySelectorAll("*")) ]; const backgroundElement = backgroundCandidates.find( function (element) { const backgroundImage = window .getComputedStyle(element) .backgroundImage; return ( backgroundImage && backgroundImage !== "none" && backgroundImage.includes("url(") ); } ); if (backgroundElement) { const backgroundImage = window .getComputedStyle(backgroundElement) .backgroundImage; const match = backgroundImage.match( /url\(["']?(.*?)["']?\)/ ); if (match && match[1]) { return { src: normalizeMediaURL(match[1]), alt: "", original: match[1] }; } } const possibleTextURL = cleanText(field.textContent); if ( possibleTextURL.includes("/images/") || possibleTextURL.startsWith("images/") ) { return { src: normalizeMediaURL(possibleTextURL), alt: "", original: possibleTextURL }; } return { src: "", alt: "", original: "" }; } function getLinkFromField(item) { const field = item.querySelector(CONFIG.linkSelector); if (field) { const anchor = field.matches("a[href]") ? field : field.querySelector("a[href]"); if (anchor) { const href = anchor.getAttribute("href"); if (isUsableURL(href)) { return normalizeLinkURL(href); } } const possibleURL = cleanText(field.textContent); if (isUsableURL(possibleURL)) { return normalizeLinkURL(possibleURL); } } const titleField = item.querySelector(CONFIG.titleSelector); const titleLink = titleField ? titleField.querySelector("a[href]") : null; if ( titleLink && isUsableURL(titleLink.getAttribute("href")) ) { return normalizeLinkURL( titleLink.getAttribute("href") ); } const coverField = item.querySelector(CONFIG.coverSelector); const coverLink = coverField ? coverField.querySelector("a[href]") : null; if ( coverLink && isUsableURL(coverLink.getAttribute("href")) ) { return normalizeLinkURL( coverLink.getAttribute("href") ); } return ""; } /* ===================================================== ΝΑΙ / ΟΧΙ ===================================================== */ function isYesValue(value) { const normalizedValue = normalizeName(value); return [ "ναι", "yes", "true", "1", "on", "checked" ].includes(normalizedValue); } /* ===================================================== ΑΝΑΓΝΩΣΗ ΗΜΕΡΟΜΗΝΙΑΣ ===================================================== */ function parsePublicationDate(value) { const rawDate = cleanText(value); if (!rawDate) { return { timestamp: 0, valid: false, original: "" }; } /* * YYYY-MM-DD * YYYY/MM/DD * YYYY-MM-DD HH:mm:ss */ let match = rawDate.match( /^(\d{4})[-/.](\d{1,2})[-/.](\d{1,2})(?:\s+.*)?$/ ); if (match) { const year = Number(match[1]); const month = Number(match[2]); const day = Number(match[3]); const date = new Date(year, month - 1, day); if ( date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ) { return { timestamp: date.getTime(), valid: true, original: rawDate }; } } /* * DD/MM/YYYY * DD-MM-YYYY * DD.MM.YYYY */ match = rawDate.match( /^(\d{1,2})[-/.](\d{1,2})[-/.](\d{4})(?:\s+.*)?$/ ); if (match) { const day = Number(match[1]); const month = Number(match[2]); const year = Number(match[3]); const date = new Date(year, month - 1, day); if ( date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ) { return { timestamp: date.getTime(), valid: true, original: rawDate }; } } /* * Τελευταίο fallback για ISO ή ημερομηνίες * που αναγνωρίζει με ασφάλεια ο browser. */ const parsedTimestamp = Date.parse(rawDate); if (!Number.isNaN(parsedTimestamp)) { return { timestamp: parsedTimestamp, valid: true, original: rawDate }; } return { timestamp: 0, valid: false, original: rawDate }; } function sortBooksByDate(books) { return [...books].sort(function (bookA, bookB) { const dateDifference = bookB.publicationTimestamp - bookA.publicationTimestamp; if (dateDifference !== 0) { return dateDifference; } /* * Σε ίδια ή κενή ημερομηνία διατηρείται * η αρχική σειρά της συλλογής. */ return bookA.index - bookB.index; }); } /* ===================================================== ΣΥΝΤΕΛΕΣΤΕΣ ===================================================== */ function buildCredits(writer, artist) { const cleanWriter = cleanText(writer); const cleanArtist = cleanText(artist); if ( cleanWriter && cleanArtist && normalizeName(cleanWriter) === normalizeName(cleanArtist) ) { return [ { label: "Σενάριο & σχέδιο", value: cleanWriter } ]; } const credits = []; if (cleanWriter) { credits.push({ label: "Σενάριο", value: cleanWriter }); } if (cleanArtist) { credits.push({ label: "Σχέδιο", value: cleanArtist }); } return credits; } function createCreditsHTML(credits, extraClass) { if (!credits || !credits.length) { return ""; } const classes = [ "ct-press-book-card__credits", extraClass || "" ] .filter(Boolean) .join(" "); return `
${credits .map(function (credit) { return `

${escapeHTML(credit.label)} ${escapeHTML(credit.value)}

`; }) .join("")}
`; } /* ===================================================== ΕΝΤΟΠΙΣΜΟΣ ΤΩΝ DYNAMIC CONTENT ITEMS ===================================================== */ function findCommonItemContainer(titleField, source) { let currentElement = titleField.parentElement; let bestMatch = null; while ( currentElement && currentElement !== source ) { const titleCount = currentElement.querySelectorAll( CONFIG.titleSelector ).length; const coverCount = currentElement.querySelectorAll( CONFIG.coverSelector ).length; if ( titleCount === 1 && coverCount === 1 ) { bestMatch = currentElement; } if ( titleCount > 1 || coverCount > 1 ) { break; } currentElement = currentElement.parentElement; } return bestMatch; } function findSourceItems(source) { const titleFields = Array.from( source.querySelectorAll(CONFIG.titleSelector) ); const items = []; const usedItems = new Set(); titleFields.forEach(function (titleField) { const item = findCommonItemContainer( titleField, source ); if ( item && !usedItems.has(item) ) { usedItems.add(item); items.push(item); } }); if (!items.length) { const probableItems = source.querySelectorAll( ".sppb-dynamic-content-item, " + ".sppb-addon-dynamic-content-item, " + "[class*='dynamic-content-item'], " + ".sppb-row > [class*='sppb-col']" ); probableItems.forEach(function (item) { if ( item.querySelector(CONFIG.coverSelector) && item.querySelector(CONFIG.titleSelector) && !usedItems.has(item) ) { usedItems.add(item); items.push(item); } }); } return items; } /* ===================================================== ΔΗΜΙΟΥΡΓΙΑ ΤΩΝ ΔΕΔΟΜΕΝΩΝ ΒΙΒΛΙΩΝ ===================================================== */ function extractBooks(source) { const sourceItems = findSourceItems(source); const books = []; const usedBooks = new Set(); debugLog( "Βρέθηκαν", sourceItems.length, "πιθανά Dynamic Content items." ); sourceItems.forEach(function (item, index) { const title = getTextFromField( item, CONFIG.titleSelector ); const writer = getTextFromField( item, CONFIG.writerSelector ); const artist = getTextFromField( item, CONFIG.artistSelector ); const rawDate = getTextFromField( item, CONFIG.dateSelector ); const rawSelected = getTextFromField( item, CONFIG.selectedSelector ); const rawFocus = getTextFromField( item, CONFIG.focusSelector ); const parsedDate = parsePublicationDate(rawDate); const image = getImageFromField(item); const link = getLinkFromField(item); if (!title) { return; } if (!image.src) { debugLog( "Παραλείφθηκε βιβλίο χωρίς εικόνα:", title ); return; } const uniqueKey = [ normalizeName(title), image.src, link ].join("|"); if (usedBooks.has(uniqueKey)) { return; } usedBooks.add(uniqueKey); books.push({ index: index, title: title, writer: writer, artist: artist, credits: buildCredits(writer, artist), image: image.src, imageAlt: image.alt || title, link: link, publicationDate: rawDate, publicationTimestamp: parsedDate.timestamp, hasValidPublicationDate: parsedDate.valid, isSelected: isYesValue(rawSelected), isFocus: isYesValue(rawFocus) }); debugLog( title, "| Ημερομηνία:", rawDate || "κενή", "| Επιλεγμένη:", isYesValue(rawSelected), "| Επίκεντρο:", isYesValue(rawFocus), "| Εικόνα:", image.src ); }); return sortBooksByDate(books); } /* ===================================================== ΚΑΡΤΑ ΒΙΒΛΙΟΥ ===================================================== */ function createBookCard(book, options) { const settings = Object.assign( { className: "", imageLoading: "lazy" }, options || {} ); const cardClasses = [ "ct-press-book-card", settings.className ] .filter(Boolean) .join(" "); const coverHTML = `
${escapeAttribute(book.imageAlt)}
`; const bodyHTML = `

${escapeHTML(book.title)}

${createCreditsHTML(book.credits)} ${ book.link ? ` Περισσότερα ` : "" }
`; if (book.link) { return `
${coverHTML} ${bodyHTML}
`; } return `
${coverHTML} ${bodyHTML}
`; } /* ===================================================== ΕΠΙΚΕΝΤΡΟ ===================================================== */ function renderFeaturedBook(book) { const target = document.querySelector( CONFIG.featuredTarget ); if (!target || !book) { return; } const imageHTML = ` ${escapeAttribute(book.imageAlt)} `; const buttonHTML = book.link ? ` Δείτε την έκδοση ` : ""; target.innerHTML = ` `; } /* ===================================================== ΕΠΙΛΕΓΜΕΝΕΣ ΕΚΔΟΣΕΙΣ ===================================================== */ function updateSelectedCount(numberOfBooks) { const countTarget = document.querySelector( CONFIG.countTarget ); const countLabelTarget = document.querySelector( CONFIG.countLabelTarget ); if (countTarget) { countTarget.textContent = String(numberOfBooks); } if (countLabelTarget) { countLabelTarget.textContent = numberOfBooks === 1 ? "επιλεγμένο βιβλίο" : "επιλεγμένα βιβλία"; } } function renderSelectedBooks(books) { const target = document.querySelector( CONFIG.libraryTarget ); updateSelectedCount(books.length); if (!target) { return; } if (!books.length) { target.innerHTML = `

Δεν έχουν οριστεί επιλεγμένες εκδόσεις.

`; return; } target.innerHTML = books .map(function (book) { return createBookCard(book); }) .join(""); } /* ===================================================== ΝΕΕΣ ΚΥΚΛΟΦΟΡΙΕΣ ===================================================== */ function renderLatestBooks(books) { const target = document.querySelector( CONFIG.latestTarget ); if (!target) { return; } const booksWithValidDate = books.filter( function (book) { return book.hasValidPublicationDate; } ); /* * Κανονικά χρησιμοποιούνται μόνο βιβλία με * έγκυρη ημερομηνία. Αν δεν υπάρχει καμία, * γίνεται fallback στη σειρά της συλλογής. */ const sourceBooks = booksWithValidDate.length ? booksWithValidDate : books; const latestBooks = sourceBooks.slice( 0, CONFIG.latestLimit ); if (!latestBooks.length) { target.innerHTML = `

Δεν βρέθηκαν νέες κυκλοφορίες.

`; return; } target.innerHTML = latestBooks .map(function (book) { return createBookCard(book, { className: "ct-press-book-card--latest" }); }) .join(""); } /* ===================================================== ΣΦΑΛΜΑΤΑ / ΚΡΥΦΗ ΠΗΓΗ ===================================================== */ function showLoadingError() { const targets = [ document.querySelector(CONFIG.featuredTarget), document.querySelector(CONFIG.libraryTarget), document.querySelector(CONFIG.latestTarget) ]; targets.forEach(function (target) { if (!target) { return; } target.innerHTML = `

Οι εκδόσεις δεν μπόρεσαν να φορτωθούν.

`; }); updateSelectedCount(0); } function hideSourceAfterReading(source) { if (!source) { return; } const sourceRow = source.closest( ".ct-press-source-row" ); const hiddenElement = sourceRow || source; hiddenElement.setAttribute( "aria-hidden", "true" ); hiddenElement.style.setProperty( "display", "none", "important" ); } /* ===================================================== ΕΚΚΙΝΗΣΗ ===================================================== */ function pageHasTargets() { return Boolean( document.querySelector(CONFIG.featuredTarget) || document.querySelector(CONFIG.libraryTarget) || document.querySelector(CONFIG.latestTarget) ); } function scheduleRetry(reason) { if (initialized || retryTimer) { return; } retryCount += 1; debugLog( reason, "Προσπάθεια", retryCount, "από", CONFIG.maximumRetries ); if (retryCount > CONFIG.maximumRetries) { showLoadingError(); console.warn( "Chaniartoon Press: δεν βρέθηκαν έγκυρα δεδομένα." ); if (observer) { observer.disconnect(); } return; } retryTimer = window.setTimeout( function () { retryTimer = null; initializePressPage(); }, CONFIG.retryDelay ); } function initializePressPage() { if (initialized) { return; } if (!pageHasTargets()) { return; } const source = document.querySelector( CONFIG.sourceSelector ); if (!source) { scheduleRetry( "Δεν βρέθηκε η πηγή Dynamic Content." ); return; } const sortedBooks = extractBooks(source); if (!sortedBooks.length) { scheduleRetry( "Η πηγή υπάρχει αλλά δεν έχει φορτώσει βιβλία." ); return; } initialized = true; /* * ΕΠΙΚΕΝΤΡΟ: * Παίρνει το νεότερο βιβλίο με Επίκεντρο = Ναι. * Αν δεν υπάρχει, παίρνει το νεότερο βιβλίο συνολικά. */ const focusBook = sortedBooks.find(function (book) { return book.isFocus; }) || sortedBooks[0]; /* * ΕΠΙΛΕΓΜΕΝΕΣ: * Μόνο όσα έχουν Επιλεγμένη έκδοση = Ναι. */ const selectedBooks = sortedBooks.filter( function (book) { return book.isSelected; } ); renderFeaturedBook(focusBook); renderSelectedBooks(selectedBooks); renderLatestBooks(sortedBooks); repairRenderedURLs(); scheduleRenderedURLRepairs(); hideSourceAfterReading(source); document.documentElement.classList.add( "ct-press-data-ready" ); if (observer) { observer.disconnect(); } if (retryTimer) { window.clearTimeout(retryTimer); retryTimer = null; } debugLog( "Η σελίδα δημιουργήθηκε επιτυχώς." ); debugLog( "Επίκεντρο:", focusBook ? focusBook.title : "κανένα" ); debugLog( "Επιλεγμένα βιβλία:", selectedBooks.length ); debugLog( "Σειρά βάσει ημερομηνίας:", sortedBooks.map(function (book) { return { title: book.title, date: book.publicationDate, selected: book.isSelected, focus: book.isFocus }; }) ); } function startPressPageScript() { if (!pageHasTargets()) { return; } initializePressPage(); observer = new MutationObserver(function () { if (initialized) { observer.disconnect(); return; } initializePressPage(); }); observer.observe( document.documentElement, { childList: true, subtree: true } ); window.setTimeout(function () { if (observer) { observer.disconnect(); } if (!initialized) { initializePressPage(); } }, 12000); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", startPressPageScript, { once: true } ); } else { startPressPageScript(); } })(); (function () { "use strict"; const root = document.querySelector(".ct-comix-page"); if ( !root || root.dataset.comixInitialized === "true" ) { return; } root.dataset.comixInitialized = "true"; root.classList.add("ct-comix-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; /* ----------------------------------------------------- REVEAL ON SCROLL ----------------------------------------------------- */ const revealItems = Array.from( root.querySelectorAll("[data-comix-reveal]") ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute("data-comix-delay") || 0 ); item.style.setProperty( "--ct-comix-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const revealObserver = new IntersectionObserver( function (entries, observer) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" } ); revealItems.forEach(function (item) { revealObserver.observe(item); }); } /* ----------------------------------------------------- SMOOTH INTERNAL LINKS ----------------------------------------------------- */ root.querySelectorAll('a[href^="#"]').forEach( function (link) { link.addEventListener("click", function (event) { const href = link.getAttribute("href"); if (!href || href === "#") { return; } let target = null; try { target = document.querySelector(href); } catch (error) { return; } if (!target) { return; } event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); }); } ); /* ----------------------------------------------------- LIGHT HERO PARALLAX ----------------------------------------------------- */ const parallaxElement = root.querySelector( "[data-comix-parallax]" ); if ( parallaxElement && !reduceMotion ) { let ticking = false; function updateParallax() { const hero = root.querySelector(".ct-comix-hero"); if (!hero) { ticking = false; return; } const heroRect = hero.getBoundingClientRect(); const viewportHeight = window.innerHeight; if ( heroRect.bottom < 0 || heroRect.top > viewportHeight ) { ticking = false; return; } const progress = Math.max( -1, Math.min( 1, -heroRect.top / Math.max(heroRect.height, 1) ) ); parallaxElement.style.setProperty( "--ct-comix-parallax-y", Math.round(progress * 24) + "px" ); ticking = false; } function requestParallaxUpdate() { if (ticking) { return; } ticking = true; window.requestAnimationFrame(updateParallax); } window.addEventListener( "scroll", requestParallaxUpdate, { passive: true } ); window.addEventListener( "resize", requestParallaxUpdate ); requestParallaxUpdate(); } })(); (function () { "use strict"; const root = document.querySelector(".ct-vol-page"); if ( !root || root.dataset.volInitialized === "true" ) { return; } root.dataset.volInitialized = "true"; root.classList.add("ct-vol-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; /* ----------------------------------------------------- REVEAL ON SCROLL ----------------------------------------------------- */ const revealItems = Array.from( root.querySelectorAll("[data-vol-reveal]") ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute("data-vol-delay") || 0 ); item.style.setProperty( "--ct-vol-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const revealObserver = new IntersectionObserver( function (entries, observer) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" } ); revealItems.forEach(function (item) { revealObserver.observe(item); }); } /* ----------------------------------------------------- SMOOTH INTERNAL LINKS ----------------------------------------------------- */ document.querySelectorAll( '.ct-vol-page a[href^="#"]' ).forEach(function (link) { link.addEventListener("click", function (event) { const href = link.getAttribute("href"); if (!href || href === "#") { return; } let target = null; try { target = document.querySelector(href); } catch (error) { return; } if (!target) { return; } event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); }); }); /* ----------------------------------------------------- LIGHT HERO PARALLAX ----------------------------------------------------- */ const parallaxElement = root.querySelector( "[data-vol-parallax]" ); if ( parallaxElement && !reduceMotion ) { let ticking = false; function updateParallax() { const hero = root.querySelector(".ct-vol-hero"); if (!hero) { ticking = false; return; } const heroRect = hero.getBoundingClientRect(); const viewportHeight = window.innerHeight; if ( heroRect.bottom < 0 || heroRect.top > viewportHeight ) { ticking = false; return; } const progress = Math.max( -1, Math.min( 1, -heroRect.top / Math.max(heroRect.height, 1) ) ); parallaxElement.style.setProperty( "--ct-vol-parallax-y", Math.round(progress * 22) + "px" ); ticking = false; } function requestParallaxUpdate() { if (ticking) { return; } ticking = true; window.requestAnimationFrame(updateParallax); } window.addEventListener( "scroll", requestParallaxUpdate, { passive: true } ); window.addEventListener( "resize", requestParallaxUpdate ); requestParallaxUpdate(); } })(); (function () { "use strict"; const root = document.querySelector(".ct-sponsors-page"); if (!root || root.dataset.sponsorsInitialized === "true") { return; } root.dataset.sponsorsInitialized = "true"; root.classList.add("ct-sponsors-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; const revealItems = Array.from( root.querySelectorAll("[data-sponsors-reveal]") ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute("data-sponsors-delay") || 0 ); item.style.setProperty( "--ct-sponsors-delay", delay + "ms" ); }); if (reduceMotion || !("IntersectionObserver" in window)) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const revealObserver = new IntersectionObserver( function (entries, observer) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.1, rootMargin: "0px 0px -7% 0px" } ); revealItems.forEach(function (item) { revealObserver.observe(item); }); } root.querySelectorAll('a[href^="#"]').forEach( function (link) { link.addEventListener("click", function (event) { const href = link.getAttribute("href"); if (!href || href === "#") { return; } let target = null; try { target = document.querySelector(href); } catch (error) { return; } if (!target) { return; } event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); }); } ); const parallaxElement = root.querySelector( "[data-sponsors-parallax]" ); if (parallaxElement && !reduceMotion) { let ticking = false; function updateParallax() { const hero = root.querySelector(".ct-sponsors-hero"); if (!hero) { ticking = false; return; } const heroRect = hero.getBoundingClientRect(); const viewportHeight = window.innerHeight; if ( heroRect.bottom < 0 || heroRect.top > viewportHeight ) { ticking = false; return; } const progress = Math.max( -1, Math.min( 1, -heroRect.top / Math.max(heroRect.height, 1) ) ); parallaxElement.style.setProperty( "--ct-sponsors-parallax-y", Math.round(progress * 18) + "px" ); ticking = false; } function requestParallaxUpdate() { if (ticking) { return; } ticking = true; window.requestAnimationFrame(updateParallax); } window.addEventListener( "scroll", requestParallaxUpdate, { passive: true } ); window.addEventListener( "resize", requestParallaxUpdate ); requestParallaxUpdate(); } })(); (function () { "use strict"; function initializePartnersPage(root) { if ( !root || root.dataset.partnersInitialized === "true" ) { return; } root.dataset.partnersInitialized = "true"; root.classList.add("ct-partners-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; /* ------------------------------------------------- REVEAL ------------------------------------------------- */ const revealItems = Array.from( root.querySelectorAll("[data-partners-reveal]") ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute("data-partners-delay") || 0 ); item.style.setProperty( "--ct-partners-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const observer = new IntersectionObserver( function (entries, revealObserver) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add("is-visible"); revealObserver.unobserve(entry.target); }); }, { threshold: 0.1, rootMargin: "0px 0px -7% 0px" } ); revealItems.forEach(function (item) { observer.observe(item); }); } /* ------------------------------------------------- SMOOTH INTERNAL LINKS ------------------------------------------------- */ root.querySelectorAll('a[href^="#"]').forEach( function (link) { link.addEventListener( "click", function (event) { const href = link.getAttribute("href"); if (!href || href === "#") { return; } let target = null; try { target = document.querySelector(href); } catch (error) { return; } if (!target) { return; } event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); } ); } ); /* ------------------------------------------------- PARTNER FILTERS ------------------------------------------------- */ const filterButtons = Array.from( root.querySelectorAll("[data-partner-filter]") ); const partnerCards = Array.from( root.querySelectorAll("[data-partner-category]") ); const emptyMessage = root.querySelector( ".ct-partners-empty" ); function applyPartnerFilter(selectedFilter) { let visibleCount = 0; filterButtons.forEach(function (button) { const buttonFilter = button.getAttribute( "data-partner-filter" ); const isActive = buttonFilter === selectedFilter; button.classList.toggle( "is-active", isActive ); button.setAttribute( "aria-pressed", isActive ? "true" : "false" ); }); partnerCards.forEach(function (card) { /* * Υποστηρίζει και μία κατηγορία: * data-partner-category="culture" * * αλλά και πολλές: * data-partner-category="culture international" */ const categories = ( card.getAttribute( "data-partner-category" ) || "" ) .trim() .split(/\s+/) .filter(Boolean); const shouldShow = selectedFilter === "all" || categories.includes(selectedFilter); card.classList.toggle( "is-hidden", !shouldShow ); card.hidden = !shouldShow; if (shouldShow) { visibleCount += 1; } }); if (emptyMessage) { emptyMessage.hidden = visibleCount > 0; } } filterButtons.forEach(function (button) { button.addEventListener( "click", function (event) { event.preventDefault(); const selectedFilter = button.getAttribute( "data-partner-filter" ) || "all"; applyPartnerFilter(selectedFilter); } ); }); /* * Ορίζει σωστά και την αρχική κατάσταση. */ applyPartnerFilter("all"); /* ------------------------------------------------- LIGHT HERO PARALLAX ------------------------------------------------- */ const parallaxElement = root.querySelector( "[data-partners-parallax]" ); if (parallaxElement && !reduceMotion) { let ticking = false; function updateParallax() { const hero = root.querySelector( ".ct-partners-hero" ); if (!hero) { ticking = false; return; } const rect = hero.getBoundingClientRect(); if ( rect.bottom < 0 || rect.top > window.innerHeight ) { ticking = false; return; } const progress = Math.max( -1, Math.min( 1, -rect.top / Math.max(rect.height, 1) ) ); parallaxElement.style.setProperty( "--ct-partners-parallax-y", Math.round(progress * 18) + "px" ); ticking = false; } function requestUpdate() { if (ticking) { return; } ticking = true; window.requestAnimationFrame( updateParallax ); } window.addEventListener( "scroll", requestUpdate, { passive: true } ); window.addEventListener( "resize", requestUpdate ); requestUpdate(); } } function initializeAllPartnersPages() { document .querySelectorAll(".ct-partners-page") .forEach(initializePartnersPage); } /* * Λειτουργεί είτε το custom.js φορτώνεται πριν είτε μετά * από το HTML του SP Page Builder. */ if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeAllPartnersPages, { once: true } ); } else { initializeAllPartnersPages(); } /* * Επιπλέον ασφάλεια για builder/cache/lazy rendering. */ window.addEventListener( "load", initializeAllPartnersPages, { once: true } ); })(); (function () { "use strict"; function initializeGallery(root) { if ( !root || root.dataset.galleryInitialized === "true" ) { return; } root.dataset.galleryInitialized = "true"; root.classList.add("ct-gallery-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; /* ------------------------------------------------- REVEAL ON SCROLL ------------------------------------------------- */ const revealItems = Array.from( root.querySelectorAll("[data-gallery-reveal]") ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute("data-gallery-delay") || 0 ); item.style.setProperty( "--ct-gallery-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const revealObserver = new IntersectionObserver( function (entries, observer) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.08, rootMargin: "0px 0px -6% 0px" } ); revealItems.forEach(function (item) { revealObserver.observe(item); }); } /* ------------------------------------------------- SMOOTH INTERNAL LINKS ------------------------------------------------- */ root.querySelectorAll('a[href^="#"]').forEach( function (link) { link.addEventListener( "click", function (event) { const href = link.getAttribute("href"); if (!href || href === "#") { return; } let target = null; try { target = document.querySelector(href); } catch (error) { return; } if (!target) { return; } event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); } ); } ); /* ------------------------------------------------- LIGHTBOX ------------------------------------------------- */ const lightbox = root.querySelector( ".ct-gallery-lightbox" ); if (!lightbox) { return; } const openButtons = Array.from( root.querySelectorAll(".ct-gallery-open") ); const lightboxImage = lightbox.querySelector( ".ct-gallery-lightbox__image" ); const lightboxCaption = lightbox.querySelector( ".ct-gallery-lightbox__caption" ); const lightboxCounter = lightbox.querySelector( ".ct-gallery-lightbox__counter" ); const previousButton = lightbox.querySelector( "[data-gallery-prev]" ); const nextButton = lightbox.querySelector( "[data-gallery-next]" ); const closeButtons = Array.from( lightbox.querySelectorAll("[data-gallery-close]") ); let currentIndex = 0; let previouslyFocusedElement = null; let touchStartX = 0; let touchEndX = 0; function getItemData(index) { const button = openButtons[index]; const thumbnail = button ? button.querySelector("img") : null; const fullImage = button?.getAttribute("data-gallery-full") || thumbnail?.currentSrc || thumbnail?.src || ""; const caption = button?.getAttribute( "data-gallery-caption" ) || thumbnail?.alt || ""; const altText = thumbnail?.alt || caption || "Φωτογραφία Chaniartoon"; return { fullImage: fullImage, caption: caption, altText: altText }; } function preloadAdjacentImages() { if (openButtons.length < 2) { return; } const adjacentIndexes = [ (currentIndex - 1 + openButtons.length) % openButtons.length, (currentIndex + 1) % openButtons.length ]; adjacentIndexes.forEach(function (index) { const itemData = getItemData(index); if (!itemData.fullImage) { return; } const preloadImage = new Image(); preloadImage.src = itemData.fullImage; }); } function showImage(index) { if (!openButtons.length) { return; } currentIndex = (index + openButtons.length) % openButtons.length; const itemData = getItemData(currentIndex); lightboxImage.classList.add("is-loading"); const loader = new Image(); loader.onload = function () { lightboxImage.src = itemData.fullImage; lightboxImage.alt = itemData.altText; window.requestAnimationFrame( function () { lightboxImage.classList.remove( "is-loading" ); } ); }; loader.onerror = function () { lightboxImage.src = itemData.fullImage; lightboxImage.alt = itemData.altText; lightboxImage.classList.remove( "is-loading" ); }; loader.src = itemData.fullImage; lightboxCaption.textContent = itemData.caption; lightboxCounter.textContent = String(currentIndex + 1).padStart(2, "0") + " / " + String(openButtons.length).padStart(2, "0"); preloadAdjacentImages(); } function openLightbox(index, triggerButton) { previouslyFocusedElement = triggerButton || document.activeElement; lightbox.hidden = false; document.body.classList.add( "ct-gallery-lightbox-open" ); showImage(index); window.requestAnimationFrame(function () { const closeButton = lightbox.querySelector( ".ct-gallery-lightbox__close" ); closeButton?.focus(); }); } function closeLightbox() { lightbox.hidden = true; document.body.classList.remove( "ct-gallery-lightbox-open" ); lightboxImage.src = ""; lightboxImage.alt = ""; if ( previouslyFocusedElement && typeof previouslyFocusedElement.focus === "function" ) { previouslyFocusedElement.focus(); } } function showPrevious() { showImage(currentIndex - 1); } function showNext() { showImage(currentIndex + 1); } openButtons.forEach(function (button, index) { button.addEventListener( "click", function () { openLightbox(index, button); } ); }); closeButtons.forEach(function (button) { button.addEventListener( "click", closeLightbox ); }); previousButton?.addEventListener( "click", showPrevious ); nextButton?.addEventListener( "click", showNext ); document.addEventListener( "keydown", function (event) { if (lightbox.hidden) { return; } if (event.key === "Escape") { event.preventDefault(); closeLightbox(); return; } if (event.key === "ArrowLeft") { event.preventDefault(); showPrevious(); return; } if (event.key === "ArrowRight") { event.preventDefault(); showNext(); return; } if (event.key === "Tab") { const focusableElements = Array.from( lightbox.querySelectorAll( 'button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])' ) ); if (!focusableElements.length) { return; } const firstElement = focusableElements[0]; const lastElement = focusableElements[ focusableElements.length - 1 ]; if ( event.shiftKey && document.activeElement === firstElement ) { event.preventDefault(); lastElement.focus(); } else if ( !event.shiftKey && document.activeElement === lastElement ) { event.preventDefault(); firstElement.focus(); } } } ); lightbox.addEventListener( "touchstart", function (event) { touchStartX = event.changedTouches[0]?.screenX || 0; }, { passive: true } ); lightbox.addEventListener( "touchend", function (event) { touchEndX = event.changedTouches[0]?.screenX || 0; const swipeDistance = touchEndX - touchStartX; if (Math.abs(swipeDistance) < 55) { return; } if (swipeDistance > 0) { showPrevious(); } else { showNext(); } }, { passive: true } ); } function initializeAllGalleries() { document .querySelectorAll(".ct-gallery-page") .forEach(initializeGallery); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeAllGalleries, { once: true } ); } else { initializeAllGalleries(); } window.addEventListener( "load", initializeAllGalleries, { once: true } ); })(); (function () { "use strict"; function initializeContactRoot(root) { if ( !root || root.dataset.contactInitialized === "true" ) { return; } root.dataset.contactInitialized = "true"; root.classList.add("ct-contact-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; /* ------------------------------------------------- REVEAL ON SCROLL ------------------------------------------------- */ const revealItems = Array.from( root.querySelectorAll("[data-contact-reveal]") ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute("data-contact-delay") || 0 ); item.style.setProperty( "--ct-contact-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const revealObserver = new IntersectionObserver( function (entries, observer) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.09, rootMargin: "0px 0px -7% 0px" } ); revealItems.forEach(function (item) { revealObserver.observe(item); }); } /* ------------------------------------------------- LIGHT HERO PARALLAX ------------------------------------------------- */ const parallaxElement = root.querySelector( "[data-contact-parallax]" ); if (parallaxElement && !reduceMotion) { let ticking = false; function updateParallax() { const hero = root.querySelector(".ct-contact-hero"); if (!hero) { ticking = false; return; } const heroRect = hero.getBoundingClientRect(); const viewportHeight = window.innerHeight; if ( heroRect.bottom < 0 || heroRect.top > viewportHeight ) { ticking = false; return; } const progress = Math.max( -1, Math.min( 1, -heroRect.top / Math.max(heroRect.height, 1) ) ); parallaxElement.style.setProperty( "--ct-contact-parallax-y", Math.round(progress * 20) + "px" ); ticking = false; } function requestUpdate() { if (ticking) { return; } ticking = true; window.requestAnimationFrame(updateParallax); } window.addEventListener( "scroll", requestUpdate, { passive: true } ); window.addEventListener( "resize", requestUpdate ); requestUpdate(); } } function initializeAllContactSections() { document .querySelectorAll( ".ct-contact-page, .ct-contact-after" ) .forEach(initializeContactRoot); /* ------------------------------------------------- SMOOTH INTERNAL LINKS Uses document scope because the form anchor and lower page section are separate SPPB addons. ------------------------------------------------- */ document .querySelectorAll( '.ct-contact-page a[href^="#"], .ct-contact-after a[href^="#"]' ) .forEach(function (link) { if ( link.dataset.contactSmoothInitialized === "true" ) { return; } link.dataset.contactSmoothInitialized = "true"; link.addEventListener( "click", function (event) { const href = link.getAttribute("href"); if (!href || href === "#") { return; } let target = null; try { target = document.querySelector(href); } catch (error) { return; } if (!target) { return; } const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; event.preventDefault(); target.scrollIntoView({ behavior: reduceMotion ? "auto" : "smooth", block: "start" }); } ); }); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeAllContactSections, { once: true } ); } else { initializeAllContactSections(); } window.addEventListener( "load", initializeAllContactSections, { once: true } ); })(); document.addEventListener('DOMContentLoaded', function () { const section = document.querySelector('.festival-newsletter'); const form = document.getElementById( 'chaniartoon-newsletter-form' ); const successPanel = document.getElementById( 'chaniartoon-newsletter-success' ); const submitButton = form ? form.querySelector( '.chaniartoon-newsletter-form__submit' ) : null; const emailInput = document.getElementById( 'newsletter-email' ); const message = document.getElementById( 'chaniartoon-newsletter-message' ); const continueLink = document.getElementById( 'newsletter-continue-link' ); if ( !section || !form || !successPanel || !submitButton || !emailInput || !message ) { return; } function clearMessage() { message.textContent = ''; message.classList.remove( 'is-success', 'is-error' ); } function updateEmailState() { const hasValue = emailInput.value.trim() !== ''; const isValid = hasValue && emailInput.validity.valid; emailInput.classList.toggle( 'is-valid', isValid ); emailInput.classList.toggle( 'is-invalid', hasValue && !isValid ); } function showSuccess() { section.classList.add('is-success'); form.setAttribute('aria-hidden', 'true'); successPanel.setAttribute('aria-hidden', 'false'); window.setTimeout(function () { successPanel.focus({ preventScroll: true }); }, 450); } emailInput.addEventListener( 'input', function () { clearMessage(); updateEmailState(); } ); emailInput.addEventListener( 'blur', updateEmailState ); form.addEventListener( 'focusin', function () { section.classList.add('has-form-focus'); } ); form.addEventListener( 'focusout', function () { window.setTimeout(function () { if (!form.contains(document.activeElement)) { section.classList.remove( 'has-form-focus' ); } }, 0); } ); form.addEventListener( 'submit', async function (event) { event.preventDefault(); clearMessage(); updateEmailState(); if (!form.checkValidity()) { form.reportValidity(); message.textContent = 'Συμπλήρωσε ένα έγκυρο email.'; message.classList.add('is-error'); return; } submitButton.disabled = true; submitButton.classList.add( 'is-loading' ); form.setAttribute( 'aria-busy', 'true' ); try { const response = await fetch( '/newsletter-subscribe.php', { method: 'POST', body: new FormData(form), headers: { 'Accept': 'application/json' } } ); let data; try { data = await response.json(); } catch (jsonError) { throw new Error( 'Η υπηρεσία επέστρεψε μη αναμενόμενη απάντηση.' ); } if ( !response.ok || !data.success ) { throw new Error( data.message || 'Δεν ολοκληρώθηκε η εγγραφή.' ); } form.reset(); updateEmailState(); window.setTimeout( showSuccess, 350 ); } catch (error) { message.textContent = error.message || 'Παρουσιάστηκε ένα πρόβλημα. Δοκίμασε ξανά.'; message.classList.add( 'is-error' ); } finally { submitButton.disabled = false; submitButton.classList.remove( 'is-loading' ); form.removeAttribute( 'aria-busy' ); } } ); if (continueLink) { continueLink.addEventListener( 'click', function (event) { event.preventDefault(); const nextSection = section.nextElementSibling; if (nextSection) { nextSection.scrollIntoView({ behavior: 'smooth', block: 'start' }); return; } window.scrollBy({ top: window.innerHeight * 0.7, behavior: 'smooth' }); } ); } }); (function () { "use strict"; function initializeProgramme(root) { if ( !root || root.dataset.programInitialized === "true" ) { return; } root.dataset.programInitialized = "true"; root.classList.add("ct-program-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; const events = Array.from( root.querySelectorAll("[data-program-event]") ); const days = Array.from( root.querySelectorAll("[data-program-day]") ); const periodButtons = Array.from( root.querySelectorAll("[data-program-period]") ); const categoryButtons = Array.from( root.querySelectorAll("[data-program-category]") ); const audienceButtons = Array.from( root.querySelectorAll("[data-program-audience]") ); const resetButtons = Array.from( root.querySelectorAll("[data-program-reset]") ); const visibleCounter = root.querySelector( "[data-program-visible-count]" ); const noResults = root.querySelector( "[data-program-no-results]" ); const categoryOptionButtons = categoryButtons.filter( function (button) { return ( String( button.dataset.programCategory || "" ) !== "all" ); } ); const categoryAllButton = categoryButtons.find( function (button) { return ( String( button.dataset.programCategory || "" ) === "all" ); } ) || null; const activeCategories = new Set(); let activePeriod = "all"; let activeAudience = "all"; function normaliseInternalProgrammeText(value) { return String(value || "") .toLocaleLowerCase("el-GR") .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .replace(/\s+/g, " ") .trim(); } function isInternalConferenceProgrammeRow(eventElement) { if (!eventElement) { return false; } const titleElement = eventElement.querySelector( "[data-program-title], " + ".ct-program-event__title, " + ".ct-program-card__title, " + "h3" ); const titleText = normaliseInternalProgrammeText( titleElement ? titleElement.textContent : eventElement.textContent ); return ( titleText.includes("διαλειμμα") || titleText.includes("break") ); } function isExhibitionButton(button) { if (!button) { return false; } const value = normaliseInternalProgrammeText( String( button.dataset.programCategory || "" ) + " " + String(button.textContent || "") ); return ( value.includes("εκθεσ") || value.includes("exhibition") ); } function setSingleButtonState( buttons, activeValue, datasetName ) { buttons.forEach(function (button) { const value = String( button.dataset[datasetName] || "all" ); const isActive = value === activeValue; button.classList.toggle( "is-active", isActive ); button.setAttribute( "aria-pressed", isActive ? "true" : "false" ); }); } function setCategoryButtonState( button, isActive ) { if (!button) { return; } button.classList.toggle( "is-active", isActive ); button.setAttribute( "aria-pressed", isActive ? "true" : "false" ); } function syncCategoryButtons() { categoryOptionButtons.forEach(function (button) { const value = String( button.dataset.programCategory || "" ); setCategoryButtonState( button, activeCategories.has(value) ); }); if (categoryAllButton) { const everyCategoryIsActive = categoryOptionButtons.length > 0 && categoryOptionButtons.every( function (button) { const value = String( button.dataset.programCategory || "" ); return activeCategories.has(value); } ); setCategoryButtonState( categoryAllButton, everyCategoryIsActive ); } } function applyDefaultCategorySelection() { activeCategories.clear(); categoryOptionButtons.forEach(function (button) { if (isExhibitionButton(button)) { return; } const value = String( button.dataset.programCategory || "" ); if (value) { activeCategories.add(value); } }); syncCategoryButtons(); } function categorySelectionIsDefault() { const expected = categoryOptionButtons .filter(function (button) { return !isExhibitionButton(button); }) .map(function (button) { return String( button.dataset.programCategory || "" ); }) .filter(Boolean); if (activeCategories.size !== expected.length) { return false; } return expected.every(function (value) { return activeCategories.has(value); }); } function updateResetVisibility() { const shouldShow = activePeriod !== "all" || activeAudience !== "all" || !categorySelectionIsDefault(); resetButtons.forEach(function (button) { button.hidden = !shouldShow; }); } function applyFilters() { let visibleEvents = 0; events.forEach(function (event) { const eventPeriod = event.dataset.period || ""; const eventCategories = ( event.dataset.categories || "" ) .split(/\s+/) .filter(Boolean); const periodMatches = activePeriod === "all" || eventPeriod === activePeriod; /* * Multi-select για ΕΙΔΟΣ ΔΡΑΣΗΣ: * μια δράση εμφανίζεται όταν ανήκει σε τουλάχιστον * μία από τις ενεργές κατηγορίες. * * Το "Kids / Για παιδιά" είναι audience filter και * δεν προστίθεται στο activeCategories. */ const categoryMatches = categoryOptionButtons.length === 0 || eventCategories.some( function (category) { return activeCategories.has(category); } ); const audienceMatches = activeAudience === "all" || eventCategories.includes( activeAudience ); const isVisible = !isInternalConferenceProgrammeRow(event) && periodMatches && categoryMatches && audienceMatches; event.hidden = !isVisible; event.classList.toggle( "is-filtered-out", !isVisible ); if (isVisible) { visibleEvents++; } }); days.forEach(function (day) { const hasVisibleEvent = Array.from( day.querySelectorAll( "[data-program-event]" ) ).some(function (event) { return !event.hidden; }); day.hidden = !hasVisibleEvent; }); if (visibleCounter) { visibleCounter.textContent = String(visibleEvents); } if (noResults) { noResults.hidden = visibleEvents !== 0; } updateResetVisibility(); } periodButtons.forEach(function (button) { button.addEventListener( "click", function () { activePeriod = button.dataset.programPeriod || "all"; setSingleButtonState( periodButtons, activePeriod, "programPeriod" ); applyFilters(); } ); }); categoryButtons.forEach(function (button) { button.addEventListener( "click", function () { const value = String( button.dataset.programCategory || "" ); /* * "Όλες οι δράσεις" λειτουργεί επίσης ως toggle: * - αν δεν είναι όλες ενεργές, τις ενεργοποιεί όλες * - αν είναι ήδη όλες ενεργές, τις αποεπιλέγει όλες */ if (value === "all") { const everyCategoryIsActive = categoryOptionButtons.length > 0 && categoryOptionButtons.every( function (optionButton) { const optionValue = String( optionButton.dataset.programCategory || "" ); return activeCategories.has( optionValue ); } ); activeCategories.clear(); if (!everyCategoryIsActive) { categoryOptionButtons.forEach( function (optionButton) { const optionValue = String( optionButton.dataset.programCategory || "" ); if (optionValue) { activeCategories.add( optionValue ); } } ); } } else if (value) { if (activeCategories.has(value)) { activeCategories.delete(value); } else { activeCategories.add(value); } } syncCategoryButtons(); applyFilters(); } ); }); audienceButtons.forEach(function (button) { button.addEventListener( "click", function () { activeAudience = button.dataset.programAudience || "all"; setSingleButtonState( audienceButtons, activeAudience, "programAudience" ); applyFilters(); } ); }); resetButtons.forEach(function (button) { button.addEventListener( "click", function () { activePeriod = "all"; activeAudience = "all"; setSingleButtonState( periodButtons, activePeriod, "programPeriod" ); setSingleButtonState( audienceButtons, activeAudience, "programAudience" ); /* * Reset = η συμφωνημένη αρχική κατάσταση: * όλες οι κατηγορίες ενεργές εκτός από Εκθέσεις. */ applyDefaultCategorySelection(); applyFilters(); } ); }); /* * Αρχική κατάσταση: * - Όλη η περίοδος * - Όλο το κοινό * - Όλα τα είδη δράσης ενεργά ΕΚΤΟΣ από Εκθέσεις */ setSingleButtonState( periodButtons, activePeriod, "programPeriod" ); setSingleButtonState( audienceButtons, activeAudience, "programAudience" ); applyDefaultCategorySelection(); /* REVEAL */ const revealItems = Array.from( root.querySelectorAll( "[data-program-reveal]" ) ); revealItems.forEach(function (item) { const delay = Number( item.getAttribute( "data-program-delay" ) || 0 ); item.style.setProperty( "--ctp-reveal-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const observer = new IntersectionObserver( function (entries, currentObserver) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add( "is-visible" ); currentObserver.unobserve( entry.target ); }); }, { threshold: 0.07, rootMargin: "0px 0px -6% 0px" } ); revealItems.forEach(function (item) { observer.observe(item); }); } applyFilters(); } function initializeAllProgrammes() { document .querySelectorAll("[data-ct-program]") .forEach(initializeProgramme); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeAllProgrammes, { once: true } ); } else { initializeAllProgrammes(); } window.addEventListener( "load", initializeAllProgrammes, { once: true } ); })(); (function () { "use strict"; function initializeDayProgramme(root) { if ( !root || root.dataset.dayProgramInitialized === "true" ) { return; } root.dataset.dayProgramInitialized = "true"; root.classList.add("ct-day-js"); const reduceMotion = window.matchMedia( "(prefers-reduced-motion: reduce)" ).matches; /* CENTER ACTIVE DATE */ const strip = root.querySelector("[data-day-strip]"); const activeTab = strip ? strip.querySelector(".ct-day-tab.is-active") : null; if (strip && activeTab) { window.requestAnimationFrame(function () { const left = activeTab.offsetLeft - strip.clientWidth / 2 + activeTab.clientWidth / 2; strip.scrollTo({ left: Math.max(0, left), behavior: reduceMotion ? "auto" : "smooth" }); }); } /* DATE PICKER */ const pickerButton = root.querySelector( "[data-day-picker-submit]" ); const pickerInput = root.querySelector( "#ctDailyDate" ); function goToSelectedDate() { if (!pickerInput || !pickerInput.value) { return; } const options = window.Joomla && typeof window.Joomla.getOptions === "function" ? window.Joomla.getOptions( "ctDailyProgrammeUrl" ) : null; if (!options) { return; } window.location.href = String(options) + encodeURIComponent( pickerInput.value ); } if (pickerButton) { pickerButton.addEventListener( "click", goToSelectedDate ); } if (pickerInput) { pickerInput.addEventListener( "keydown", function (event) { if (event.key !== "Enter") { return; } event.preventDefault(); goToSelectedDate(); } ); } /* LIVE EVENT BADGE */ const allEventRows = Array.from( root.querySelectorAll("[data-day-event]") ); function normaliseDayEventText(value) { return String(value || "") .toLocaleLowerCase("el-GR") .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") .replace(/\s+/g, " ") .trim(); } function isInternalConferenceDayRow(eventRow) { if (!eventRow) { return false; } const titleElement = eventRow.querySelector( "[data-day-title], " + ".ct-day-event__title, " + ".ct-day-event__content h3, " + "h3" ); const titleText = normaliseDayEventText( titleElement ? titleElement.textContent : eventRow.textContent ); return ( titleText.includes("διαλειμμα") || titleText.includes("break") ); } allEventRows.forEach(function (eventRow) { if (!isInternalConferenceDayRow(eventRow)) { return; } eventRow.hidden = true; eventRow.classList.add( "is-conference-internal", "is-hidden" ); eventRow.setAttribute("aria-hidden", "true"); }); const eventRows = allEventRows.filter( function (eventRow) { return !isInternalConferenceDayRow(eventRow); } ); /* * Αν η daily σελίδα έχει counter στο markup, διόρθωσέ τον ώστε * το Διάλειμμα να μην προσμετράται ούτε στον αριθμό δράσεων. */ root.querySelectorAll( "[data-day-visible-count], " + "[data-day-event-count], " + "[data-day-actions-count]" ).forEach(function (counter) { counter.textContent = String(eventRows.length); }); function updateLiveEvents() { const now = Date.now(); eventRows.forEach(function (eventRow) { const startValue = eventRow.dataset.start || ""; const endValue = eventRow.dataset.end || ""; const startTime = Date.parse(startValue); const endTime = Date.parse(endValue); const badge = eventRow.querySelector( "[data-day-live-badge]" ); if (!badge) { return; } const isLive = Number.isFinite(startTime) && Number.isFinite(endTime) && now >= startTime && now <= endTime; badge.hidden = !isLive; eventRow.classList.toggle( "is-live", isLive ); }); } updateLiveEvents(); window.setInterval( updateLiveEvents, 60000 ); /* REVEAL */ const revealItems = Array.from( root.querySelectorAll( "[data-day-reveal]" ) ).filter(function (item) { const eventRow = item.matches("[data-day-event]") ? item : item.closest("[data-day-event]"); return !( eventRow && isInternalConferenceDayRow(eventRow) ); }); revealItems.forEach(function (item) { const delay = Number( item.getAttribute( "data-day-delay" ) || 0 ); item.style.setProperty( "--ctd-delay", delay + "ms" ); }); if ( reduceMotion || !("IntersectionObserver" in window) ) { revealItems.forEach(function (item) { item.classList.add("is-visible"); }); } else { const observer = new IntersectionObserver( function (entries, currentObserver) { entries.forEach(function (entry) { if (!entry.isIntersecting) { return; } entry.target.classList.add( "is-visible" ); currentObserver.unobserve( entry.target ); }); }, { threshold: 0.08, rootMargin: "0px 0px -6% 0px" } ); revealItems.forEach(function (item) { observer.observe(item); }); } } function initializeAllDayProgrammes() { document .querySelectorAll("[data-ct-day-program]") .forEach(initializeDayProgramme); } if (document.readyState === "loading") { document.addEventListener( "DOMContentLoaded", initializeAllDayProgrammes, { once: true } ); } else { initializeAllDayProgrammes(); } window.addEventListener( "load", initializeAllDayProgrammes, { once: true } ); })(); (function () { "use strict"; function init(root) { if (!root || root.dataset.exhibitionInitialized === "true") return; root.dataset.exhibitionInitialized = "true"; root.classList.add("ct-exhibition-js"); const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const revealItems = Array.from(root.querySelectorAll("[data-exhibition-reveal]")); revealItems.forEach((item) => { const delay = Number(item.getAttribute("data-exhibition-delay") || 0); item.style.setProperty("--cte-delay", delay + "ms"); }); if (reduced || !("IntersectionObserver" in window)) { revealItems.forEach((item) => item.classList.add("is-visible")); } else { const revealObserver = new IntersectionObserver((entries, observer) => { entries.forEach((entry) => { if (!entry.isIntersecting) return; entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }); }, { threshold: 0.08, rootMargin: "0px 0px -6% 0px" }); revealItems.forEach((item) => revealObserver.observe(item)); } /* Hero tilt */ const tilt = root.querySelector("[data-exhibition-tilt]"); if (tilt && !reduced && window.matchMedia("(pointer: fine)").matches) { tilt.addEventListener("pointermove", (event) => { const bounds = tilt.getBoundingClientRect(); const x = (event.clientX - bounds.left) / bounds.width - 0.5; const y = (event.clientY - bounds.top) / bounds.height - 0.5; tilt.style.transform = `perspective(1100px) rotateY(${x * 3.2}deg) rotateX(${y * -3.2}deg)`; }); tilt.addEventListener("pointerleave", () => { tilt.style.transform = ""; }); } /* Data-driven sticky table of contents */ const sections = Array.from(root.querySelectorAll("[data-exhibition-section]")); const tocNav = root.querySelector("[data-exhibition-toc-nav]"); const progress = root.querySelector("[data-exhibition-progress]"); const tocLinks = new Map(); if (tocNav) { sections.forEach((section, index) => { if (!section.id) section.id = `ct-exhibition-section-${index + 1}`; const label = section.dataset.sectionLabel || section.querySelector("h2")?.textContent?.trim() || ""; if (!label) return; const link = document.createElement("a"); link.href = `#${section.id}`; link.textContent = label.replace(/^\d+\s*\/\s*/, ""); link.addEventListener("click", (event) => { event.preventDefault(); section.scrollIntoView({ behavior: reduced ? "auto" : "smooth", block: "start" }); }); tocNav.appendChild(link); tocLinks.set(section, link); }); } if (sections.length && "IntersectionObserver" in window) { const sectionObserver = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (!entry.isIntersecting) return; tocLinks.forEach((link) => link.classList.remove("is-active")); tocLinks.get(entry.target)?.classList.add("is-active"); }); }, { rootMargin: "-28% 0px -60% 0px", threshold: 0 }); sections.forEach((section) => sectionObserver.observe(section)); } function updateProgress() { if (!progress) return; const rect = root.getBoundingClientRect(); const scrollable = Math.max(1, rect.height - window.innerHeight); const value = Math.min(1, Math.max(0, -rect.top / scrollable)); progress.style.setProperty("--cte-progress", `${value * 100}%`); } updateProgress(); window.addEventListener("scroll", updateProgress, { passive: true }); window.addEventListener("resize", updateProgress); /* Premium lightbox */ const triggers = Array.from(root.querySelectorAll("[data-lightbox-src]")); const lightbox = root.querySelector("[data-exhibition-lightbox]"); if (lightbox && triggers.length) { let current = 0; const image = lightbox.querySelector("[data-lightbox-image]"); const title = lightbox.querySelector("[data-lightbox-title]"); const caption = lightbox.querySelector("[data-lightbox-caption]"); const credit = lightbox.querySelector("[data-lightbox-credit]"); const counter = lightbox.querySelector("[data-lightbox-counter]"); function show(index) { current = (index + triggers.length) % triggers.length; const trigger = triggers[current]; image.src = trigger.dataset.lightboxSrc || ""; image.alt = trigger.dataset.lightboxAlt || ""; title.textContent = trigger.dataset.lightboxTitle || ""; caption.textContent = trigger.dataset.lightboxCaption || ""; credit.textContent = trigger.dataset.lightboxCredit || ""; counter.textContent = `${String(current + 1).padStart(2, "0")} / ${String(triggers.length).padStart(2, "0")}`; } function open(index) { show(index); lightbox.hidden = false; lightbox.setAttribute("aria-hidden", "false"); document.documentElement.style.overflow = "hidden"; lightbox.querySelector("[data-lightbox-close]")?.focus(); } function close() { lightbox.hidden = true; lightbox.setAttribute("aria-hidden", "true"); document.documentElement.style.overflow = ""; triggers[current]?.focus(); } triggers.forEach((trigger, index) => trigger.addEventListener("click", () => open(index))); lightbox.querySelector("[data-lightbox-close]")?.addEventListener("click", close); lightbox.querySelector("[data-lightbox-prev]")?.addEventListener("click", () => show(current - 1)); lightbox.querySelector("[data-lightbox-next]")?.addEventListener("click", () => show(current + 1)); lightbox.addEventListener("click", (event) => { if (event.target === lightbox) close(); }); document.addEventListener("keydown", (event) => { if (lightbox.hidden) return; if (event.key === "Escape") close(); if (event.key === "ArrowLeft") show(current - 1); if (event.key === "ArrowRight") show(current + 1); }); } } function initAll() { document.querySelectorAll("[data-ct-exhibition]").forEach(init); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initAll, { once: true }); } else { initAll(); } window.addEventListener("load", initAll, { once: true }); })(); (function () { "use strict"; function initStickyExhibitionToc(root) { if (!root || root.dataset.stickyTocInitialized === "true") { return; } const toc = root.querySelector("[data-exhibition-toc]"); const body = root.querySelector(".ct-exhibition-body"); const inner = toc ? toc.querySelector(".ct-exhibition-toc__inner") : null; if (!toc || !body || !inner) { return; } root.dataset.stickyTocInitialized = "true"; const desktopQuery = window.matchMedia("(min-width: 1660px)"); const topOffset = 120; const bottomGap = 100; let ticking = false; function resetState() { toc.classList.remove("is-fixed", "is-at-bottom"); } function updatePosition() { ticking = false; if (!desktopQuery.matches) { resetState(); return; } const bodyRect = body.getBoundingClientRect(); const tocHeight = inner.offsetHeight; const spaceNeededAtBottom = topOffset + tocHeight + bottomGap; if (bodyRect.top > topOffset) { resetState(); return; } if (bodyRect.bottom <= spaceNeededAtBottom) { toc.classList.remove("is-fixed"); toc.classList.add("is-at-bottom"); return; } toc.classList.remove("is-at-bottom"); toc.classList.add("is-fixed"); } function requestUpdate() { if (ticking) { return; } ticking = true; window.requestAnimationFrame(updatePosition); } updatePosition(); window.addEventListener("scroll", requestUpdate, { passive: true }); window.addEventListener("resize", requestUpdate); window.addEventListener("load", requestUpdate, { once: true }); if (typeof desktopQuery.addEventListener === "function") { desktopQuery.addEventListener("change", requestUpdate); } } function initAllStickyTocs() { document .querySelectorAll("[data-ct-exhibition]") .forEach(initStickyExhibitionToc); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initAllStickyTocs, { once: true }); } else { initAllStickyTocs(); } })(); /* ========================================================= CHANIARTOON — PREMIUM EXHIBITIONS LANDING Copy into custom.js without