PageSourceSearch

https://www.melvil.cz/wp-content/plugins/melvil-filter/assets/js/html-cache-manager.js?ver=1790242497

js melvil.cz collected 2026-09-24 09:36:10 UTC 12,757 bytes, 381 lines download raw bytes

1/**
2 * HTML Cache Manager - Cache Storage version
3 */
4class HtmlCacheManager {
5    constructor() {
6        this.cacheName = 'melvil_html_cache';
7        this.metaKey = 'html-meta';
8        this.expiryMs = 24 * 60 * 60 * 1000; // 24 hours
9        this.backgroundSyncInProgress = false;
10        this.backgroundSyncCompleted = false;
11        this.backgroundSyncStartTime = null;
12        this.backgroundSyncEndTime = null;
13        this.serverCacheVersion = melvil_filter.cache_version || 0;
14    }
15
16    // Open cache
17    async getCache() {
18        return await caches.open(this.cacheName);
19    }
20
21    // Get meta (timestamp)
22    async getMeta() {
23        const cache = await this.getCache();
24        const response = await cache.match(this.metaKey);
25        if (response) {
26            const meta = await response.json();
27            return meta;
28        }
29        return null;
30    }
31
32    // Set meta (timestamp)
33    async setMeta(meta) {
34        const cache = await this.getCache();
35        await cache.put(
36            this.metaKey,
37            new Response(JSON.stringify(meta), { headers: { 'Content-Type': 'application/json' } })
38        );
39    }
40
41    // Check if cache is expired
42    async isExpired() {
43        const meta = await this.getMeta();
44
45        if (!meta || !meta.timestamp) {
46
47            return true;
48        }
49
50        // Check if server cache version is newer than cached version
51        if (this.serverCacheVersion > 0 && meta.cacheVersion !== this.serverCacheVersion) {
52
53            return true;
54        }
55
56        const isTimeExpired = (Date.now() - meta.timestamp) > this.expiryMs;
57        if (isTimeExpired) {
58        }
59
60        return isTimeExpired;
61    }
62
63    // Clear HTML cards cache (keeps product config intact)
64    async clearCache() {
65        const cache = await this.getCache();
66        const keys = await cache.keys();
67		for (const request of keys) {
68            const url = request.url.split('/').pop();
69            if (url && (url.startsWith('product-card-') || url === this.metaKey)) {
70                await cache.delete(request);
71            }
72        }
73
74        // Automaticky načti všechny karty znovu na pozadí
75        this.loadAllCards(true);
76    }
77
78    // Save HTML card
79    async setCachedHtml(productId, html) {
80        const cache = await this.getCache();
81        const cacheKey = new Request(`/product-card-${productId}`, { method: 'GET' });
82        await cache.put(
83            cacheKey,
84            new Response(html, { headers: { 'Content-Type': 'text/html' } })
85        );
86        // Update meta timestamp and cache version
87        await this.setMeta({
88            timestamp: Date.now(),
89            cacheVersion: this.serverCacheVersion
90        });
91    }
92
93    // Get HTML card
94    async getCachedHtml(productId) {
95        const cache = await this.getCache();
96        const cacheKey = new Request(`/product-card-${productId}`, { method: 'GET' });
97        const response = await cache.match(cacheKey);
98        if (response) {
99            return await response.text();
100        }
101        return null;
102    }
103
104    // Check if card is cached
105    async isCached(productId) {
106        const cache = await this.getCache();
107        const cacheKey = new Request(`/product-card-${productId}`, { method: 'GET' });
108        const response = await cache.match(cacheKey);
109        return !!response;
110    }
111
112    // Get all cached product IDs
113    async getCachedProductIds() {
114        const cache = await this.getCache();
115        const keys = await cache.keys();
116        return keys
117            .map(req => req.url.split('/').pop())
118            .filter(key => key && key.startsWith('product-card-'))
119            .map(key => key.replace('product-card-', ''));
120    }
121
122    // Get missing product IDs from config
123    async getMissingProductIds() {
124        // Wait for product config to be ready
125        const cacheManager = window.melvilProductCache;
126        if (!cacheManager) {
127            return [];
128        }
129        const productsData = await cacheManager.getCachedData();
130        if (!productsData || !productsData.products) {
131            return [];
132        }
133        const allProductIds = Object.keys(productsData.products);
134        const cachedProductIds = await this.getCachedProductIds();
135        // Find missing product IDs
136        const missingIds = allProductIds.filter(id => !cachedProductIds.includes(id));
137
138        return missingIds;
139    }
140
141    // Load missing HTML cards from server
142    async loadMissingCards(missingIds = null, isBackgroundSync = false) {
143        // Pokud není zadáno, zjisti chybějící ID sám
144        if (!missingIds) {
145            missingIds = await this.getMissingProductIds();
146        }
147        if (missingIds.length === 0) {
148            return { success: true, loaded: 0, total: 0 };
149        }
150        
151        if (isBackgroundSync) {
152            this.backgroundSyncInProgress = true;
153            this.backgroundSyncStartTime = Date.now();
154        } else {
155        }
156        
157        try {
158            const response = await fetch(melvil_filter.rest_url + 'melvil-filter/v1/load-html-cache/', {
159                method: 'POST',
160                headers: {
161                    'X-WP-Nonce': melvil_filter.rest_nonce,
162                    'Content-Type': 'application/json'
163                },
164                body: JSON.stringify(missingIds)
165            });
166            if (!response.ok) {
167                throw new Error(`HTTP error! status: ${response.status}`);
168            }
169            const data = await response.json();
170            if (data.success && data.html_data) {
171                let cachedCount = 0;
172                for (const productId in data.html_data) {
173                    await this.setCachedHtml(productId, data.html_data[productId]);
174                    cachedCount++;
175                }
176                
177                if (isBackgroundSync) {
178                    this.backgroundSyncInProgress = false;
179                    this.backgroundSyncCompleted = true;
180                    this.backgroundSyncEndTime = Date.now();
181                } else {
182                }
183                
184                return {
185                    success: true,
186                    loaded: cachedCount,
187                    total: missingIds.length,
188                    execution_time: data.execution_time_ms
189                };
190            } else {
191                throw new Error('Invalid response from server');
192            }
193        } catch (error) {
194            if (isBackgroundSync) {
195                this.backgroundSyncInProgress = false;
196                this.backgroundSyncCompleted = false;
197            }
198
199            return { success: false, error: error.message };
200        }
201    }
202
203    // Get HTML for specific product (from cache only, background sync handles missing cards)
204    async getProductHtml(productId) {
205        if (await this.isCached(productId)) {
206            const html = await this.getCachedHtml(productId);
207            return html;
208        }
209        // Card not in cache - background sync will handle it
210        return null;
211    }
212
213    // Get HTML for multiple products
214    async getProductsHtml(productIds) {
215        const results = {};
216        for (const productId of productIds) {
217            results[productId] = await this.getProductHtml(productId);
218        }
219        return results;
220    }
221
222    // Get cache statistics
223    async getCacheStats() {
224        const cachedIds = await this.getCachedProductIds();
225        let totalSize = 0;
226        for (const productId of cachedIds) {
227            const html = await this.getCachedHtml(productId);
228            totalSize += html ? html.length : 0;
229        }
230        return {
231            cached_count: cachedIds.length,
232            total_size: this.formatBytes(totalSize),
233            average_size: cachedIds.length > 0 ? this.formatBytes(totalSize / cachedIds.length) : '0 Bytes'
234        };
235    }
236
237    // Check if background sync is in progress
238    isBackgroundSyncInProgress() {
239        return this.backgroundSyncInProgress;
240    }
241
242    // Check if background sync is completed
243    isBackgroundSyncCompleted() {
244        return this.backgroundSyncCompleted;
245    }
246
247    // Get background sync status
248    getBackgroundSyncStatus() {
249        return {
250            inProgress: this.backgroundSyncInProgress,
251            completed: this.backgroundSyncCompleted,
252            startTime: this.backgroundSyncStartTime,
253            endTime: this.backgroundSyncEndTime,
254            duration: this.backgroundSyncEndTime ? this.backgroundSyncEndTime - this.backgroundSyncStartTime : null
255        };
256    }
257
258    // Bulk load all cards from server (one GET request)
259    async loadAllCards(isBackgroundSync = false) {
260        if (isBackgroundSync) {
261            this.backgroundSyncInProgress = true;
262            this.backgroundSyncStartTime = Date.now();
263        }
264
265        try {
266            const response = await fetch(melvil_filter.rest_url + 'melvil-filter/v1/load-html-cache/', {
267                method: 'GET',
268                headers: {
269                    'X-WP-Nonce': melvil_filter.rest_nonce
270                }
271            });
272            if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
273            const data = await response.json();
274
275            if (data.success && data.html_data) {
276                let loadedCount = 0;
277                for (const productId in data.html_data) {
278                    await this.setCachedHtml(productId, data.html_data[productId]);
279                    loadedCount++;
280                }
281
282                if (isBackgroundSync) {
283                    this.backgroundSyncInProgress = false;
284                    this.backgroundSyncCompleted = true;
285                    this.backgroundSyncEndTime = Date.now();
286                }
287                return { success: true, loaded: loadedCount, total: data.total_products };
288            }
289            throw new Error('Invalid response from server');
290        } catch (error) {
291            if (isBackgroundSync) {
292                this.backgroundSyncInProgress = false;
293                this.backgroundSyncCompleted = false;
294            }
295
296            return { success: false, error: error.message };
297        }
298    }
299
300    // Format bytes to human readable format
301    formatBytes(bytes) {
302        if (bytes === 0) return '0 Bytes';
303        const k = 1024;
304        const sizes = ['Bytes', 'KB', 'MB', 'GB'];
305        const i = Math.floor(Math.log(bytes) / Math.log(k));
306        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
307    }
308}
309
310// Inicializace při načtení stránky
311// Automaticky načte kompletní cache na pozadí
312document.addEventListener('DOMContentLoaded', function() {
313    const initHtmlCacheManager = async () => {
314        const cacheManager = window.melvilProductCache;
315        if (cacheManager) {
316            try {
317                await cacheManager.getReadyPromise();
318                window.melvilHtmlCacheManager = new HtmlCacheManager();
319
320                // Zkontroluj platnost cache na pozadí
321                setTimeout(async () => {
322                    try {
323                        const isExpired = await window.melvilHtmlCacheManager.isExpired();
324                        if (isExpired) {
325                            await window.melvilHtmlCacheManager.clearCache();
326                        }
327                    } catch (error) {
328                    }
329                }, 1000);
330
331            } catch (error) {
332            }
333        } else {
334            setTimeout(initHtmlCacheManager, 500);
335        }
336    };
337    initHtmlCacheManager();
338});
339
340// Globální funkce pro debug
341window.getHtmlCacheStats = async function() {
342    if (window.melvilHtmlCacheManager) {
343        return await window.melvilHtmlCacheManager.getCacheStats();
344    }
345    return null;
346};
347
348window.clearHtmlCache = async function() {
349    if (window.melvilHtmlCacheManager) {
350        await window.melvilHtmlCacheManager.clearCache();
351    }
352};
353
354window.syncHtmlCache = async function() {
355    if (window.melvilHtmlCacheManager) {
356        return await window.melvilHtmlCacheManager.loadMissingCards();
357    }
358    return Promise.reject('HTML Cache Manager not available');
359};
360
361// Globální funkce pro kontrolu stavu background sync
362window.getHtmlCacheBackgroundSyncStatus = function() {
363    if (window.melvilHtmlCacheManager) {
364        return window.melvilHtmlCacheManager.getBackgroundSyncStatus();
365    }
366    return null;
367};
368
369window.isHtmlCacheBackgroundSyncInProgress = function() {
370    if (window.melvilHtmlCacheManager) {
371        return window.melvilHtmlCacheManager.isBackgroundSyncInProgress();
372    }
373    return false;
374};
375
376window.isHtmlCacheBackgroundSyncCompleted = function() {
377    if (window.melvilHtmlCacheManager) {
378        return window.melvilHtmlCacheManager.isBackgroundSyncCompleted();
379    }
380    return false;
381}; 

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.