PageSourceSearch

https://ludeon.netlify.app/src/utils/helpers.js

js ludeon.netlify.app collected 2026-10-03 11:01:48 UTC 5,100 bytes, 191 lines download raw bytes

1/**
2 * Pomocné funkce pro Ludeon aplikaci
3 */
4
5/**
6 * Debounce funkce pro optimalizaci vyhledávání
7 */
8export function debounce(func, wait) {
9    let timeout;
10    return function executedFunction(...args) {
11        const later = () => {
12            clearTimeout(timeout);
13            func(...args);
14        };
15        clearTimeout(timeout);
16        timeout = setTimeout(later, wait);
17    };
18}
19
20/**
21 * Stable poster file key from CSFD URL (e.g. 1661633-resident-evil, 1608318-lanterns).
22 * @param {string} url - CSFD film/episode URL
23 * @param {'entry'|'series'} mode - episodes: 'series' uses parent series segment for shared poster
24 * @returns {string|null}
25 */
26export function extractCsfdPosterKey(url, mode = 'entry') {
27    if (!url || typeof url !== 'string') {
28        return null;
29    }
30
31    const episodeMatch = url.match(/csfd\.cz\/film\/(\d+)-([^/?#]+)\/(\d+)-([^/?#]+)/i);
32    if (episodeMatch) {
33        const [, seriesId, seriesSlug, epId, epSlug] = episodeMatch;
34        if (mode === 'series') {
35            return `${seriesId}-${seriesSlug}`.toLowerCase();
36        }
37        return `${epId}-${epSlug}`.toLowerCase();
38    }
39
40    const filmMatch = url.match(/csfd\.cz\/film\/(\d+)-([^/?#]+)/i);
41    if (filmMatch) {
42        const [, filmId, slug] = filmMatch;
43        return `${filmId}-${slug}`.toLowerCase();
44    }
45
46    return null;
47}
48
49/**
50 * Sanitizace názvu pro URL (vylepšená pro diakritiku)
51 */
52export function sanitizeTitle(title) {
53    return title
54        .toLowerCase()
55        // Normalizace diakritiky
56        .normalize('NFD')
57        .replace(/[\u0300-\u036f]/g, '')
58        // Odstraň speciální znaky, ponech čísla, písmena, mezery a pomlčky
59        .replace(/[^\w\s-]/g, '')
60        // Nahraď mezery pomlčkami
61        .replace(/\s+/g, '-')
62        // Odstraň duplicitní pomlčky
63        .replace(/-+/g, '-')
64        // Odstraň pomlčky na začátku a konci
65        .replace(/^-+|-+$/g, '')
66        .trim();
67}
68
69/**
70 * Zobrazí toast zprávu
71 */
72export function showToast(message, type = 'info', duration = 3000) {
73    // Vytvoř toast element
74    const toast = document.createElement('div');
75    toast.className = `toast toast-${type}`;
76    toast.textContent = message;
77    
78    // Přidej styly
79    Object.assign(toast.style, {
80        position: 'fixed',
81        top: '20px',
82        right: '20px',
83        padding: '12px 20px',
84        borderRadius: '8px',
85        color: 'white',
86        fontSize: '14px',
87        fontWeight: '500',
88        zIndex: '10000',
89        opacity: '0',
90        transform: 'translateX(100%)',
91        transition: 'all 0.3s ease',
92        backgroundColor: type === 'error' ? '#ef4444' : 
93                        type === 'success' ? '#10b981' : '#3b82f6',
94        borderLeft: type === 'success' ? '4px solid #059669' : 
95                   type === 'error' ? '4px solid #dc2626' : '4px solid #2563eb',
96        boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
97    });
98    
99    document.body.appendChild(toast);
100    
101    // Animace vstupu
102    requestAnimationFrame(() => {
103        toast.style.opacity = '1';
104        toast.style.transform = 'translateX(0)';
105    });
106    
107    // Automatické odstranění
108    setTimeout(() => {
109        toast.style.opacity = '0';
110        toast.style.transform = 'translateX(100%)';
111        setTimeout(() => {
112            if (toast.parentNode) {
113                toast.parentNode.removeChild(toast);
114            }
115        }, 300);
116    }, duration);
117}
118
119/**
120 * Logování chyb
121 */
122export function logError(error, context = '') {
123    console.error(`[LudeonApp${context ? ` - ${context}` : ''}]`, error);
124    
125    // Zobraz toast pro uživatele
126    showToast('Došlo k chybě. Zkuste to prosím znovu.', 'error');
127}
128
129/**
130 * Kontrola, zda je element viditelný
131 */
132export function isElementVisible(element) {
133    const rect = element.getBoundingClientRect();
134    return (
135        rect.top >= 0 &&
136        rect.left >= 0 &&
137        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
138        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
139    );
140}
141
142/**
143 * Throttle funkce pro optimalizaci scroll eventů
144 */
145export function throttle(func, limit) {
146    let inThrottle;
147    return function() {
148        const args = arguments;
149        const context = this;
150        if (!inThrottle) {
151            func.apply(context, args);
152            inThrottle = true;
153            setTimeout(() => inThrottle = false, limit);
154        }
155    };
156}
157
158/**
159 * Generování náhodného ID
160 */
161export function generateId() {
162    return Math.random().toString(36).substr(2, 9);
163}
164
165/**
166 * Formátování čísla s mezerami
167 */
168export function formatNumber(num) {
169    return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
170}
171
172/**
173 * Kontrola, zda je mobilní zařízení
174 */
175export function isMobile() {
176    return window.innerWidth <= 768;
177}
178
179/**
180 * Kontrola, zda je tablet
181 */
182export function isTablet() {
183    return window.innerWidth > 768 && window.innerWidth <= 1024;
184}
185
186/**
187 * Kontrola, zda je desktop
188 */
189export function isDesktop() {
190    return window.innerWidth > 1024;
191}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.