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}
${currentLangData.name}
Türkçe
English
Français
Deutsch
Italiano
Nederlands
`;
}
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
` : '';
return `
${editorsBadge}
${item.title}
${item.stats.size}
${item.stats.photos}
${item.stats.videos}
${timeAgo}
${downloadsTime}
@${item.creator || 'leaksdaily'}
`;
}
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,
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 = `