1/** 2 * ð® GameScript - Dynamic Grid Pagination & Infinite Horizontal Slider 3 */ 4document.addEventListener('DOMContentLoaded', function() { 5 6 // ========================================== 7 // 1. DİNAMİK GRID PAGINATION LİMİT AYARI 8 // ========================================== 9 function calculateOptimalGridLimit() { 10 const gamesGrid = document.querySelector('.games-grid'); 11 if (!gamesGrid) return 48; // Varsayılan 12 13 // Grid'in kaç sütunlu olduÄunu hesapla 14 const gridComputedStyle = window.getComputedStyle(gamesGrid); 15 const gridTemplateColumns = gridComputedStyle.getPropertyValue('grid-template-columns'); 16 const columns = gridTemplateColumns.split(' ').length; 17 18 // Ekran geniÅliÄine göre 4-6 satırlık tam doluluk hedefle 19 let rows = 6; 20 if (window.innerWidth < 480) rows = 6; 21 else if (window.innerWidth < 1024) rows = 5; 22 23 let optimalLimit = columns * rows; 24 if (optimalLimit < 12) optimalLimit = 12; 25 26 return optimalLimit; 27 } 28 29 // Ekran sütun sayısına göre cookie deÄerini güncelle (Backend'in okuması için) 30 const optimalLimit = calculateOptimalGridLimit(); 31 document.cookie = "grid_per_page=" + optimalLimit + "; path=/; max-age=31536000; SameSite=Lax"; 32 33 // "Daha Fazla Yükle" Butonu İÅlemleri
34 const loadMoreBtn = document.getElementById('load-more-btn'); 35 const gamesGrid = document.querySelector('.standard-grid') || document.querySelector('.games-grid'); 36 37 if (loadMoreBtn && gamesGrid) { 38 loadMoreBtn.addEventListener('click', function() { 39 const btn = this; 40 const nextPage = parseInt(btn.getAttribute('data-page')); 41 const totalPages = parseInt(btn.getAttribute('data-total')); 42 const btnText = btn.querySelector('.btn-text'); 43 const btnLoader = btn.querySelector('.btn-loader'); 44 45 const reqUrl = new URL(window.location.href); 46 reqUrl.searchParams.set('page', nextPage); 47 // Limit parametresini URL barına yansıtmadan sadece AJAX isteÄinde gönderelim 48 reqUrl.searchParams.set('limit', optimalLimit); 49 50 btn.disabled = true; 51 if (btnText) btnText.style.opacity = '0'; 52 if (btnLoader) btnLoader.style.display = 'inline-block'; 53 54 fetch(reqUrl.toString(), { 55 headers: { 'X-Requested-With': 'XMLHttpRequest' } 56 }) 57 .then(response => response.text()) 58 .then(html => { 59 const parser = new DOMParser(); 60 const doc = parser.parseFromString(html, 'text/html'); 61 62 const gridSelector = doc.querySelector('.standard-grid') ? '.standard-grid' : '.games-grid'; 63 const newGames = doc.querySelectorAll(`${gridSelector} .game-card`); 64 65 if (newGames.length > 0) { 66 newGames.forEach(game => { 67 game.style.opacity = '0'; 68 game.style.transform = 'translateY(10px)'; 69 game.style.transition = 'all 0.4s ease'; 70 gamesGrid.appendChild(game); 71 setTimeout(() => { 72 game.style.opacity = '1'; 73 game.style.transform = 'translateY(0)'; 74 }, 50); 75 }); 76 77 // URL'den limit ve page parametrelerini temizleyelim (refresh yapıldıÄında en baÅtan baÅlanması için) 78 const cleanUrl = new URL(window.location.href); 79 cleanUrl.searchParams.delete('page'); 80 cleanUrl.searchParams.delete('limit'); 81 window.history.replaceState({ page: nextPage }, '', cleanUrl.toString()); 82 83 if (nextPage < totalPages) { 84 btn.setAttribute('data-page', nextPage + 1); 85 btn.disabled = false; 86 if (btnText) btnText.style.opacity = '1'; 87 if (btnLoader) btnLoader.style.display = 'none'; 88 } else { 89 btn.parentElement.remove(); 90 } 91 } 92 }) 93 .catch(error => { 94 console.error('Pagination Error:', error); 95 btn.disabled = false; 96 if (btnText) btnText.style.opacity = '1'; 97 if (btnLoader) btnLoader.style.display = 'none'; 98 }); 99 }); 100 } 101 102 // ========================================== 103 // 2. DİNAMİK INFINITE CATEGORY LATEST SLIDER 104 // ========================================== 105 const sliderTrack = document.querySelector('.slider-track'); 106 const prevBtn = document.querySelector('.slider-nav-btn.prev'); 107 const nextBtn = document.querySelector('.slider-nav-btn.next'); 108 109 if (sliderTrack) { 110 let isFetching = false; 111 let hasMore = true; 112 let loadedOffset = sliderTrack.querySelectorAll('.slider-card').length; 113 const categorySlug = sliderTrack.getAttribute('data-category'); 114 115 // SaÄa ve Sola Buton Kontrolleri 116 if (prevBtn && nextBtn) { 117 prevBtn.addEventListener('click', () => { 118 sliderTrack.scrollBy({ left: -400, behavior: 'smooth' }); 119 }); 120 121 nextBtn.addEventListener('click', () => { 122 sliderTrack.scrollBy({ left: 400, behavior: 'smooth' }); 123 }); 124 } 125 126 // Sonsuz Kaydırma / Ajax Ek Oyun Yükleme DinamiÄi 127 sliderTrack.addEventListener('scroll', function() { 128 // SaÄ uca 200px kala yeni veriyi çek 129 const scrollLeft = sliderTrack.scrollLeft; 130 const scrollWidth = sliderTrack.scrollWidth; 131 const clientWidth = sliderTrack.clientWidth; 132 133 if (scrollLeft + clientWidth >= scrollWidth - 200) { 134 fetchMoreSliderGames(); 135 } 136 }); 137 138 function fetchMoreSliderGames() { 139 if (isFetching || !hasMore) return; 140 isFetching = true; 141 142 const limit = 10; 143 const fetchUrl = `${window.location.origin}/api/category-latest/${categorySlug}?offset=${loadedOffset}&limit=${limit}`; 144 145 fetch(fetchUrl) 146 .then(res => res.json()) 147 .then(data => { 148 if (data.success && data.games && data.games.length > 0) {
149 data.games.forEach(game => { 150 const wrapper = document.createElement('div'); 151 wrapper.className = 'slider-game-item'; 152 wrapper.style.cssText = 'flex: 0 0 auto; width: 220px; opacity: 0; transform: translateY(10px); transition: all 0.4s ease;'; 153 154 // Badge logic 155 let badgeHtml = ''; 156 if (game.is_featured) { 157 badgeHtml = '<span class="game-badge badge-featured">ÃNE ÃIKAN</span>'; 158 } else if (game.is_new) { 159 badgeHtml = '<span class="game-badge badge-new">YENİ</span>'; 160 } else if (game.is_hot) { 161 badgeHtml = '<span class="game-badge badge-hot">POPÃLER</span>'; 162 } 163 164 wrapper.innerHTML = ` 165 <a href="${game.url}" class="game-card glass-card"> 166 <div class="game-card-wrapper"> 167 <div class="game-thumb"> 168 <img src="${game.thumbnail}" alt="${escapeHtml(game.title)}" width="240" height="180" class="card-image" loading="lazy"> 169 <div class="card-overlay"> 170 <div class="game-hover-info"> 171 <h4 class="hover-title">${escapeHtml(game.title)}</h4> 172 </div> 173 </div> 174 </div> 175 ${badgeHtml} 176 </div> 177 </a> 178 `; 179 180 sliderTrack.appendChild(wrapper); 181 loadedOffset++; 182 183 setTimeout(() => { 184 wrapper.style.opacity = '1'; 185 wrapper.style.transform = 'translateY(0)'; 186 }, 50); 187 }); 188 189 hasMore = data.has_more; 190 } else { 191 hasMore = false; 192 } 193 isFetching = false; 194 }) 195 .catch(err => { 196 console.error('Error fetching slider games:', err); 197 isFetching = false; 198 }); 199 } 200 201 function escapeHtml(text) { 202 return text 203 .replace(/&/g, "&") 204 .replace(/</g, "<") 205 .replace(/>/g, ">") 206 .replace(/"/g, """) 207 .replace(/'/g, "'"); 208 } 209 } 210});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.