(() => { 'use strict'; const PER_PAGE = 30; const INVALID_MARKERS = new Set(['bulunamadı', 'bulunamadi', 'bulunamadä±', 'hata', 'null', 'undefined']); const body = document.body; const appRoot = document.getElementById('appRoot'); const grid = document.getElementById('videoGrid'); const noResults = document.getElementById('noResults'); const noResultsText = document.getElementById('noResultsText'); const paginationWrap = document.getElementById('paginationWrap'); const pagination = document.getElementById('pagination'); const modal = document.getElementById('videoModal'); const modalBackdrop = document.getElementById('modalBackdrop'); const modalClose = document.getElementById('modalClose'); const modalTitle = document.getElementById('modalTitle'); const modalPlayer = document.getElementById('modalPlayer'); const modalPlayerContainer = document.getElementById('modalPlayerContainer'); const fullscreenButton = document.getElementById('toggleFullscreen'); const notice = document.getElementById('siteNotice'); let baseVideos = []; try { const parsed = JSON.parse(document.getElementById('videoData').textContent || '[]'); baseVideos = Array.isArray(parsed) ? parsed.map(normalizeVideo) : []; } catch (error) { baseVideos = []; } const activeVideos = baseVideos.slice(); let currentPage = Math.max(1, Number.parseInt(body.dataset.currentPage || '1', 10) || 1); let lastFocusedElement = null; let noticeTimer = 0; function safeUrl(value) { if (typeof value !== 'string') return ''; const trimmed = value.trim(); if (!trimmed || INVALID_MARKERS.has(trimmed.toLocaleLowerCase('tr-TR'))) return ''; try { const url = new URL(trimmed, window.location.href); if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) return ''; return url.href; } catch (error) { return ''; } } function scalarText(value, fallback = '') { if (typeof value === 'string' || typeof value === 'number') { const text = String(value).trim(); return text || fallback; } return fallback; } function normalizeVideo(row, index) { const video = row && typeof row === 'object' && !Array.isArray(row) ? row : {}; const iframeSrc = safeUrl(video.iframeSrc); const videoSrc = safeUrl(video.videoSrc); return { key: Number.isInteger(index) ? index : Number(video.key) || 0, id: scalarText(video.id, String((Number.isInteger(index) ? index : 0) + 1)), title: scalarText(video.title, 'Başlıksız video'), link: safeUrl(video.link), image: safeUrl(video.image), iframeSrc, videoSrc }; } function isMp4(url) { return /\.mp4(?:$|[?#])/i.test(url); } function videoEntries() { return activeVideos.map((video, index) => ({ video, index })); } function buildCard(video, index) { const article = document.createElement('article'); article.className = 'video-card'; const button = document.createElement('button'); button.className = 'video-trigger'; button.type = 'button'; button.dataset.videoIndex = String(index); button.setAttribute('aria-haspopup', 'dialog'); button.setAttribute('aria-label', `#${video.id} ${video.title} videosunu aç`); const thumbnail = document.createElement('div'); thumbnail.className = `thumbnail-frame${video.image ? '' : ' image-failed'}`; const placeholder = document.createElement('div'); placeholder.className = 'thumb-placeholder'; placeholder.setAttribute('aria-hidden', 'true'); placeholder.textContent = 'VİDEO'; thumbnail.appendChild(placeholder); if (video.image) { const image = document.createElement('img'); image.src = video.image; image.alt = ''; image.width = 320; image.height = 180; image.loading = 'lazy'; image.decoding = 'async'; image.addEventListener('error', () => thumbnail.classList.add('image-failed'), { once: true }); thumbnail.appendChild(image); } const cardBody = document.createElement('div'); cardBody.className = 'video-card-body'; const title = document.createElement('h2'); title.className = 'video-title'; title.textContent = video.title; cardBody.appendChild(title); button.append(thumbnail, cardBody); article.appendChild(button); return article; } function paginationTokens(page, totalPages) { if (totalPages <= 7) return Array.from({ length: totalPages }, (_, index) => index + 1); const pages = new Set([1, totalPages]); for (let value = Math.max(2, page - 2); value <= Math.min(totalPages - 1, page + 2); value += 1) { pages.add(value); } const sorted = Array.from(pages).sort((a, b) => a - b); const tokens = []; sorted.forEach((value, index) => { if (index > 0 && value - sorted[index - 1] > 1) tokens.push('ellipsis'); tokens.push(value); }); return tokens; } function pageHref(page) { const url = new URL(window.location.href); url.searchParams.delete('q'); if (page > 1) url.searchParams.set('page', String(page)); else url.searchParams.delete('page'); return `${url.pathname}${url.search}`; } function paginationItem(label, page, options = {}) { const li = document.createElement('li'); if (options.wide) li.classList.add('wide-page-control'); if (options.disabled) li.classList.add('disabled'); if (options.active) li.classList.add('active'); if (options.disabled || options.active) { const span = document.createElement('span'); span.textContent = label; if (options.disabled) span.setAttribute('aria-disabled', 'true'); if (options.active) span.setAttribute('aria-current', 'page'); li.appendChild(span); } else { const anchor = document.createElement('a'); anchor.href = pageHref(page); anchor.dataset.page = String(page); anchor.textContent = label; if (options.ariaLabel) anchor.setAttribute('aria-label', options.ariaLabel); li.appendChild(anchor); } return li; } function renderPagination(totalPages) { pagination.replaceChildren(); pagination.appendChild(paginationItem('« İlk', 1, { wide: true, disabled: currentPage === 1, ariaLabel: 'İlk sayfa' })); pagination.appendChild(paginationItem('‹ Önceki', currentPage - 1, { disabled: currentPage === 1 })); paginationTokens(currentPage, totalPages).forEach(token => { if (token === 'ellipsis') { pagination.appendChild(paginationItem('…', currentPage, { disabled: true })); } else { pagination.appendChild(paginationItem(String(token), token, { active: token === currentPage })); } }); pagination.appendChild(paginationItem('Sonraki ›', currentPage + 1, { disabled: currentPage === totalPages })); pagination.appendChild(paginationItem('Son »', totalPages, { wide: true, disabled: currentPage === totalPages, ariaLabel: 'Son sayfa' })); } function renderPage({ updateUrl = true, scroll = false } = {}) { const entries = videoEntries(); const filteredCount = entries.length; const totalPages = Math.max(1, Math.ceil(filteredCount / PER_PAGE)); currentPage = Math.min(Math.max(1, currentPage), totalPages); const startIndex = (currentPage - 1) * PER_PAGE; const pageEntries = entries.slice(startIndex, startIndex + PER_PAGE); grid.replaceChildren(...pageEntries.map(({ video, index }) => buildCard(video, index))); grid.hidden = filteredCount === 0; noResults.hidden = filteredCount !== 0; paginationWrap.hidden = filteredCount === 0; noResultsText.textContent = 'Gösterilebilecek video bulunmuyor.'; if (filteredCount > 0) renderPagination(totalPages); if (updateUrl) { window.history.replaceState({ page: currentPage }, '', pageHref(currentPage)); } if (scroll) { const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; document.getElementById('main-content').scrollIntoView({ behavior: reduceMotion ? 'auto' : 'smooth' }); } } function navigateToPage(page, scroll = true) { const totalPages = Math.max(1, Math.ceil(videoEntries().length / PER_PAGE)); const nextPage = Math.min(Math.max(1, Number(page) || 1), totalPages); if (nextPage === currentPage && !scroll) return; currentPage = nextPage; renderPage({ updateUrl: true, scroll }); } function markBrokenImages(root = document) { root.querySelectorAll('.thumbnail-frame img').forEach(image => { const fail = () => image.closest('.thumbnail-frame')?.classList.add('image-failed'); image.addEventListener('error', fail, { once: true }); if (image.complete && image.naturalWidth === 0) fail(); }); } function playbackSource(video) { const iframe = safeUrl(video.iframeSrc); const direct = safeUrl(video.videoSrc); if (iframe) return { type: 'iframe', url: iframe }; if (direct && isMp4(direct)) return { type: 'video', url: direct }; if (direct) return { type: 'iframe', url: direct }; return null; } function teardownPlayer() { const iframe = modalPlayer.querySelector('iframe'); if (iframe) iframe.src = 'about:blank'; const video = modalPlayer.querySelector('video'); if (video) { video.pause(); video.removeAttribute('src'); video.load(); } modalPlayer.replaceChildren(); } function openVideo(index, trigger) { const video = activeVideos[index]; if (!video) return; lastFocusedElement = trigger || document.activeElement; modalTitle.textContent = `#${video.id} — ${video.title}`; modal.classList.add('is-open'); modal.setAttribute('aria-hidden', 'false'); body.classList.add('modal-open'); if ('inert' in appRoot) appRoot.inert = true; teardownPlayer(); const source = playbackSource(video); if (!source) { const placeholder = document.createElement('div'); placeholder.className = 'player-placeholder'; const content = document.createElement('div'); const title = document.createElement('strong'); title.textContent = 'Bu video için oynatılabilir kaynak bulunamadı.'; const detail = document.createElement('span'); detail.textContent = 'Bu kayıt için geçerli bir video kaynağı yok.'; content.append(title, detail); placeholder.appendChild(content); modalPlayer.appendChild(placeholder); modalClose.focus(); return; } if (source.type === 'video') { const player = document.createElement('video'); player.src = source.url; player.controls = true; player.autoplay = true; player.playsInline = true; player.preload = 'metadata'; if (video.image) player.poster = video.image; modalPlayer.appendChild(player); const playPromise = player.play(); if (playPromise && typeof playPromise.catch === 'function') { playPromise.catch(() => {}); } } else { const frame = document.createElement('iframe'); frame.src = source.url; frame.title = `${video.title} oynatıcısı`; frame.allow = 'autoplay; encrypted-media; fullscreen; picture-in-picture'; frame.allowFullscreen = true; frame.referrerPolicy = 'strict-origin-when-cross-origin'; let sandbox = 'allow-scripts allow-forms allow-presentation allow-popups'; try { if (new URL(source.url).origin !== window.location.origin) sandbox += ' allow-same-origin'; } catch (error) { } frame.setAttribute('sandbox', sandbox); modalPlayer.appendChild(frame); } modalClose.focus(); } async function closeVideo() { if (!modal.classList.contains('is-open')) return; if (document.fullscreenElement) { try { await document.exitFullscreen(); } catch (error) { } } teardownPlayer(); modal.classList.remove('is-open'); modal.setAttribute('aria-hidden', 'true'); body.classList.remove('modal-open'); if ('inert' in appRoot) appRoot.inert = false; if (lastFocusedElement && typeof lastFocusedElement.focus === 'function') lastFocusedElement.focus(); lastFocusedElement = null; } async function toggleFullscreen() { if (!modal.classList.contains('is-open')) return; try { if (!document.fullscreenElement) await modalPlayerContainer.requestFullscreen(); else await document.exitFullscreen(); } catch (error) { showNotice('Tarayıcı tam ekran moduna izin vermedi.', 'warning'); } } function showNotice(message, type = 'info') { window.clearTimeout(noticeTimer); notice.className = `site-notice alert alert-${type}`; notice.textContent = message; notice.hidden = false; noticeTimer = window.setTimeout(() => { notice.hidden = true; }, 4200); } function trapModalFocus(event) { if (event.key !== 'Tab' || !modal.classList.contains('is-open')) return; const focusable = Array.from(modal.querySelectorAll('button:not(:disabled), [href], iframe, video[controls]')) .filter(element => !element.hasAttribute('hidden')); if (!focusable.length) return; const first = focusable[0]; const last = focusable[focusable.length - 1]; if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } } grid.addEventListener('click', event => { const trigger = event.target.closest('[data-video-index]'); if (!trigger) return; openVideo(Number.parseInt(trigger.dataset.videoIndex, 10), trigger); }); pagination.addEventListener('click', event => { const link = event.target.closest('[data-page]'); if (!link) return; event.preventDefault(); navigateToPage(Number.parseInt(link.dataset.page, 10)); }); modalClose.addEventListener('click', closeVideo); modalBackdrop.addEventListener('click', closeVideo); document.getElementById('closeVideoFooter').addEventListener('click', closeVideo); fullscreenButton.addEventListener('click', toggleFullscreen); modal.addEventListener('keydown', trapModalFocus); document.addEventListener('fullscreenchange', () => { fullscreenButton.textContent = document.fullscreenElement ? 'Tam Ekrandan Çık' : 'Tam Ekran'; }); window.addEventListener('popstate', () => { const url = new URL(window.location.href); currentPage = Math.max(1, Number.parseInt(url.searchParams.get('page') || '1', 10) || 1); renderPage({ updateUrl: false }); }); markBrokenImages(); })();