PageSourceSearch

https://imgmin.pro/analytics.js?v=20260924a

js imgmin.pro collected 2026-10-02 01:08:15 UTC 6,104 bytes, 118 lines download raw bytes

1/* ImgMin first-party analytics: anonymous, allowlisted and same-origin only. */
2(function () {
3    'use strict';
4
5    const SESSION_KEY = 'imgmin_analytics_session_v1';
6    const LOCAL_HOSTS = new Set(['localhost', '127.0.0.1']);
7    const EVENT_SURFACES = {
8        tool_view: 'compressor', extension_cta_clicked: 'compressor', upload_intent: 'compressor',
9        files_accepted: 'compressor', advanced_settings_opened: 'compressor', task_start: 'compressor',
10        process_success: 'compressor', process_fail: 'compressor', target_met: 'compressor', export_triggered: 'compressor',
11        settings_adjusted: 'compressor', rule_saved: 'compressor', rule_reused: 'compressor', rule_shared: 'compressor',
12        bg_tool_view: 'background_remove', bg_upload_intent: 'background_remove', bg_file_accepted: 'background_remove',
13        bg_run_start: 'background_remove', bg_result: 'background_remove', bg_error: 'background_remove', bg_export: 'background_remove'
14    };
15    let queue = [];
16    let flushTimer = null;
17
18    function uuid() {
19        if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
20        const bytes = globalThis.crypto?.getRandomValues ? globalThis.crypto.getRandomValues(new Uint8Array(16)) : Array.from({ length: 16 }, () => Math.floor(Math.random() * 256));
21        bytes[6] = (bytes[6] & 0x0f) | 0x40;
22        bytes[8] = (bytes[8] & 0x3f) | 0x80;
23        return [...bytes].map((value, index) => [4, 6, 8, 10].includes(index) ? `-${value.toString(16).padStart(2, '0')}` : value.toString(16).padStart(2, '0')).join('');
24    }
25
26    function isOptedOut() {
27        return navigator.globalPrivacyControl === true || navigator.doNotTrack === '1' || window.doNotTrack === '1';
28    }
29
30    function enabled() {
31        return !isOptedOut() && (!LOCAL_HOSTS.has(location.hostname) || typeof window.__IMGMIN_ANALYTICS_TEST__ === 'function');
32    }
33
34    function sessionId() {
35        try {
36            let id = sessionStorage.getItem(SESSION_KEY);
37            if (!id) { id = uuid(); sessionStorage.setItem(SESSION_KEY, id); }
38            return id;
39        } catch (_) {
40            return uuid();
41        }
42    }
43
44    function language() {
45        const value = (document.documentElement.lang || 'en').toLowerCase();
46        if (value.startsWith('zh-hant')) return 'zh-Hant';
47        if (value.startsWith('zh')) return 'zh-CN';
48        if (value.startsWith('fr')) return 'fr-FR';
49        if (value.startsWith('de')) return 'de-DE';
50        if (value.startsWith('es')) return 'es-ES';
51        if (value.startsWith('ja')) return 'ja-JP';
52        return 'en-US';
53    }
54
55    function page() {
56        if (location.pathname.includes('remove-background')) return 'remove_background';
57        if (location.pathname.includes('compress-to-')) return 'target_size';
58        if (location.pathname.includes('/convert/')) return 'format_conversion';
59        const params = new URLSearchParams(location.search);
60        if (params.has('targetKB')) return 'target_size';
61        if (params.get('format') && params.get('format') !== 'auto') return 'format_conversion';
62        return 'home';
63    }
64
65    function safeProperties(name, input) {
66        input = input || {};
67        const task = () => ({ task_type: input.task_type, output_format: input.output_format, target_bucket: input.target_bucket });
68        if (['tool_view', 'extension_cta_clicked', 'advanced_settings_opened', 'settings_adjusted', 'rule_saved', 'rule_reused', 'rule_shared', 'bg_tool_view'].includes(name)) return {};
69        if (['upload_intent', 'bg_upload_intent', 'bg_file_accepted'].includes(name)) return { method: input.method };
70        if (name === 'files_accepted') return { method: input.method, count_bucket: input.count_bucket };
71        if (name === 'process_success') return { ...task(), duration_bucket: input.duration_bucket };
72        if (['task_start', 'target_met'].includes(name)) return task();
73        if (name === 'process_fail') return { ...task(), reason: input.reason };
74        if (name === 'export_triggered') return { ...task(), method: input.method, export_format: input.export_format, target_met: input.target_met };
75        if (name === 'bg_run_start') return { tier: input.tier };
76        if (name === 'bg_result') return { tier: input.tier, downscaled: input.downscaled };
77        if (name === 'bg_error') return { reason: input.reason };
78        if (name === 'bg_export') return { method: input.method, tier: input.tier };
79        return {};
80    }
81
82    function scheduleFlush() {
83        if (flushTimer || !queue.length) return;
84        flushTimer = window.setTimeout(() => flush(false), 800);
85    }
86
87    function flush(unloading) {
88        window.clearTimeout(flushTimer);
89        flushTimer = null;
90        if (!queue.length) return;
91        const events = queue.splice(0, 20);
92        const body = JSON.stringify({ events });
93        if (unloading && navigator.sendBeacon) {
94            navigator.sendBeacon('/api/analytics/events', new Blob([body], { type: 'application/json' }));
95        } else {
96            fetch('/api/analytics/events', { method: 'POST', headers: { 'content-type': 'application/json' }, body, keepalive: true, credentials: 'same-origin' })
97                .catch(() => { /* Analytics must never affect local image processing. */ });
98        }
99        if (queue.length) scheduleFlush();
100    }
101
102    function track(name, properties) {
103        if (!enabled() || !Object.prototype.hasOwnProperty.call(EVENT_SURFACES, name)) return;
104        const event = {
105            event_id: uuid(), session_id: sessionId(), name, surface: EVENT_SURFACES[name],
106            page: page(), language: language(), properties: safeProperties(name, properties)
107        };
108        if (typeof window.__IMGMIN_ANALYTICS_TEST__ === 'function') {
109            window.__IMGMIN_ANALYTICS_TEST__(event);
110            return;
111        }
112        queue.push(event);
113        if (queue.length >= 20) flush(false); else scheduleFlush();
114    }
115
116    window.addEventListener('pagehide', () => flush(true));
117    window.imgminAnalytics = { track, flush: () => flush(false), enabled };
118})();

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.