PageSourceSearch

https://www.zaltbommel.nl/framework/js/Search/analytics.js

js zaltbommel.nl collected 2026-09-24 22:50:05 UTC 3,554 bytes, 130 lines download raw bytes

1import { isQueryValid } from './validator.js';
2
3export const IS_REDIRECT_URL_PARAM = 'fsb';
4export const IS_REDIRECT_VALUE = '1';
5
6export class AnalyticsOptions {
7    constructor(maxQueryLength) {
8        this.maxQueryLength = maxQueryLength;
9    }
10}
11
12// Analytics
13export class AnalyticsManager {
14    constructor(delay, siteId, options) {
15        this.delay = delay;
16        this.siteId = siteId;
17        this.options = options ?? new AnalyticsOptions(255);
18
19        this.hits = 0;
20        this.query = null;
21        this.skipAnalyticOnce = sessionStorage.getItem(IS_REDIRECT_URL_PARAM) === IS_REDIRECT_VALUE;
22
23        this.debouncedAnalytics = this._debounceAnalytic(this.sendSearchAnalyticEvent.bind(this), this.delay);
24    }
25
26    updateQuery(query) {
27        this.query = query;
28    }
29
30    updateHits(hits) {
31        this.hits = hits;
32    }
33
34    // Logic
35    setShouldSkipForNextInit() {
36        sessionStorage.setItem(IS_REDIRECT_URL_PARAM, IS_REDIRECT_VALUE);
37    }
38
39    clearShouldSkip() {
40        sessionStorage.removeItem(IS_REDIRECT_URL_PARAM);
41    }
42
43    flush() {
44        this.debouncedAnalytics.flush();
45    }
46
47    // Util
48    _debounceAnalytic(fn, time) {
49        let timerId = undefined;
50        let lastArgs = [];
51
52        const debounced = function (...args) {
53            lastArgs = args;
54            if (timerId) {
55                clearTimeout(timerId);
56            }
57
58            timerId = setTimeout(() => {
59                fn(...lastArgs); // calls sendSearchAnalyticEvent
60                timerId = undefined;
61            }, time);
62        };
63
64        debounced.flush = () => {
65            if (timerId) {
66                clearTimeout(timerId);
67                fn(...lastArgs); // calls sendSearchAnalyticEvent
68                timerId = undefined;
69            }
70        };
71
72        return debounced;
73    }
74
75    // Indirect
76    debounceAnalytic(url) {
77        this.debouncedAnalytics(url);
78    }
79
80    // Direct
81    async sendSearchAnalyticEvent(url) {
82        await this._sendSearchAnalyticEvent(url, this.query, this.hits !== 0);
83    }
84
85    async _sendSearchAnalyticEvent(url, query, hasResults) {
86        try {
87            if (isQueryValid(query)) {
88                return;
89            }
90
91            const params = { siteId: this.siteId, type: 'query', query: this.parseQuery(query), hasResult: hasResults ? 'true' : 'false' };
92            const formData = new FormData();
93            for (const key in params) {
94                formData.append(key, params[key]);
95            }
96
97            await this._sendAnalytic(url, formData);
98        } catch (error) {
99            console.error('Could not send search analytic:', error);
100        }
101    }
102
103    async sendClickAnalyticEvent(url, query, searchIndexId) {
104        try {
105            if (isQueryValid(query)) {
106                return;
107            }
108
109            const params = { siteId: this.siteId, type: 'click', query: this.parseQuery(query), searchIndexId: searchIndexId };
110            const formData = new FormData();
111            for (const key in params) {
112                formData.append(key, params[key]);
113            }
114
115            await this._sendAnalytic(url, formData);
116        } catch (error) {
117            console.error(error);
118            console.error('Could not send click analytic');
119        }
120    }
121
122    async _sendAnalytic(url, formData) {
123        // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon
124        navigator.sendBeacon(url, formData);
125    }
126
127    parseQuery(query) {
128        return query.slice(0, this.options.maxQueryLength);
129    }
130}

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.