class leaksdailyApp { constructor() { this.currentTab = 'new'; this.searchTerm = ''; this.currentEthnicity = 'All Ethnicities'; this.selectedCreator = ''; this.currentLanguage = 'en'; // Always default to English this.clickCount = 0; this.clickLimit = 3; this.availableCreators = []; this.currentContentData = {}; // Store current content for timestamp updates this.init(); } getTimeTranslations() { return { 'tr': { 'dakika önce': 'dakika önce', 'saat önce': 'saat önce', 'gün önce': 'gün önce', 'hafta önce': 'hafta önce', 'ay önce': 'ay önce' }, 'en': { 'dakika önce': 'minutes ago', 'saat önce': 'hours ago', 'gün önce': 'days ago', 'hafta önce': 'weeks ago', 'ay önce': 'm }, 'fr': { 'dakika önce': 'minutes', 'saat önce': 'heures', 'gün önce': 'jours', 'hafta önce': 'semaines', 'ay önce': 'mois' }, 'de': { 'dakika önce': 'Minuten', 'saat önce': 'Stunden', 'gün önce': 'Tage', 'hafta önce': 'Wochen', 'ay önce': 'Monate' }, 'it': { 'dakika önce': 'minuti fa', 'saat önce': 'ore fa', 'gün önce': 'giorni fa', 'hafta önce': 'settimane fa', 'ay önce': 'mesi fa' }, 'nl': { 'dakika önce': 'minuten geleden', 'saat önce': 'uur geleden', 'gün önce': 'dagen geleden', 'hafta önce': 'weken geleden', 'ay önce': 'maanden geleden' } }; } getNavigationTranslations() { return { 'tr': { buy: 'Satın Al', premium: 'Soyun', terms: 'Şartlar' }, 'en': { buy: 'Buy', premium: 'Premium', terms: 'Terms' }, 'fr': { buy: 'Acheter', premium: 'Premium', terms: 'Conditions' }, 'de': { buy: 'Kaufen', premium: 'Premium', terms: 'Bedingungen' }, 'it': { buy: 'Acquista', premium: 'Premium', terms: 'Termini' }, 'nl': { buy: 'Kopen', premium: 'Premium', terms: 'Voorwaarden' } }; } getUiTranslations() { return { 'tr': { searchPlaceholder: 'Ara ve Filtrele' }, 'en': { searchPlaceholder: 'Search & Filter' }, 'fr': { searchPlaceholder: 'Rechercher et filtrer' }, 'de': { searchPlaceholder: 'Suchen & Filtern' }, 'it': { searchPlaceholder: 'Cerca e filtra' }, 'nl': { searchPlaceholder: 'Zoeken & Filteren' } }; } getAgeModalTranslations() { return { 'tr': { title: 'Bu site sadece yetişkinler için!', description: 'Bu web sitesine girerek 18 yaşında veya daha büyük olduğumu kabul ediyor ve Hizmet Şartlarını kabul ediyorum.', language: 'Türkçe', login: 'Giriş', confirm: '18 yaşındayım veya daha büyük' }, 'en': { title: 'This site is for adults only!', description: 'By entering this website, I confirm that I am 18 years of age or older and I agree to the Terms of Service.', language: 'English', login: 'Login', confirm: 'I am 18 or older' }, 'fr': { title: 'Ce site est réservé aux adultes!', description: 'En entrant sur ce site, je confirme que j\'ai 18 ans ou plus et que j\'accepte les Conditions d\'utilisation.', language: 'Français', login: 'Connexion', confirm: 'J\'ai 18 ans ou plus' }, 'de': { title: 'Diese Seite ist nur für Erwachsene!', description: 'Mit dem Betreten dieser Website bestätige ich, dass ich 18 Jahre oder älter bin und die Nutzungsbedingungen akzeptiere.', language: 'Deutsch', login: 'Anmelden', confirm: 'Ich bin 18 oder älter' }, 'it': { title: 'Questo sito è solo per adulti!', description: 'Entrando in questo sito, confermo di avere 18 anni o più e di accettare i Termini di servizio.', language: 'Italiano', login: 'Accesso', confirm: 'Ho 18 anni o più' }, 'nl': { title: 'Deze site is alleen voor volwassenen!', description: 'Door deze website te betreden, bevestig ik dat ik 18 jaar of ouder ben en akkoord ga met de Servicevoorwaarden.', language: 'Nederlands', login: 'Inloggen', confirm: 'Ik ben 18 of ouder' } }; } getPremiumTranslations() { return { 'tr': { premiumRequired: 'Premium Üyelik Gerekli', limitInfo1: 'Ücretsiz kullanıcılar günde sadece 3 model görüntüleyebilir.', limitInfo2: 'Sınırsız erişim için premium üyeliğe geçin!', premiumBenefits: 'Premium Avantajları:', unlimitedAccess: 'Sınırsız model erişimi', highQuality: 'Yüksek kaliteli içerik', adFree: 'Reklamsız deneyim', exclusiveContent: 'Özel içerikler', prioritySupport: 'Öncelikli destek', monthlyPlan: 'Aylık Plan', yearlyPlan: 'Yıllık Plan', perMonth: '/ay', perYear: '/yıl', mostPopular: 'En Popüler', savings: '2 ay ücretsiz!', upgradeToPremium: 'Premium Ol', moneyBackGuarantee: 'Güvenli ödeme ile 30 gün para iade garantisi', lastFreeView: 'Son 1 ücretsiz görüntüleme hakkınız kaldı!', orRedeem: 'Veya anahtar kullan', redeemKey: 'Anahtar Kullan', enterKey: 'Premium Anahtarı Girin', keyPlaceholder: 'Premium anahtarınızı girin...', activate: 'Etkinleştir', cancel: 'İptal' }, 'en': { premiumRequired: 'Premium Membership Required', limitInfo1: 'Free users can only view 3 models per day.', limitInfo2: 'Upgrade to premium for unlimited access!', premiumBenefits: 'Premium Benefits:', unlimitedAccess: 'Unlimited model access', highQuality: 'High quality content', adFree: 'Ad-free experience', exclusiveContent: 'Exclusive content', prioritySupport: 'Priority support', monthlyPlan: 'Monthly Plan', yearlyPlan: 'Yearly Plan', perMonth: '/month', perYear: '/year', mostPopular: 'Most Popular', savings: '2 months free!', upgradeToPremium: 'Go Premium', moneyBackGuarantee: 'Secure payment with 30-day money-back guarantee', lastFreeView: 'You have 1 free view remaining!', orRedeem: 'Or redeem a key', redeemKey: 'Redeem Key', enterKey: 'Enter Premium Key', keyPlaceholder: 'Enter your premium key...', activate: 'Activate', cancel: 'Cancel' }, 'fr': { premiumRequired: 'Abonnement Premium Requis', limitInfo1: 'Les utilisateurs gratuits ne peuvent voir que 3 modèles par jour.', limitInfo2: 'Passez au premium pour un accès illimité!', premiumBenefits: 'Avantages Premium:', unlimitedAccess: 'Accès illimité aux modèles', highQuality: 'Contenu de haute qualité', adFree: 'Expérience sans publicité', exclusiveContent: 'Contenu exclusif', prioritySupport: 'Support prioritaire', monthlyPlan: 'Plan Mensuel', yearlyPlan: 'Plan Annuel', perMonth: '/mois', perYear: '/an', mostPopular: 'Le Plus Populaire', savings: '2 mois gratuits!', upgradeToPremium: 'Passer Premium', moneyBackGuarantee: 'Paiement sécurisé avec garantie de remboursement de 30 jours', lastFreeView: 'Il vous reste 1 vue gratuite!', orRedeem: 'Ou utiliser une clé', redeemKey: 'Utiliser une Clé', enterKey: 'Entrer la Clé Premium', keyPlaceholder: 'Entrez votre clé premium...', activate: 'Activer', cancel: 'Annuler' }, 'de': { premiumRequired: 'Premium-Mitgliedschaft Erforderlich', limitInfo1: 'Kostenlose Nutzer können nur 3 Modelle pro Tag ansehen.', limitInfo2: 'Upgrade auf Premium für unbegrenzten Zugang!', premiumBenefits: 'Premium-Vorteile:', unlimitedAccess: 'Unbegrenzter Modellzugang', highQuality: 'Hochwertige Inhalte', adFree: 'Werbefreie Erfahrung', exclusiveContent: 'Exklusive Inhalte', prioritySupport: 'Prioritäts-Support', monthlyPlan: 'Monatsplan', yearlyPlan: 'Jahresplan', perMonth: '/Monat', perYear: '/Jahr', mostPopular: 'Am Beliebtesten', savings: '2 Monate kostenlos!', upgradeToPremium: 'Premium Werden', moneyBackGuarantee: 'Sichere Zahlung mit 30-Tage-Geld-zurück-Garantie', lastFreeView: 'Sie haben noch 1 kostenlose Ansicht!', orRedeem: 'Oder Schlüssel einlösen', redeemKey: 'Schlüssel Einlösen', enterKey: 'Premium-Schlüssel Eingeben', keyPlaceholder: 'Geben Sie Ihren Premium-Schlüssel ein...', activate: 'Aktivieren', cancel: 'Abbrechen' }, 'it': { premiumRequired: 'Abbonamento Premium Richiesto', limitInfo1: 'Gli utenti gratuiti possono visualizzare solo 3 modelli al giorno.', limitInfo2: 'Passa al premium per accesso illimitato!', premiumBenefits: 'Vantaggi Premium:', unlimitedAccess: 'Accesso illimitato ai modelli', highQuality: 'Contenuti di alta qualità', adFree: 'Esperienza senza pubblicità', exclusiveContent: 'Contenuti esclusivi', prioritySupport: 'Supporto prioritario', monthlyPlan: 'Piano Mensile', yearlyPlan: 'Piano Annuale', perMonth: '/mese', perYear: '/anno', mostPopular: 'Più Popolare', savings: '2 mesi gratuiti!', upgradeToPremium: 'Diventa Premium', moneyBackGuarantee: 'Pagamento sicuro con garanzia di rimborso di 30 giorni', lastFreeView: 'Ti rimane 1 visualizzazione gratuita!', orRedeem: 'O riscatta una chiave', redeemKey: 'Riscatta Chiave', enterKey: 'Inserisci Chiave Premium', keyPlaceholder: 'Inserisci la tua chiave premium...', activate: 'Attiva', cancel: 'Annulla' }, 'nl': { premiumRequired: 'Premium Lidmaatschap Vereist', limitInfo1: 'Gratis gebruikers kunnen slechts 3 modellen per dag bekijken.', limitInfo2: 'Upgrade naar premium voor onbeperkte toegang!', premiumBenefits: 'Premium Voordelen:', unlimitedAccess: 'Onbeperkte modeltoegang', highQuality: 'Hoogwaardige inhoud', adFree: 'Reclamevrije ervaring', exclusiveContent: 'Exclusieve inhoud', prioritySupport: 'Prioriteitsondersteuning', monthlyPlan: 'Maandelijks Plan', yearlyPlan: 'Jaarlijks Plan', perMonth: '/maand', perYear: '/jaar', mostPopular: 'Meest Populair', savings: '2 maanden gratis!', upgradeToPremium: 'Word Premium', moneyBackGuarantee: 'Veilige betaling met 30 dagen geld-terug-garantie', lastFreeView: 'Je hebt nog 1 gratis weergave over!', orRedeem: 'Of wissel een sleutel in', redeemKey: 'Sleutel Inwisselen', enterKey: 'Premium Sleutel Invoeren', keyPlaceholder: 'Voer je premium sleutel in...', activate: 'Activeren', cancel: 'Annuleren' } }; } getProfileTranslations() { return { 'tr': { userProfile: 'Kullanıcı Profili', accountStatus: 'Hesap Durumu', premiumMember: 'Premium Üye', freeMember: 'Ücretsiz Üye', unlimitedAccess: 'Sınırsız Erişim', limitedAccess: 'Sınırlı Erişim', accountType: 'Hesap Türü', memberSince: 'Üyelik Tarihi', clicksUsed: 'Kullanılan Tıklama', clicksRemaining: 'Kalan Tıklama', totalClicks: 'Toplam Tıklama', premiumPlan: 'Premium Plan', monthly: 'Aylık', yearly: 'Yıllık', premiumKey: 'Premium Anahtarı', upgradeToPremium: 'Premium Ol', redeemKey: 'Anahtar Kullan', manageSubscription: 'Aboneliği Yönet', premiumBenefits: 'Premium avantajlarından yararlanın!', freeUserMessage: 'Günde sadece 3 model görüntüleyebilirsiniz.', today: 'Bugün' }, 'en': { userProfile: 'User Profile', accountStatus: 'Account Status', premiumMember: 'Premium Member', freeMember: 'Free Member', unlimitedAccess: 'Unlimited Access', limitedAccess: 'Limited Access', accountType: 'Account Type', memberSince: 'Member Since', clicksUsed: 'Clicks Used', clicksRemaining: 'Clicks Remaining', totalClicks: 'Total Clicks', premiumPlan: 'Premium Plan', monthly: 'Monthly', yearly: 'Yearly', premiumKey: 'Premium Key', upgradeToPremium: 'Go Premium', redeemKey: 'Redeem Key', manageSubscription: 'Manage Subscription', premiumBenefits: 'Enjoy premium benefits!', freeUserMessage: 'You can only view 3 models per day.', today: 'Today' }, 'fr': { userProfile: 'Profil Utilisateur', accountStatus: 'Statut du Compte', premiumMember: 'Membre Premium', freeMember: 'Membre Gratuit', unlimitedAccess: 'Accès Illimité', limitedAccess: 'Accès Limité', accountType: 'Type de Compte', memberSince: 'Membre Depuis', clicksUsed: 'Clics Utilisés', clicksRemaining: 'Clics Restants', totalClicks: 'Total des Clics', premiumPlan: 'Plan Premium', monthly: 'Mensuel', yearly: 'Annuel', premiumKey: 'Clé Premium', upgradeToPremium: 'Passer Premium', redeemKey: 'Utiliser une Clé', manageSubscription: 'Gérer l\'Abonnement', premiumBenefits: 'Profitez des avantages premium!', freeUserMessage: 'Vous ne pouvez voir que 3 modèles par jour.', today: 'Aujourd\'hui' }, 'de': { userProfile: 'Benutzerprofil', accountStatus: 'Kontostatus', premiumMember: 'Premium-Mitglied', freeMember: 'Kostenloses Mitglied', unlimitedAccess: 'Unbegrenzter Zugang', limitedAccess: 'Begrenzter Zugang', accountType: 'Kontotyp', memberSince: 'Mitglied Seit', clicksUsed: 'Verwendete Klicks', clicksRemaining: 'Verbleibende Klicks', totalClicks: 'Gesamte Klicks', premiumPlan: 'Premium-Plan', monthly: 'Monatlich', yearly: 'Jährlich', premiumKey: 'Premium-Schlüssel', upgradeToPremium: 'Premium Werden', redeemKey: 'Schlüssel Einlösen', manageSubscription: 'Abonnement Verwalten', premiumBenefits: 'Genießen Sie Premium-Vorteile!', freeUserMessage: 'Sie können nur 3 Modelle pro Tag ansehen.', today: 'Heute' }, 'it': { userProfile: 'Profilo Utente', accountStatus: 'Stato Account', premiumMember: 'Membro Premium', freeMember: 'Membro Gratuito', unlimitedAccess: 'Accesso Illimitato', limitedAccess: 'Accesso Limitato', accountType: 'Tipo di Account', memberSince: 'Membro Dal', clicksUsed: 'Click Utilizzati', clicksRemaining: 'Click Rimanenti', totalClicks: 'Click Totali', premiumPlan: 'Piano Premium', monthly: 'Mensile', yearly: 'Annuale', premiumKey: 'Chiave Premium', upgradeToPremium: 'Diventa Premium', redeemKey: 'Riscatta Chiave', manageSubscription: 'Gestisci Abbonamento', premiumBenefits: 'Goditi i vantaggi premium!', freeUserMessage: 'Puoi visualizzare solo 3 modelli al giorno.', today: 'Oggi' }, 'nl': { userProfile: 'Gebruikersprofiel', accountStatus: 'Accountstatus', premiumMember: 'Premium Lid', freeMember: 'Gratis Lid', unlimitedAccess: 'Onbeperkte Toegang', limitedAccess: 'Beperkte Toegang', accountType: 'Accounttype', memberSince: 'Lid Sinds', clicksUsed: 'Gebruikte Klikken', clicksRemaining: 'Resterende Klikken', totalClicks: 'Totale Klikken', premiumPlan: 'Premium Plan', monthly: 'Maandelijks', yearly: 'Jaarlijks', premiumKey: 'Premium Sleutel', upgradeToPremium: 'Word Premium', redeemKey: 'Sleutel Inwisselen', manageSubscription: 'Abonnement Beheren', premiumBenefits: 'Geniet van premium voordelen!', freeUserMessage: 'Je kunt slechts 3 modellen per dag bekijken.', today: 'Vandaag' } }; } getPlansTranslations() { return { 'tr': { title: 'Premium Planlar', basic: 'Basic', premiumLite: 'Premium Lite', ultimate: 'Ultimate', free: 'Ücretsiz', currentPlan: 'Mevcut Plan', buyNow: 'Satın Al', mostPopular: 'En Popüler', bestValue: 'En İyi Değer', active: '(Aktif)', perDay: '/ gün', perMonth: '/ ay', month30Days: '/ 30 Gün', features: { aiGenerations: 'AI Üretimi içerir her ay', queuePriority: 'Kuyruk Önceliği', lowPriority: 'Düşük Kuyruk Önceliği', premiumPriority: 'Premium Lite Kuyruk Önceliği', ultimatePriority: 'Ultimate Kuyruk Önceliği', unlimitedAccess: 'Sınırsız erişim', prioritySupport: 'Öncelikli destek' } }, 'en': { title: 'Premium Plans', basic: 'Basic', premiumLite: 'Premium Lite', ultimate: 'Ultimate', free: 'Free', currentPlan: 'Current Plan', buyNow: 'Buy Now', mostPopular: 'Most Popular', bestValue: 'Best Value', active: '(Active)', perDay: '/ day', perMonth: '/ month', month30Days: '/ 30 Days', features: { aiGenerations: 'AI Generations included per month', queuePriority: 'Queue Priority', lowPriority: 'Low Queue Priority', premiumPriority: 'Premium Lite Queue Priority', ultimatePriority: 'Ultimate Queue Priority', unlimitedAccess: 'Unlimited access', prioritySupport: 'Priority support' } }, 'fr': { title: 'Plans Premium', basic: 'Basique', premiumLite: 'Premium Lite', ultimate: 'Ultimate', free: 'Gratuit', currentPlan: 'Plan Actuel', buyNow: 'Acheter', mostPopular: 'Le Plus Populaire', bestValue: 'Meilleure Valeur', active: '(Actif)', perDay: '/ jour', perMonth: '/ mois', month30Days: '/ 30 Jours', features: { aiGenerations: 'Générations IA incluses par mois', queuePriority: 'Priorité de File', lowPriority: 'Priorité de File Faible', premiumPriority: 'Priorité Premium Lite', ultimatePriority: 'Priorité Ultimate', unlimitedAccess: 'Accès illimité', prioritySupport: 'Support prioritaire' } }, 'de': { title: 'Premium-Pläne', basic: 'Basic', premiumLite: 'Premium Lite', ultimate: 'Ultimate', free: 'Kostenlos', currentPlan: 'Aktueller Plan', buyNow: 'Kaufen', mostPopular: 'Am Beliebtesten', bestValue: 'Bester Wert', active: '(Aktiv)', perDay: '/ Tag', perMonth: '/ Monat', month30Days: '/ 30 Tage', features: { aiGenerations: 'KI-Generierungen pro Monat enthalten', queuePriority: 'Warteschlangen-Priorität', lowPriority: 'Niedrige Warteschlangen-Priorität', premiumPriority: 'Premium Lite Priorität', ultimatePriority: 'Ultimate Priorität', unlimitedAccess: 'Unbegrenzter Zugang', prioritySupport: 'Prioritäts-Support' } }, 'it': { title: 'Piani Premium', basic: 'Basic', premiumLite: 'Premium Lite', ultimate: 'Ultimate', free: 'Gratuito', currentPlan: 'Piano Attuale', buyNow: 'Acquista', mostPopular: 'Più Popolare', bestValue: 'Miglior Valore', active: '(Attivo)', perDay: '/ giorno', perMonth: '/ mese', month30Days: '/ 30 Giorni', features: { aiGenerations: 'Generazioni AI incluse al mese', queuePriority: 'Priorità Coda', lowPriority: 'Priorità Coda Bassa', premiumPriority: 'Priorità Premium Lite', ultimatePriority: 'Priorità Ultimate', unlimitedAccess: 'Accesso illimitato', prioritySupport: 'Supporto prioritario' } }, 'nl': { title: 'Premium Plannen', basic: 'Basic', premiumLite: 'Premium Lite', ultimate: 'Ultimate', free: 'Gratis', currentPlan: 'Huidig Plan', buyNow: 'Kopen', mostPopular: 'Meest Populair', bestValue: 'Beste Waarde', active: '(Actief)', perDay: '/ dag', perMonth: '/ maand', month30Days: '/ 30 Dagen', features: { aiGenerations: 'AI Generaties inbegrepen per maand', queuePriority: 'Wachtrij Prioriteit', lowPriority: 'Lage Wachtrij Prioriteit', premiumPriority: 'Premium Lite Prioriteit', ultimatePriority: 'Ultimate Prioriteit', unlimitedAccess: 'Onbeperkte toegang', prioritySupport: 'Prioriteitsondersteuning' } } }; } async fetchExchangeRates() { try { const response = await fetch('https://api.exchangerate-api.com/v4/latest/USD'); const data = await response.json(); return data.rates; } catch (error) { console.warn('Could not fetch exchange rates, using fallback rates'); return { USD: 1, EUR: 0.85, TRY: 32.5, GBP: 0.73, CAD: 1.35, AUD: 1.52, JPY: 110 }; } } async convertCurrency(usdAmount, targetCurrency, rates) { if (!rates) { rates = await this.fetchExchangeRates(); } const rate = rates[targetCurrency] || 1; const convertedAmount = usdAmount * rate; const currencySymbols = { USD: '$', EUR: '€', TRY: '₺', GBP: '£', CAD: 'C$', AUD: 'A$', JPY: '¥' }; const symbol = currencySymbols[targetCurrency] || targetCurrency; if (targetCurrency === 'JPY') { return `${symbol}${Math.round(convertedAmount)}`; } else { return `${symbol}${convertedAmount.toFixed(2)}`; } } detectUserCurrency() { const userLang = this.currentLanguage; const currencyMap = { 'tr': 'TRY', 'en': 'USD', 'fr': 'EUR', 'de': 'EUR', 'it': 'EUR', 'nl': 'EUR' }; return currencyMap[userLang] || 'USD'; } // Calculate real-time "time ago" from timestamp calculateTimeAgo(timestamp) { if (!timestamp) return 'Unknown'; const now = new Date(); const postTime = new Date(timestamp); // Check if timestamp is valid if (isNaN(postTime.getTime())) { return 'Unknown'; } const diffMs = now - postTime; const diffSeconds = Math.floor(diffMs / 1000); const diffMinutes = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); // Handle future timestamps (shouldn't happen but just in case) if (diffMs < 0) { return 'Just now'; } // Always use English for timestamps as requested if (diffSeconds < 30) { return 'Just now'; } else if (diffSeconds < 60) { return `${diffSeconds} seconds ago`; } else if (diffMinutes < 60) { return `${diffMinutes} minute${diffMinutes > 1 ? 's' : ''} ago`; } else if (diffHours < 24) { return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; } else if (diffDays < 7) { return `${diffDays} day${diffDays > 1 ? 's' : ''} ago`; } else { const diffWeeks = Math.floor(diffDays / 7); return `${diffWeeks} week${diffWeeks > 1 ? 's' : ''} ago`; } } translateTimeString(timeStr, targetLang = this.currentLanguage) { if (!timeStr || targetLang === 'tr') return timeStr; const translations = this.getTimeTranslations(); const targetTranslations = translations[targetLang]; if (!targetTranslations) return timeStr; const match = timeStr.match(/(\d+)\s+(.+)/); if (!match) return timeStr; const [, number, timeUnit] = match; const translatedUnit = targetTranslations[timeUnit]; if (!translatedUnit) return timeStr; if (targetLang === 'fr') { return `il y a ${number}${translatedUnit}`; } else if (targetLang === 'de') { return `vor ${number}${translatedUnit}`; } else { return `${number}${translatedUnit}`; } } async fetchApiData() { // Add caching to prevent repeated API calls const cacheKey = 'apiData'; const cacheTimeout = 300000; // 5 minutes const now = Date.now(); if (this.apiCache && this.apiCache[cacheKey] && (now - this.apiCache[cacheKey].timestamp) < cacheTimeout) { console.log('Using cached API data:', this.apiCache[cacheKey].data.length, 'items'); return this.apiCache[cacheKey].data; } try { // First try to fetch from the leaksdaily API endpoint const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.models}`; const response = await fetch(apiUrl); if (response.ok) { const data = await response.json(); console.log('Using API data:', data.length, 'items'); // Cache the data if (!this.apiCache) this.apiCache = {}; this.apiCache[cacheKey] = { data, timestamp: now }; return data; } else { console.warn('Failed to load API data:', response.status); // Fallback to /data.json if API fails const fallbackResponse = await fetch('/data.json'); if (fallbackResponse.ok) { const fallbackData = await fallbackResponse.json(); console.log('Local data loaded successfully:', fallbackData.length, 'items'); // Cache fallback data too if (!this.apiCache) this.apiCache = {}; this.apiCache[cacheKey] = { data: fallbackData, timestamp: now }; return fallbackData; } else { console.warn('Failed to load local data.json:', fallbackResponse.status); return null; } } } catch (error) { console.warn('Error fetching API data:', error); // Try fallback to /data.json try { const fallbackResponse = await fetch('/data.json'); if (fallbackResponse.ok) { const fallbackData = await fallbackResponse.json(); console.log('Local data loaded successfully:', fallbackData.length, 'items'); // Cache fallback data if (!this.apiCache) this.apiCache = {}; this.apiCache[cacheKey] = { data: fallbackData, timestamp: now }; return fallbackData; } else { console.warn('Failed to load local data.json:', fallbackResponse.status); return null; } } catch (fallbackError) { console.warn('Error fetching local data.json:', fallbackError); return null; } } } async init() { // Clean up any invalid authentication data first this.validateAndCleanAuthData(); // Initialize image optimization this.initImageOptimization(); this.setupEventListeners(); this.loadConfiguration(); this.updateNavigationButtons(); // Translate buttons on initial load this.showAgeModal(); this.checkClickStatus(); // Add delays between API calls to prevent spam setTimeout(async () => { await this.loadCreators(); setTimeout(async () => { await this.renderContent(); this.setupModelTags(); this.setupEthnicityFilters(); this.startTimestampUpdater(); this.showOperationalNotification(); }, 500); // 500ms delay }, 200); // 200ms initial delay } setupEventListeners() { const searchInput = document.getElementById('searchInput'); if (searchInput) { searchInput.addEventListener('input', async (e) => { this.searchTerm = e.target.value.toLowerCase(); await this.renderContent(); }); } document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', async (e) => { const tabId = e.currentTarget.dataset.tab; await this.switchTab(tabId); }); }); window.closeModal = () => { this.closeAgeModal(); }; document.addEventListener('click', (e) => { if (e.target.closest('.nav-link')) { e.preventDefault(); const link = e.target.closest('.nav-link'); const href = link.getAttribute('href'); this.handleNavigation(href); } }); this.setupLanguageSelector(); // this.setupUserProfile(); // DISABLED: HTML handles authentication this.setupSearchDropdown(); } loadConfiguration() { document.title = SITE_CONFIG.seo.title; // Don't overwrite the logo if it's already properly set in HTML // Only update if SITE_CONFIG.siteIcon is provided and logo doesn't have an img element const logo = document.querySelector('.logo'); if (logo && SITE_CONFIG.siteIcon && !logo.querySelector('img')) { logo.innerHTML = ` ${SITE_CONFIG.siteName} `; } this.updateNavigation(); this.updateAgeModal(); const searchInput = document.getElementById('searchInput'); if (searchInput) { const uiTranslations = this.getUiTranslations(); const lang = uiTranslations[this.currentLanguage] || uiTranslations['en']; searchInput.placeholder = lang.searchPlaceholder; } this.updateTabs(); this.updateSocialNotifications(); } updateNavigation() { const navLinks = document.querySelector('.nav-links'); if (!navLinks) return; const navTranslations = this.getNavigationTranslations(); const lang = navTranslations[this.currentLanguage] || navTranslations['en']; let navHTML = ''; // Add all primary links SITE_CONFIG.navigation.primaryLinks.forEach(link => { if (link.text === 'Buy') { navHTML += ` ${lang.buy} `; } else if (link.text === 'Terms') { navHTML += ` ${lang.terms} `; } }); // Add special button (Premium) const specialBtn = SITE_CONFIG.navigation.specialButton; navHTML += ` ${lang.premium} `; navLinks.innerHTML = navHTML; } updateAgeModal() { const modal = document.getElementById('ageModal'); if (!modal) return; const modalContent = modal.querySelector('.modal-content'); if (!modalContent) return; const ageModalTranslations = this.getAgeModalTranslations(); const lang = ageModalTranslations[this.currentLanguage] || ageModalTranslations['en']; const languageData = { 'en': { flag: 'https://flagcdn.com/w20/us.png', alt: 'US', name: 'English' }, 'tr': { flag: 'https://flagcdn.com/w20/tr.png', alt: 'TR', name: 'Türkçe' }, 'fr': { flag: 'https://flagcdn.com/w20/fr.png', alt: 'FR', name: 'Français' }, 'de': { flag: 'https://flagcdn.com/w20/de.png', alt: 'DE', name: 'Deutsch' }, 'it': { flag: 'https://flagcdn.com/w20/it.png', alt: 'IT', name: 'Italiano' }, 'nl': { flag: 'https://flagcdn.com/w20/nl.png', alt: 'NL', name: 'Nederlands' } }; const currentLangData = languageData[this.currentLanguage] || languageData['en']; modalContent.innerHTML = `

${lang.title}

${lang.description}

`; } updateTabs() { const navTabs = document.querySelector('.nav-tabs'); if (!navTabs) return; let tabsHTML = ''; SITE_CONFIG.tabs.forEach(tab => { const activeClass = tab.active ? 'active' : ''; tabsHTML += ` `; }); navTabs.innerHTML = tabsHTML; } updateSocialNotifications() { const userMenu = document.querySelector('.user-menu'); if (!userMenu) return; // Don't overwrite the entire user menu, just update the social links part const existingTelegramLink = userMenu.querySelector('a[href*="t.me"]'); if (existingTelegramLink && SITE_CONFIG.socialLinks.length > 0) { const telegramConfig = SITE_CONFIG.socialLinks.find(link => link.platform === 'telegram'); if (telegramConfig) { existingTelegramLink.href = telegramConfig.url; // Remove any existing notification badges const notificationBadge = existingTelegramLink.querySelector('.notification-badge'); if (notificationBadge) { notificationBadge.remove(); } } } } async switchTab(tabId) { document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('active'); }); const targetTab = document.querySelector(`[data-tab="${tabId}"]`); if (targetTab) { targetTab.classList.add('active'); } this.currentTab = tabId; await this.renderContent(); } async renderContent() { const contentGrid = document.getElementById('contentGrid'); if (!contentGrid) return; // Don't show skeleton loader to avoid DOM manipulation issues // this.renderSkeletonLoader(); let contentData = []; // Check if we have any active filters const hasFilters = this.searchTerm || this.currentEthnicity !== 'All Ethnicities' || this.selectedCreator; if (hasFilters) { // Use advanced search when filters are active contentData = await this.performAdvancedSearch(); console.log('Using advanced search:', contentData.length, 'items'); } else if (['hot', 'views', 'random', 'oldest'].includes(this.currentTab)) { // For new categories, fetch from specific API endpoints try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.category}/${this.currentTab}`; const response = await fetch(apiUrl); if (response.ok) { const result = await response.json(); contentData = result.data || []; console.log(`Using ${this.currentTab} category data:`, contentData.length, 'items'); } else { console.warn(`Failed to fetch ${this.currentTab} category data:`, response.status); contentData = []; } } catch (error) { console.error(`Error fetching ${this.currentTab} category data:`, error); contentData = []; } } else { // Try to fetch from API first for regular categories const apiData = await this.fetchApiData(); if (apiData && apiData.length > 0) { contentData = apiData; console.log('Using API data:', contentData.length, 'items'); } else if (window.excelDataLoader && window.excelDataLoader.isDataLoaded()) { contentData = window.excelDataLoader.getContentData(); console.log('Using Excel data:', contentData.length, 'items'); } else { contentData = SITE_CONFIG.contentData || []; console.log('Using config data:', contentData.length, 'items'); } } let filteredContent; if (hasFilters) { // Content is already filtered by advanced search filteredContent = contentData; } else if (['hot', 'views', 'random', 'oldest'].includes(this.currentTab)) { // For new categories, content is already filtered by the API filteredContent = contentData; } else { // For regular categories, filter as before filteredContent = contentData.filter(item => { const matchesTab = this.currentTab === 'new' ? true : item.category === this.currentTab; return matchesTab; }); } // Sort filtered content to show pinned models (Editor's Choice) at the top filteredContent.sort((a, b) => { const aPinned = a.pinned === true || a.pinned === 1; const bPinned = b.pinned === true || b.pinned === 1; // If both are pinned or both are not pinned, maintain original order if (aPinned === bPinned) { return 0; } // Pinned models come first return bPinned ? 1 : -1; }); // Render content immediately without timeout to avoid CSS conflicts let contentHTML = ''; filteredContent.forEach(item => { contentHTML += this.createContentCard(item); }); if (filteredContent.length === 0) { contentHTML = `

No content found

Try changing your search criteria.

`; } contentGrid.innerHTML = contentHTML; // Store current content data for timestamp updates this.currentContentData = {}; filteredContent.forEach(item => { this.currentContentData[item.id] = item; }); this.setupContentCardListeners(); } createContentCard(item) { // Calculate real-time timestamps using the calculateTimeAgo method const timeAgo = this.calculateTimeAgo(item.stats.timeAgo); const downloadsTime = this.calculateTimeAgo(item.stats.downloads); // Check if model is pinned (Editor's Choice) const isPinned = item.pinned === true || item.pinned === 1; const editorsBadge = isPinned ? `
Editor's Choice
` : ''; // Check if the image URL is actually a video URL const isVideoUrl = this.isVideoUrl(item.image); // For pomf.cat URLs, we need to handle them specially const isPomfCat = item.image && item.image.toLowerCase().includes('pomf.cat'); let mediaElement; if (isPomfCat) { // For pomf.cat, create a smart media element that can switch between image and video mediaElement = this.createSmartMediaElement(item.image, item.title); } else { // For other URLs, use the normal logic mediaElement = isVideoUrl ? this.createVideoElement(item.image, item.title) : this.createImageElement(item.image, item.title); } return `
${editorsBadge}
${item.title}
${item.stats.size}
${item.stats.photos}
${item.stats.videos}
${timeAgo}
${downloadsTime}
${mediaElement}
@${item.creator || 'leaksdaily'}
${isVideoUrl ? '
' : ''}
`; } // Helper method to create smart media element for pomf.cat URLs createSmartMediaElement(url, title) { return `${title}`; } // Helper method to detect if a URL is a video URL isVideoUrl(url) { if (!url || typeof url !== 'string') return false; // Common video file extensions const videoExtensions = ['.mp4', '.webm', '.ogg', '.avi', '.mov', '.wmv', '.flv', '.mkv', '.m4v']; // Check for direct video file extensions const urlLower = url.toLowerCase(); const hasVideoExtension = videoExtensions.some(ext => urlLower.includes(ext)); // Check for common video hosting domains that serve direct video files const videoHosts = ['uguu.se', 'catbox.moe', 'files.catbox.moe', 'i.imgur.com', 'gfycat.com', 'redgifs.com']; const hasVideoHost = videoHosts.some(host => urlLower.includes(host)); // If it's from a video host and has .mp4 or similar extension, treat as video return hasVideoExtension || (hasVideoHost && urlLower.includes('.mp4')); } // Helper method to dynamically detect content type for pomf.cat URLs async detectPomfCatContentType(url) { if (!url || !url.toLowerCase().includes('pomf.cat')) { return 'image'; // Default to image for non-pomf.cat URLs } return new Promise((resolve) => { // Try to load as video first const video = document.createElement('video'); video.preload = 'metadata'; video.muted = true; const videoTimeout = setTimeout(() => { video.src = ''; resolve('image'); // If video doesn't load quickly, assume it's an image }, 3000); // 3 second timeout video.onloadedmetadata = () => { clearTimeout(videoTimeout); video.src = ''; resolve('video'); }; video.onerror = () => { clearTimeout(videoTimeout); video.src = ''; resolve('image'); }; video.src = url; }); } // Helper method to create video element createVideoElement(videoUrl, title) { return ``; } // Helper method to create image element (existing functionality) createImageElement(imageUrl, title) { return `${title}`; } setupContentCardListeners() { document.querySelectorAll('.content-card').forEach(card => { card.addEventListener('click', async (e) => { // Don't trigger if clicking the close button if (e.target.closest('.card-close-btn')) { return; } const url = card.dataset.url; const id = card.dataset.id; // Show "Redirecting..." message in the card const cardTitle = card.querySelector('.card-title'); const originalTitle = cardTitle.textContent; cardTitle.textContent = 'Redirecting...'; cardTitle.style.color = '#4CAF50'; // Add loading animation to the card card.style.opacity = '0.7'; card.style.pointerEvents = 'none'; try { await this.handleContentClick(url, id); } catch (error) { // Restore original state if there's an error cardTitle.textContent = originalTitle; cardTitle.style.color = ''; card.style.opacity = ''; card.style.pointerEvents = ''; } }); }); } async handleContentClick(url, id) { // Add debouncing to prevent rapid clicks on same model const clickKey = `click_${id}`; const now = Date.now(); if (this.lastClicks && this.lastClicks[clickKey] && (now - this.lastClicks[clickKey]) < 2000) { console.log('Click debounced - too rapid'); return; } if (!this.lastClicks) this.lastClicks = {}; this.lastClicks[clickKey] = now; // Track model view count regardless of premium status try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.trackView}`; await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ modelId: id }) }); console.log(`View tracked for model: ${id}`); } catch (error) { console.warn('Error tracking view:', error); } // Add small delay before click tracking to prevent rapid API calls await new Promise(resolve => setTimeout(resolve, 300)); try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.trackClick}`; // Get auth token for premium users const token = localStorage.getItem('authToken'); const headers = { 'Content-Type': 'application/json', }; // Add Authorization header if user is logged in if (token && this.isValidJWTFormat(token)) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(apiUrl, { method: 'POST', headers: { ...headers, 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }, body: JSON.stringify({ modelId: id }) }); const result = await response.json(); if (response.ok) { // Handle Premium users with direct mega links if (result.isPremiumAccess && result.directMegaLink) { console.log(`Premium user - direct mega link provided`); // Show premium access notification this.showPremiumAccessNotification(); // Show countdown modal before redirecting to direct mega link await this.showCountdownModal(url, result.directMegaLink); } else { // Regular click handling for free users console.log(`Click tracked: ${result.clickCount}/${result.limit}`); this.clickCount = result.clickCount; if (url && url !== '#') { // Show countdown modal before redirecting await this.showCountdownModal(url); } else { alert(`Content ${id} accessed! Remaining clicks: ${result.remaining}`); } if (result.remaining === 1) { this.showClickWarning(); } } } else { if (result.requiresLogin) { // User has premium features but needs to log in this.showErrorNotification('Please log in to access your premium features!'); setTimeout(() => { window.location.href = '/login'; }, 2000); } else if (result.dailyLimitReached || (result.error && result.error.includes('Your account reached daily limit'))) { // Handle daily limit - use the ACTUAL remaining time from backend let timeUntilReset = result.timeUntilReset || 0; let countdownTime = result.countdownTime || "00:00:00"; // Show countdown notification with the actual remaining time from backend this.showDailyLimitNotification(timeUntilReset, countdownTime); // IMPORTANT: Return here to prevent model from opening return; } else if (result.premium || result.needsPremium) { this.showPremiumModal(); // IMPORTANT: Return here to prevent model from opening return; } else { console.error('Click tracking failed:', result.error); this.showErrorNotification(result.error || 'Failed to track click'); // IMPORTANT: Return here to prevent model from opening on any other error return; } } } catch (error) { console.error('Error tracking click:', error); // Only show network error for actual network issues, not server responses if (error.name === 'TypeError' && error.message.includes('fetch')) { this.showErrorNotification('Network error occurred. Please try again later.'); } else { // For other errors, still don't open the URL to prevent bypassing limits this.showErrorNotification('An error occurred. Please try again later.'); } } } handleNavigation(href) { console.log(`Navigation clicked: ${href}`); if (href && href !== '#') { // Check if it's an internal link (starts with / or is relative) if (href.startsWith('/') || (!href.includes('://') && !href.startsWith('#'))) { // Internal navigation - use same window window.location.href = href; } else { // External link - navigate in same tab to avoid popup blocker on iOS window.location.href = href; } } } showAgeModal() { // Check if user has already confirmed age verification if (this.hasAgeVerificationCookie()) { return; // Don't show modal if already verified } const modal = document.getElementById('ageModal'); if (modal) { modal.style.display = 'flex'; } } closeAgeModal() { const modal = document.getElementById('ageModal'); if (modal) { modal.style.display = 'none'; // Set cookie to remember age verification for 30 days this.setAgeVerificationCookie(); } } // Cookie helper functions hasAgeVerificationCookie() { return this.getCookie('ageVerified') === 'true'; } setAgeVerificationCookie() { // Set cookie for 30 days const expirationDate = new Date(); expirationDate.setTime(expirationDate.getTime() + (30 * 24 * 60 * 60 * 1000)); document.cookie = `ageVerified=true; expires=${expirationDate.toUTCString()}; path=/; SameSite=Lax`; console.log('Age verification cookie set for 30 days'); } getCookie(name) { const nameEQ = name + "="; const ca = document.cookie.split(';'); for (let i = 0; i < ca.length; i++) { let c = ca[i]; while (c.charAt(0) === ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); } return null; } renderSkeletonLoader() { const contentGrid = document.getElementById('contentGrid'); if (!contentGrid) return; let skeletonHTML = ''; for (let i = 0; i < 12; i++) { skeletonHTML += `
`; } contentGrid.innerHTML = skeletonHTML; } setupLanguageSelector() { const languageSelector = document.getElementById('languageSelector'); const languageDropdown = document.getElementById('languageDropdown'); const currentLanguage = languageSelector?.querySelector('.current-language'); if (!languageSelector || !languageDropdown || !currentLanguage) return; currentLanguage.addEventListener('click', (e) => { e.stopPropagation(); languageDropdown.classList.toggle('show'); }); languageDropdown.addEventListener('click', async (e) => { const languageOption = e.target.closest('.language-option'); if (!languageOption) return; const langCode = languageOption.dataset.lang; const flagSrc = languageOption.querySelector('.flag').src; const flagAlt = languageOption.querySelector('.flag').alt; const name = languageOption.querySelector('.language-name').textContent; this.currentLanguage = langCode; currentLanguage.querySelector('.flag').src = flagSrc; currentLanguage.querySelector('.flag').alt = flagAlt; currentLanguage.querySelector('.language-name').textContent = name; languageDropdown.querySelectorAll('.language-option').forEach(opt => { opt.classList.remove('active'); }); languageOption.classList.add('active'); this.updatePremiumModalContent(); this.updateNavigationButtons(); await this.renderContent(); languageDropdown.classList.remove('show'); }); document.addEventListener('click', (e) => { if (!languageSelector.contains(e.target)) { languageDropdown.classList.remove('show'); } }); const currentLang = 'en'; // Always default to English const activeOption = languageDropdown.querySelector(`[data-lang="${currentLang}"]`); if (activeOption) { activeOption.classList.add('active'); const flagSrc = activeOption.querySelector('.flag').src; const flagAlt = activeOption.querySelector('.flag').alt; const name = activeOption.querySelector('.language-name').textContent; currentLanguage.querySelector('.flag').src = flagSrc; currentLanguage.querySelector('.flag').alt = flagAlt; currentLanguage.querySelector('.language-name').textContent = name; } } setupUserProfile() { console.log('🔧 Setting up user profile...'); // Check if user is logged in by looking for auth token const token = localStorage.getItem('authToken'); const user = JSON.parse(localStorage.getItem('user') || '{}'); console.log('🔍 Profile setup auth check:', { hasToken: !!token, tokenValid: token ? this.isValidJWTFormat(token) : false, hasUserId: !!user.id, user: user }); // Validate token format before using it if (token && this.isValidJWTFormat(token) && user.id) { // User is logged in - setup direct navigation to account page console.log('✅ User is logged in, setting up account navigation...'); const userProfile = document.getElementById('userProfile'); const loginIcon = document.getElementById('loginIcon'); console.log('🔍 Profile elements:', { userProfile: !!userProfile, loginIcon: !!loginIcon }); if (userProfile && loginIcon) { loginIcon.style.display = 'none'; userProfile.style.display = 'block'; // Update user info const usernameDisplay = document.getElementById('usernameDisplay'); if (usernameDisplay) { usernameDisplay.textContent = user.username || 'User'; } // Set up direct navigation to account page userProfile.onclick = function() { console.log('🖱️ User profile clicked - redirecting to account'); window.location.href = '/account'; }; console.log('✅ Updated user display info and set up account navigation'); } } else { // User is not logged in - setup login modal (NOT redirect) console.log('❌ User not logged in, setting up login modal...'); const userProfile = document.getElementById('userProfile'); const loginIcon = document.getElementById('loginIcon'); if (userProfile && loginIcon) { userProfile.style.display = 'none'; loginIcon.style.display = 'block'; } // Add click handler for login icon to redirect to login page const loginIconElement = document.getElementById('loginIcon'); if (loginIconElement) { loginIconElement.onclick = function() { console.log('🖱️ Login icon clicked - redirecting to login'); window.location.href = '/login'; }; } // Override showLoginModal globally to redirect instead of showing modal window.showLoginModal = function() { console.log('🔐 showLoginModal called - redirecting to /login instead'); window.location.href = '/login'; }; // Add global age verification functions window.confirmAge = function() { console.log('✅ Age confirmed - setting cookie and closing modal'); // Set age verification cookie for 30 days const expirationDate = new Date(); expirationDate.setTime(expirationDate.getTime() + (30 * 24 * 60 * 60 * 1000)); document.cookie = `ageVerified=true; expires=${expirationDate.toUTCString()}; path=/; SameSite=Lax`; // Close modal const modal = document.getElementById('ageModal'); if (modal) { modal.style.display = 'none'; } }; window.loginFromModal = function() { console.log('🔐 Login button clicked from modal - redirecting to /login'); window.location.href = '/login'; }; window.toggleModalLanguageDropdown = function() { console.log('🌍 Modal language dropdown toggled'); const dropdown = document.getElementById('modalLanguageDropdown'); if (dropdown) { const isVisible = dropdown.style.display === 'block'; dropdown.style.display = isVisible ? 'none' : 'block'; } }; window.selectModalLanguage = function(lang) { console.log('🌍 Modal language selected:', lang); const languageData = { 'en': { flag: 'https://flagcdn.com/w20/us.png', alt: 'US', name: 'English' }, 'tr': { flag: 'https://flagcdn.com/w20/tr.png', alt: 'TR', name: 'Türkçe' }, 'fr': { flag: 'https://flagcdn.com/w20/fr.png', alt: 'FR', name: 'Français' }, 'de': { flag: 'https://flagcdn.com/w20/de.png', alt: 'DE', name: 'Deutsch' }, 'it': { flag: 'https://flagcdn.com/w20/it.png', alt: 'IT', name: 'Italiano' }, 'nl': { flag: 'https://flagcdn.com/w20/nl.png', alt: 'NL', name: 'Nederlands' } }; // Update current language display const currentFlag = document.getElementById('modalCurrentFlag'); const currentText = document.getElementById('modalLanguageText'); if (currentFlag && currentText && languageData[lang]) { currentFlag.src = languageData[lang].flag; currentFlag.alt = languageData[lang].alt; currentText.textContent = languageData[lang].name; } // Update global language and app language window.DETECTED_LANGUAGE = lang; if (window.app) { window.app.currentLanguage = lang; window.app.updateAgeModal(); } // Close dropdown const dropdown = document.getElementById('modalLanguageDropdown'); if (dropdown) { dropdown.style.display = 'none'; } // Update active state document.querySelectorAll('#modalLanguageDropdown .language-option').forEach(opt => { opt.classList.remove('active'); }); const activeOption = document.querySelector(`#modalLanguageDropdown [data-lang="${lang}"]`); if (activeOption) { activeOption.classList.add('active'); } console.log(`🌍 Language changed to: ${lang} (${languageData[lang].name})`); }; } } // Helper function to validate JWT token format isValidJWTFormat(token) { if (!token || typeof token !== 'string') { console.log('Invalid token: not a string or empty'); this.clearInvalidAuthData(); return false; } const parts = token.split('.'); if (parts.length !== 3) { console.log('Invalid token: does not have 3 parts'); this.clearInvalidAuthData(); return false; } // Check if each part is base64-like (basic validation) for (let part of parts) { if (!part || part.length === 0) { console.log('Invalid token: empty part detected'); this.clearInvalidAuthData(); return false; } } return true; } // Helper function to clear invalid authentication data clearInvalidAuthData() { console.log('Clearing invalid authentication data from localStorage'); localStorage.removeItem('authToken'); localStorage.removeItem('user'); localStorage.removeItem('refreshToken'); } // Validate and clean authentication data on app initialization validateAndCleanAuthData() { const token = localStorage.getItem('authToken'); const user = localStorage.getItem('user'); // Check if token exists and is valid format if (token && !this.isValidJWTFormat(token)) { console.log('Found malformed token in localStorage, cleaning up...'); this.clearInvalidAuthData(); return; } // Check if user data is valid JSON if (user) { try { JSON.parse(user); } catch (error) { console.log('Found malformed user data in localStorage, cleaning up...'); this.clearInvalidAuthData(); return; } } // If we have a token but no user data, or vice versa, clean up if ((token && !user) || (!token && user)) { console.log('Inconsistent auth data found, cleaning up...'); this.clearInvalidAuthData(); return; } console.log('Authentication data validation completed'); } // Helper function to toggle dropdown - REMOVED (using global function instead) async showProfileModal() { console.log('🔍 showProfileModal called'); // Check if user is logged in first const token = localStorage.getItem('authToken'); const user = JSON.parse(localStorage.getItem('user') || '{}'); if (token && this.isValidJWTFormat(token) && user.id) { // User is logged in - use the global toggleUserDropdown function console.log('✅ User is logged in, toggling dropdown'); if (window.toggleUserDropdown) { window.toggleUserDropdown(); } return; } // User is not logged in - redirect to login page console.log('❌ User not logged in - redirecting to login'); window.location.href = '/login'; } async updateProfileModalContent() { // DISABLED: This function is disabled to prevent showing cached profile content console.log('Profile modal content generation disabled'); return; } closeProfileModal() { const modal = document.getElementById('profileModal'); if (modal) { modal.style.display = 'none'; } } async checkClickStatus() { // Add debouncing to prevent rapid calls if (this.clickStatusTimeout) { clearTimeout(this.clickStatusTimeout); } this.clickStatusTimeout = setTimeout(async () => { try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.clickStatus}`; const response = await fetch(apiUrl); const result = await response.json(); if (response.ok) { this.clickCount = result.clickCount; console.log(`Current click status: ${result.clickCount}/${result.limit}`); if (result.needsPremium) { console.log('Premium required for more clicks'); } } } catch (error) { console.error('Error checking click status:', error); } }, 1000); // 1 second debounce } showPremiumModal() { const modal = document.getElementById('premiumModal'); if (modal) { this.updatePremiumModalContent(); modal.style.display = 'flex'; } } updatePremiumModalContent() { const translations = this.getPremiumTranslations(); const lang = translations[this.currentLanguage] || translations['en']; const modal = document.getElementById('premiumModal'); if (!modal) return; const modalContent = modal.querySelector('.modal-content'); if (!modalContent) return; modalContent.innerHTML = `

${lang.premiumRequired}

${lang.limitInfo1}

${lang.limitInfo2}

${lang.premiumBenefits}

${lang.monthlyPlan}

$9.99${lang.perMonth}
${lang.orRedeem || 'Or redeem a key'}
`; } closePremiumModal() { const modal = document.getElementById('premiumModal'); if (modal) { modal.style.display = 'none'; } } showClickWarning() { const translations = this.getPremiumTranslations(); const lang = translations[this.currentLanguage] || translations['en']; const warning = document.createElement('div'); warning.style.cssText = ` position: fixed;top: 20px;right: 20px;background: linear-gradient(135deg,#ff4757 0%,#ff3742 100%);color: white;padding: 15px 20px;border-radius: 8px;box-shadow: 0 10px 30px rgba(255,71,87,0.3);z-index: 9999;font-weight: 600;animation: slideIn 0.3s ease;`; warning.innerHTML = ` ${lang.lastFreeView}`; document.body.appendChild(warning); setTimeout(() => { if (warning.parentNode) { warning.parentNode.removeChild(warning); } }, 5000); } showDailyLimitNotification(timeUntilReset, initialCountdownTime) { // Remove any existing daily limit notification const existingNotification = document.getElementById('dailyLimitNotification'); if (existingNotification) { existingNotification.remove(); } const notification = document.createElement('div'); notification.id = 'dailyLimitNotification'; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #ff4757 0%, #ff3742 100%); color: white; padding: 20px 25px; border-radius: 12px; box-shadow: 0 10px 30px rgba(255,71,87,0.4); z-index: 10000; font-weight: 600; animation: slideIn 0.3s ease; max-width: 400px; border: 1px solid rgba(255,255,255,0.2); `; // Create countdown display const countdownDisplay = document.createElement('div'); countdownDisplay.style.cssText = ` font-size: 20px; font-family: 'Courier New', monospace; background: rgba(0,0,0,0.3); padding: 10px 15px; border-radius: 8px; margin-top: 12px; text-align: center; letter-spacing: 2px; border: 1px solid rgba(255,255,255,0.2); `; notification.innerHTML = `
Daily Limit Reached
Your account reached daily limit. Please wait to finish in order to access data again.
`; notification.appendChild(countdownDisplay); document.body.appendChild(notification); // Start countdown timer let remainingTime = timeUntilReset; const timeDisplay = notification.querySelector('#timeDisplay'); const updateCountdown = () => { if (remainingTime <= 0) { // Time's up - remove notification without refreshing notification.remove(); clearInterval(countdownInterval); return; } const hours = Math.floor(remainingTime / (1000 * 60 * 60)); const minutes = Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60)); const seconds = Math.floor((remainingTime % (1000 * 60)) / 1000); const timeString = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; // Update both displays countdownDisplay.textContent = timeString; if (timeDisplay) { timeDisplay.textContent = timeString; } remainingTime -= 1000; }; // Update immediately and then every second updateCountdown(); window.dailyLimitCountdownInterval = setInterval(updateCountdown, 1000); const countdownInterval = window.dailyLimitCountdownInterval; // Clean up interval when notification is removed const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.removedNodes.forEach((node) => { if (node === notification) { clearInterval(window.dailyLimitCountdownInterval); observer.disconnect(); } }); }); }); observer.observe(document.body, { childList: true }); // Auto-remove after countdown completes (backup) setTimeout(() => { if (notification.parentNode) { notification.remove(); clearInterval(window.dailyLimitCountdownInterval); } }, remainingTime + 1000); } showPremiumAccessNotification() { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; padding: 15px 20px; border-radius: 8px; box-shadow: 0 10px 30px rgba(76,175,80,0.3); z-index: 9999; font-weight: 600; animation: slideIn 0.3s ease; `; notification.innerHTML = ` Premium access - Direct mega link opened! `; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 4000); } showErrorNotification(message) { const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #ff4757 0%, #ff3742 100%); color: white; padding: 15px 20px; border-radius: 8px; box-shadow: 0 10px 30px rgba(255,71,87,0.3); z-index: 9999; font-weight: 600; animation: slideIn 0.3s ease; `; notification.innerHTML = ` ${message} `; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 5000); } showCountdownModal(url, directMegaLink = null) { return new Promise((resolve) => { // Just show "Redirecting..." message and redirect after 3 seconds setTimeout(() => { // Navigate to the appropriate URL const targetUrl = directMegaLink || url; if (targetUrl && targetUrl !== '#') { window.location.href = targetUrl; } resolve(); }, 3000); }); } updateNavigationButtons() { const navTranslations = this.getNavigationTranslations(); const lang = navTranslations[this.currentLanguage] || navTranslations['en']; const buyButton = document.querySelector('.nav-link:not(.soyun-btn)'); if (buyButton) { buyButton.innerHTML = ` ${lang.buy}`; } const premiumButton = document.querySelector('.nav-link.soyun-btn'); if (premiumButton) { premiumButton.innerHTML = `${lang.premium}`; } } // Load available creators for tags with caching async loadCreators() { // Add caching to prevent repeated creator API calls const cacheKey = 'creators'; const cacheTimeout = 600000; // 10 minutes const now = Date.now(); if (this.apiCache && this.apiCache[cacheKey] && (now - this.apiCache[cacheKey].timestamp) < cacheTimeout) { this.availableCreators = this.apiCache[cacheKey].data; console.log('Using cached creators:', this.availableCreators.length); return; } try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.creators}`; const response = await fetch(apiUrl); if (response.ok) { const result = await response.json(); this.availableCreators = result.data || []; console.log('Loaded creators:', this.availableCreators.length); // Cache the creators data if (!this.apiCache) this.apiCache = {}; this.apiCache[cacheKey] = { data: this.availableCreators, timestamp: now }; } } catch (error) { console.warn('Error loading creators:', error); this.availableCreators = []; } } // Setup model tags setupModelTags() { const modelTagsContainer = document.getElementById('modelTags'); if (!modelTagsContainer || this.availableCreators.length === 0) return; // Show top 8 most popular creators as tags const topCreators = this.availableCreators.slice(0, 8); let tagsHTML = ''; topCreators.forEach(creator => { const isActive = this.selectedCreator === creator ? 'active' : ''; tagsHTML += ` `; }); modelTagsContainer.innerHTML = tagsHTML; // Add click listeners modelTagsContainer.addEventListener('click', (e) => { const tag = e.target.closest('.model-tag'); if (tag) { const creator = tag.dataset.creator; this.handleCreatorFilter(creator); } }); } // Setup ethnicity filters setupEthnicityFilters() { const ethnicityContainer = document.getElementById('ethnicityFilters'); if (!ethnicityContainer) return; ethnicityContainer.addEventListener('click', (e) => { const btn = e.target.closest('.ethnicity-btn'); if (btn) { const ethnicity = btn.dataset.ethnicity; this.handleEthnicityFilter(ethnicity); } }); } // Handle creator filter async handleCreatorFilter(creator) { // Toggle creator selection if (this.selectedCreator === creator) { this.selectedCreator = ''; } else { this.selectedCreator = creator; } // Update UI document.querySelectorAll('.model-tag').forEach(tag => { tag.classList.remove('active'); }); if (this.selectedCreator) { const activeTag = document.querySelector(`[data-creator="${this.selectedCreator}"]`); if (activeTag) { activeTag.classList.add('active'); } } // Re-render content await this.renderContent(); } // Handle ethnicity filter async handleEthnicityFilter(ethnicity) { this.currentEthnicity = ethnicity; // Update UI document.querySelectorAll('.ethnicity-btn').forEach(btn => { btn.classList.remove('active'); }); const activeBtn = document.querySelector(`[data-ethnicity="${ethnicity}"]`); if (activeBtn) { activeBtn.classList.add('active'); } // Re-render content await this.renderContent(); } // Enhanced search with filters and caching async performAdvancedSearch() { // Create cache key based on search parameters const cacheKey = `search_${this.searchTerm}_${this.currentEthnicity}_${this.selectedCreator}_${this.currentTab}`; const cacheTimeout = 120000; // 2 minutes for search results const now = Date.now(); if (this.apiCache && this.apiCache[cacheKey] && (now - this.apiCache[cacheKey].timestamp) < cacheTimeout) { console.log('Using cached search results'); return this.apiCache[cacheKey].data; } try { const params = new URLSearchParams(); if (this.searchTerm) params.append('q', this.searchTerm); if (this.currentEthnicity !== 'All Ethnicities') params.append('ethnicity', this.currentEthnicity); if (this.selectedCreator) params.append('creator', this.selectedCreator); if (this.currentTab !== 'new') params.append('category', this.currentTab); params.append('limit', '20'); const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.advancedSearch}?${params}`; const response = await fetch(apiUrl); if (response.ok) { const result = await response.json(); const data = result.data || []; // Cache search results if (!this.apiCache) this.apiCache = {}; this.apiCache[cacheKey] = { data, timestamp: now }; return data; } } catch (error) { console.error('Error in advanced search:', error); } return []; } // Setup search dropdown functionality setupSearchDropdown() { const searchDropdown = document.getElementById('searchDropdown'); const dropdownMenu = document.getElementById('dropdownMenu'); const currentCategory = document.getElementById('currentCategory'); if (!searchDropdown || !dropdownMenu || !currentCategory) return; // Toggle dropdown on click searchDropdown.addEventListener('click', (e) => { e.stopPropagation(); dropdownMenu.classList.toggle('show'); }); // Handle dropdown item selection dropdownMenu.addEventListener('click', async (e) => { const dropdownItem = e.target.closest('.dropdown-item'); if (!dropdownItem) return; const category = dropdownItem.dataset.category; const icon = dropdownItem.querySelector('i').className; const text = dropdownItem.querySelector('span').textContent; // Update current category display const currentIcon = searchDropdown.querySelector('i'); if (currentIcon) { currentIcon.className = icon; } currentCategory.textContent = text; // Update active state dropdownMenu.querySelectorAll('.dropdown-item').forEach(item => { item.classList.remove('active'); }); dropdownItem.classList.add('active'); // Switch to the selected category await this.switchTab(category); // Close dropdown dropdownMenu.classList.remove('show'); }); // Close dropdown when clicking outside document.addEventListener('click', (e) => { if (!searchDropdown.contains(e.target)) { dropdownMenu.classList.remove('show'); } }); // Set initial active state const initialItem = dropdownMenu.querySelector(`[data-category="${this.currentTab}"]`); if (initialItem) { initialItem.classList.add('active'); } } // Start real-time timestamp updater with reduced frequency startTimestampUpdater() { // Update timestamps every 5 minutes instead of 30 seconds to reduce spam setInterval(() => { this.updateTimestamps(); }, 300000); // 5 minutes = 300,000ms console.log('Real-time timestamp updater started (5 min intervals)'); } // Update all visible timestamps updateTimestamps() { const timestampElements = document.querySelectorAll('[data-timestamp]'); timestampElements.forEach(element => { const rawTimestamp = element.getAttribute('data-timestamp'); if (rawTimestamp) { const newTimeAgo = this.calculateTimeAgo(rawTimestamp); element.textContent = newTimeAgo; console.log('Updated timestamp:', rawTimestamp, '→', newTimeAgo); } }); if (timestampElements.length > 0) { console.log(`Updated ${timestampElements.length} timestamps`); } } // Show operational status notification showOperationalNotification() { // Check if notification was already shown in this session if (sessionStorage.getItem('operationalNotificationShown')) { return; } // Create notification const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; padding: 20px 25px; border-radius: 12px; box-shadow: 0 10px 30px rgba(76,175,80,0.4); z-index: 10000; font-weight: 600; animation: slideInFromRight 0.5s ease; max-width: 400px; border: 1px solid rgba(255,255,255,0.2); cursor: pointer; `; notification.innerHTML = `
System Status
The website is operational and working. All issues have been dealt with and fixed.
`; // Add CSS animation if not already added if (!document.getElementById('operationalNotificationStyles')) { const style = document.createElement('style'); style.id = 'operationalNotificationStyles'; style.textContent = ` @keyframes slideInFromRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } `; document.head.appendChild(style); } // Add click to dismiss functionality notification.addEventListener('click', () => { notification.remove(); sessionStorage.setItem('operationalNotificationShown', 'true'); }); document.body.appendChild(notification); // Auto-dismiss after 8 seconds setTimeout(() => { if (notification.parentNode) { notification.style.animation = 'slideInFromRight 0.3s ease reverse'; setTimeout(() => { if (notification.parentNode) { notification.remove(); sessionStorage.setItem('operationalNotificationShown', 'true'); } }, 300); } }, 8000); console.log('✅ Operational status notification displayed'); } // Image optimization methods initImageOptimization() { // Create image cache this.imageCache = new Map(); // Preload critical images this.preloadCriticalImages(); // Setup intersection observer for lazy loading this.setupIntersectionObserver(); } preloadCriticalImages() { const criticalImages = [ '/assets/logo3.png', '/assets/logo2.png', '/assets/NaughtyLeaks.jpg' ]; criticalImages.forEach(src => { const img = new Image(); img.src = src; this.imageCache.set(src, img); }); } setupIntersectionObserver() { if ('IntersectionObserver' in window) { this.imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; if (img.dataset.src) { this.loadImage(img); this.imageObserver.unobserve(img); } } }); }, { rootMargin: '50px 0px', threshold: 0.01 }); } } loadImage(img) { return new Promise((resolve, reject) => { const tempImg = new Image(); tempImg.onload = () => { img.src = tempImg.src; img.style.opacity = '1'; this.imageCache.set(img.src, tempImg); resolve(); }; tempImg.onerror = () => { img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPkltYWdlIE5vdCBGb3VuZDwvdGV4dD48L3N2Zz4='; img.style.opacity = '1'; reject(); }; tempImg.src = img.dataset.src; }); } optimizeImageLoading() { // Add cache headers for better browser caching const images = document.querySelectorAll('img[loading="lazy"]'); images.forEach(img => { if (!img.complete && this.imageObserver) { this.imageObserver.observe(img); } }); } } // Global smart media error handler for pomf.cat URLs window.handleSmartMediaError = function(img, originalSrc) { console.log(`Smart media (image) load failed for pomf.cat URL: ${originalSrc}, trying as video...`); // If image fails to load, try loading as video const video = document.createElement('video'); video.className = 'card-video'; video.muted = true; video.autoplay = true; video.loop = true; video.playsInline = true; video.preload = 'metadata'; video.style.opacity = '0'; video.style.transition = 'opacity 0.3s ease'; video.onloadedmetadata = function() { // Video loaded successfully, replace the image with video console.log(`Successfully loaded pomf.cat URL as video: ${originalSrc}`); video.style.opacity = '1'; img.parentNode.replaceChild(video, img); // Add video indicator const container = video.parentNode; if (container && !container.querySelector('.video-indicator')) { const videoIndicator = document.createElement('div'); videoIndicator.className = 'video-indicator'; videoIndicator.innerHTML = ''; container.appendChild(videoIndicator); } // Update card class to indicate it's a video const card = video.closest('.content-card'); if (card) { card.classList.add('video-card'); } }; video.onerror = function() { // Video also failed, use fallback image console.warn(`Both image and video failed for pomf.cat URL: ${originalSrc}`); img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPkNvbnRlbnQgTm90IEZvdW5kPC90ZXh0Pjwvc3ZnPg=='; img.style.opacity = '1'; img.dataset.loaded = 'failed'; }; // Set video source video.innerHTML = ` Your browser does not support the video tag. `; video.load(); }; // Global image error handler with retry logic (automatic deletion disabled) window.handleImageError = function(img, originalSrc) { const retryCount = parseInt(img.dataset.retryCount || '0'); const maxRetries = 2; console.log(`Image load failed for: ${originalSrc}, retry count: ${retryCount}`); // Check if this is a catbox.moe URL that might be returning 404 if (originalSrc && originalSrc.includes('catbox.moe')) { // Log the failure but don't delete the model (automatic deletion disabled) console.log(`⚠️ Catbox.moe image failed: ${originalSrc} - automatic deletion disabled`); } // Use normal retry logic for all URLs (including catbox.moe) window.continueImageRetry(img, originalSrc, retryCount, maxRetries); }; // Separate function to handle the retry logic window.continueImageRetry = function(img, originalSrc, retryCount, maxRetries) { if (retryCount < maxRetries) { // Increment retry count img.dataset.retryCount = (retryCount + 1).toString(); // Add a small delay before retry to handle temporary network issues setTimeout(() => { console.log(`Retrying image load (attempt ${retryCount + 1}): ${originalSrc}`); // Add cache busting parameter to bypass potential caching issues const cacheBuster = `?retry=${retryCount + 1}&t=${Date.now()}`; img.src = originalSrc + cacheBuster; }, 1000 * (retryCount + 1)); // Exponential backoff: 1s, 2s, 3s } else { // All retries failed, show fallback image console.warn(`Image failed to load after ${maxRetries} retries: ${originalSrc}`); img.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZGRkIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPkltYWdlIE5vdCBGb3VuZDwvdGV4dD48L3N2Zz4='; img.style.opacity = '1'; img.dataset.loaded = 'failed'; } }; // Global video error handler with retry logic window.handleVideoError = function(video, originalSrc) { const retryCount = parseInt(video.dataset.retryCount || '0'); const maxRetries = 2; console.log(`Video load failed for: ${originalSrc}, retry count: ${retryCount}`); if (retryCount < maxRetries) { // Increment retry count video.dataset.retryCount = (retryCount + 1).toString(); // Add a small delay before retry setTimeout(() => { console.log(`Retrying video load (attempt ${retryCount + 1}): ${originalSrc}`); // Add cache busting parameter const cacheBuster = `?retry=${retryCount + 1}&t=${Date.now()}`; const newSrc = originalSrc + cacheBuster; // Update all source elements const sources = video.querySelectorAll('source'); sources.forEach(source => { source.src = newSrc; }); video.load(); // Reload the video }, 1000 * (retryCount + 1)); } else { // All retries failed, fallback to placeholder image console.warn(`Video failed to load after ${maxRetries} retries: ${originalSrc}`); // Hide the video element video.style.display = 'none'; // Create fallback image element const fallbackImg = document.createElement('img'); fallbackImg.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMzMzIi8+PHRleHQgeD0iNTAlIiB5PSI0NSUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPkNvdWxkIG5vdCBsb2FkIHZpZGVvPC90ZXh0Pjx0ZXh0IHg9IjUwJSIgeT0iNjAlIiBmb250LWZhbWlseT0iQXJpYWwsIHNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiM2NjYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGR5PSIuM2VtIj5DbGljayB0byB2aWV3IGNvbnRlbnQ8L3RleHQ+PC9zdmc+'; fallbackImg.className = 'card-image'; fallbackImg.style.opacity = '1'; fallbackImg.dataset.loaded = 'failed'; // Insert the fallback image before the video video.parentNode.insertBefore(fallbackImg, video); } }; window.closePremiumModal = () => { if (window.app) { window.app.closePremiumModal(); } }; window.upgradeToPremium = (plan) => { console.log(`Upgrade to premium: ${plan}`); // All plans redirect to the same Paylix product page - use same tab to avoid popup blocker window.location.href = 'https://paylix.gg/product/688534bfd10a8'; }; window.showRedeemForm = () => { const redeemForm = document.getElementById('redeemForm'); if (redeemForm) { redeemForm.style.display = 'block'; document.getElementById('premiumKeyInput').focus(); } }; window.hideRedeemForm = () => { const redeemForm = document.getElementById('redeemForm'); if (redeemForm) { redeemForm.style.display = 'none'; document.getElementById('premiumKeyInput').value = ''; } }; window.redeemKey = async () => { const keyInput = document.getElementById('premiumKeyInput'); const enteredKey = keyInput.value.trim(); if (!enteredKey) { alert('Please enter a premium key!'); return; } try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.redeemKey}`; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' }, body: JSON.stringify({ key: enteredKey }) }); const result = await response.json(); if (response.ok) { localStorage.setItem('premiumStatus', result.keyType); localStorage.setItem('premiumKey', enteredKey); window.clearPremiumCache(); showSuccessNotification(result.keyType); window.closePremiumModal(); if (window.app) { window.app.clickCount = 0; } console.log(`Premium ${result.keyType} activated with key: ${enteredKey}`); } else { if (result.banned || result.requiresPurchase) { localStorage.removeItem('premiumStatus'); localStorage.removeItem('premiumKey'); window.clearPremiumCache(); window.hideRedeemForm(); window.closePremiumModal(); setTimeout(() => { if (window.app) { window.app.showPremiumModal(); } }, 500); showErrorNotification('This key has been banned. Please purchase premium.'); } else { showErrorNotification(result.error || 'Invalid key'); keyInput.value = ''; keyInput.focus(); } } } catch (error) { console.error('Error redeeming key:', error); showErrorNotification('Network error. Please try again.'); keyInput.focus(); } }; window.premiumStatusCache = { status: null, lastCheck: 0, cacheTimeout: 300000 // Increase cache timeout to 5 minutes }; window.hasPremiumAccess = async () => { const premiumStatus = localStorage.getItem('premiumStatus'); const premiumKey = localStorage.getItem('premiumKey'); if (!premiumStatus) { window.premiumStatusCache.status = false; return false; } const now = Date.now(); if (window.premiumStatusCache.status !== null && (now - window.premiumStatusCache.lastCheck) < window.premiumStatusCache.cacheTimeout) { return window.premiumStatusCache.status; } if (premiumKey) { try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.checkKey}/${encodeURIComponent(premiumKey)}`; const response = await fetch(apiUrl); if (response.ok) { const keyStatus = await response.json(); if (keyStatus.isBanned || !keyStatus.isValid || !keyStatus.isRedeemed) { localStorage.removeItem('premiumStatus'); localStorage.removeItem('premiumKey'); window.premiumStatusCache.status = false; window.premiumStatusCache.lastCheck = now; console.log('Premium access revoked: Key is banned, invalid, or not redeemed'); return false; } window.premiumStatusCache.status = true; window.premiumStatusCache.lastCheck = now; return true; } else { localStorage.removeItem('premiumStatus'); localStorage.removeItem('premiumKey'); window.premiumStatusCache.status = false; window.premiumStatusCache.lastCheck = now; console.log('Premium access revoked: Server validation failed'); return false; } } catch (error) { console.warn('Could not verify key status:', error); localStorage.removeItem('premiumStatus'); localStorage.removeItem('premiumKey'); window.premiumStatusCache.status = false; window.premiumStatusCache.lastCheck = now; console.log('Premium access revoked: Server unreachable'); return false; } } localStorage.removeItem('premiumStatus'); window.premiumStatusCache.status = false; return false; }; window.clearPremiumCache = () => { window.premiumStatusCache.status = null; window.premiumStatusCache.lastCheck = 0; }; window.showSuccessNotification = (keyType) => { const translations = window.app ? window.app.getPremiumTranslations() : {}; const currentLang = window.app ? window.app.currentLanguage : 'tr'; const lang = translations[currentLang] || translations['en'] || {}; const successMessages = { 'tr': { title: 'Premium Başarıyla Etkinleştirildi!', monthly: 'Aylık Premium planınız aktif. Artık sınırsız erişiminiz var!', yearly: 'Yıllık Premium planınız aktif. Artık sınırsız erişiminiz var!', unlimited: 'Sınırsız Erişim' }, 'en': { title: 'Premium Successfully Activated!', monthly: 'Your Monthly Premium plan is active. You now have unlimited access!', yearly: 'Your Yearly Premium plan is active. You now have unlimited access!', unlimited: 'Unlimited Access' }, 'fr': { title: 'Premium Activé avec Succès!', monthly: 'Votre plan Premium mensuel est actif. Vous avez maintenant un accès illimité!', yearly: 'Votre plan Premium annuel est actif. Vous avez maintenant un accès illimité!', unlimited: 'Accès Illimité' }, 'de': { title: 'Premium Erfolgreich Aktiviert!', monthly: 'Ihr monatlicher Premium-Plan ist aktiv. Sie haben jetzt unbegrenzten Zugang!', yearly: 'Ihr jährlicher Premium-Plan ist aktiv. Sie haben jetzt unbegrenzten Zugang!', unlimited: 'Unbegrenzter Zugang' }, 'it': { title: 'Premium Attivato con Successo!', monthly: 'Il tuo piano Premium mensile è attivo. Ora hai accesso illimitato!', yearly: 'Il tuo piano Premium annuale è attivo. Ora hai accesso illimitato!', unlimited: 'Accesso Illimitato' }, 'nl': { title: 'Premium Succesvol Geactiveerd!', monthly: 'Je maandelijkse Premium-plan is actief. Je hebt nu onbeperkte toegang!', yearly: 'Je jaarlijkse Premium-plan is actief. Je hebt nu onbeperkte toegang!', unlimited: 'Onbeperkte Toegang' } }; const messages = successMessages[currentLang] || successMessages['en']; const planMessage = keyType === 'yearly' ? messages.yearly : messages.monthly; const notification = document.createElement('div'); notification.style.cssText = ` position: fixed;top: 50%;left: 50%;transform: translate(-50%,-50%);background: linear-gradient(135deg,#4CAF50 0%,#45a049 100%);color: white;padding: 30px 40px;border-radius: 15px;box-shadow: 0 20px 40px rgba(76,175,80,0.4),0 0 50px rgba(76,175,80,0.3);z-index: 10000;text-align: center;min-width: 400px;max-width: 500px;animation: successSlideIn 0.5s ease;border: 2px solid rgba(255,255,255,0.3);`; notification.innerHTML = `

${messages.title}

${planMessage}

${messages.unlimited}
`; if (!document.getElementById('successAnimationStyles')) { const style = document.createElement('style'); style.id = 'successAnimationStyles'; style.textContent = ` @keyframes successSlideIn {from {opacity: 0;transform: translate(-50%,-50%) scale(0.8);}to {opacity: 1;transform: translate(-50%,-50%) scale(1);}}`; document.head.appendChild(style); } document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.style.animation = 'successSlideIn 0.3s ease reverse'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); } }, 4000); }; window.showErrorNotification = (customMessage = null) => { const translations = window.app ? window.app.getPremiumTranslations() : {}; const currentLang = window.app ? window.app.currentLanguage : 'tr'; const errorMessages = { 'tr': { title: 'Geçersiz Anahtar!', message: 'Lütfen anahtarınızı kontrol edin ve tekrar deneyin.' }, 'en': { title: 'Invalid Key!', message: 'Please check your key and try again.' }, 'fr': { title: 'Clé Invalide!', message: 'Veuillez vérifier votre clé et réessayer.' }, 'de': { title: 'Ungültiger Schlüssel!', message: 'Bitte überprüfen Sie Ihren Schlüssel und versuchen Sie es erneut.' }, 'it': { title: 'Chiave Non Valida!', message: 'Controlla la tua chiave e riprova.' }, 'nl': { title: 'Ongeldige Sleutel!', message: 'Controleer je sleutel en probeer opnieuw.' } }; const messages = errorMessages[currentLang] || errorMessages['en']; const displayMessage = customMessage || messages.message; const notification = document.createElement('div'); notification.style.cssText = ` position: fixed;top: 20px;right: 20px;background: linear-gradient(135deg,#ff4757 0%,#ff3742 100%);color: white;padding: 20px 25px;border-radius: 10px;box-shadow: 0 10px 30px rgba(255,71,87,0.4);z-index: 10000;animation: errorSlideIn 0.3s ease;max-width: 350px;border: 1px solid rgba(255,255,255,0.2);`; notification.innerHTML = `
${messages.title}
${displayMessage}
`; if (!document.getElementById('errorAnimationStyles')) { const style = document.createElement('style'); style.id = 'errorAnimationStyles'; style.textContent = ` @keyframes errorSlideIn {from {transform: translateX(100%);opacity: 0;}to {transform: translateX(0);opacity: 1;}}`; document.head.appendChild(style); } document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.style.animation = 'errorSlideIn 0.3s ease reverse'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); } }, 4000); }; // Global function to override HTML onclick - FIXED to use correct element ID window.toggleUserDropdown = () => { console.log('🌐 Global toggleUserDropdown called'); // Use the correct dropdown element ID from HTML const dropdown = document.getElementById('userDropdownNew'); const arrow = document.getElementById('dropdownArrow'); console.log('🔍 Dropdown element found:', !!dropdown); if (dropdown) { const isVisible = dropdown.style.display === 'block'; const newDisplay = isVisible ? 'none' : 'block'; dropdown.style.display = newDisplay; dropdown.style.zIndex = '99999'; dropdown.style.position = 'absolute'; // Rotate arrow if (arrow) { if (newDisplay === 'block') { arrow.classList.add('rotated'); } else { arrow.classList.remove('rotated'); } } console.log('🔍 Dropdown toggled:', { from: isVisible ? 'block' : 'none', to: newDisplay, zIndex: dropdown.style.zIndex, position: dropdown.style.position }); return true; } else { console.error('❌ Dropdown element userDropdownNew not found!'); return false; } }; document.addEventListener('DOMContentLoaded', async () => { window.app = new leaksdailyApp(); if (window.excelDataLoader) { setTimeout(async () => { if (window.excelDataLoader.isDataLoaded()) { await window.app.renderContent(); } }, 1000); } }); window.leaksdailyUtils = { addContent: (contentItem) => { SITE_CONFIG.contentData.push(contentItem); if (window.app) { window.app.renderContent(); } }, updateConfig: (newConfig) => { Object.assign(SITE_CONFIG, newConfig); if (window.app) { window.app.loadConfiguration(); window.app.renderContent(); } }, getConfig: () => SITE_CONFIG }; window.closeProfileModal = () => { if (window.app) { window.app.closeProfileModal(); } }; window.showRedeemFromProfile = () => { window.closeProfileModal(); if (window.app) { window.app.showPremiumModal(); setTimeout(() => { window.showRedeemForm(); }, 300); } }; window.manageSubscription = () => { console.log('Manage subscription clicked'); const translations = window.app ? window.app.getProfileTranslations() : {}; const currentLang = window.app ? window.app.currentLanguage : 'en'; const messages = { 'tr': 'Abonelik yönetimi yakında eklenecek!', 'en': 'Subscription management coming soon!', 'fr': 'Gestion des abonnements bientôt disponible!', 'de': 'Abonnement-Verwaltung kommt bald!', 'it': 'Gestione abbonamenti in arrivo!', 'nl': 'Abonnementbeheer komt binnenkort!' }; const message = messages[currentLang] || messages['en']; const notification = document.createElement('div'); notification.style.cssText = ` position: fixed;top: 20px;right: 20px;background: linear-gradient(135deg,#4a90e2 0%,#357abd 100%);color: white;padding: 15px 20px;border-radius: 8px;box-shadow: 0 10px 30px rgba(74,144,226,0.3);z-index: 10000;font-weight: 600;animation: slideIn 0.3s ease;`; notification.innerHTML = ` ${message}`; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 3000); }; // Global reset clicks functionality window.resetClicksGlobally = async () => { try { const apiUrl = `${SITE_CONFIG.api.baseUrl}${SITE_CONFIG.api.endpoints.resetClicks || '/api/reset-clicks'}`; const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'Accept': 'application/json' } }); const result = await response.json(); if (response.ok) { // Reset local click count if (window.app) { window.app.clickCount = 0; } // Show success notification const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%); color: white; padding: 20px 25px; border-radius: 12px; box-shadow: 0 10px 30px rgba(76,175,80,0.4); z-index: 10000; font-weight: 600; animation: slideIn 0.3s ease; border: 1px solid rgba(255,255,255,0.2); `; notification.innerHTML = `
Clicks Reset Successfully!
All user click counts have been reset globally.
`; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 5000); console.log('✅ Global click reset successful:', result); } else { throw new Error(result.error || 'Failed to reset clicks'); } } catch (error) { console.error('❌ Error resetting clicks globally:', error); // Show error notification const notification = document.createElement('div'); notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: linear-gradient(135deg, #ff4757 0%, #ff3742 100%); color: white; padding: 20px 25px; border-radius: 12px; box-shadow: 0 10px 30px rgba(255,71,87,0.4); z-index: 10000; font-weight: 600; animation: slideIn 0.3s ease; border: 1px solid rgba(255,255,255,0.2); `; notification.innerHTML = `
Reset Failed
Could not reset clicks. Please try again.
`; document.body.appendChild(notification); setTimeout(() => { if (notification.parentNode) { notification.remove(); } }, 5000); } }; // Add keyboard shortcut for admin reset (Ctrl+Shift+R) document.addEventListener('keydown', (e) => { if (e.ctrlKey && e.shiftKey && e.key === 'R') { e.preventDefault(); const confirmReset = confirm('Are you sure you want to reset all user clicks globally? This action cannot be undone.'); if (confirmReset) { window.resetClicksGlobally(); } } }); // Add method to leaksdailyApp class for resetting clicks window.app && (window.app.resetClicksGlobally = window.resetClicksGlobally);