PageSourceSearch

https://www.elbil24.no/view-resources/public/common/ReadProgress.js

js elbil24.no collected 2026-10-02 18:25:39 UTC 4,298 bytes, 118 lines download raw bytes

1/**
2 * ReadProgress
3 * Visually indicate remaining length of an article
4 * Input:
5 * - progressElement (dom-element) Element to display reader progress with
6 * - options
7 *   - targetSelectors (array) List of selectors. Children of these elements are used to measure progress.
8 *   - minElementCount (int) Minimum number of elements required to use this functionality.
9 *   - debug (bool) If true the class will print debug-info to the console. If false, only warnings are printed.
10 */
11
12window.Dac = window.Dac || {};
13window.Dac.ReadProgress = class {
14
15    constructor(progressElement, options) {
16        this.debug = !!options.debug;
17        this.log('Creating instance ...');
18        this.progressElement = progressElement;
19        let elements = [];
20        for (const target of (options.targetSelectors || [])) {
21            const targetElement = document.querySelector(target);
22            if (targetElement) {
23                elements = elements.concat(Array.from(targetElement.children).filter((el) => !el.classList.contains('column')));
24            } else {
25                this.log(`Cannot find element for selector "${ target }"`, true);
26            }
27        }
28        if (!elements.length) {
29            this.log(`No elements found. Cannot measure reading-progress`, true);
30            return;
31        }
32        const minElementCount = parseInt(options.minElementCount || 25, 10);
33        if (elements.length < minElementCount) {
34            this.log(`Number of elements (${ elements.length }) is below required count (${ minElementCount }). Skipping.`);
35            return;
36        }
37        let index = 0;
38        this.items = elements.map(el => ({
39            isIntersecting: false,
40            index: index++,
41            element: el
42        }));
43        setTimeout(() => {
44            this.observeElements(elements);
45            this.progressElement.classList.remove('dac-hidden');
46        }, 1000);
47    }
48
49    log(msg, isCritical = false) {
50        if (isCritical) {
51            console.warn('ReadProgress:', msg);
52            return;
53        }
54        if (!this.debug) { return; }
55        console.log('ReadProgress:', msg);
56    }
57
58    getIndex(element) {
59        for (const item of this.items) {
60            if (item.element === element) {
61                return item.index;
62            }
63        }
64        return null;
65    }
66
67    observeElements(items) {
68        this.log(`Will register IntersectionObserver for ${ items.length } elements.`);
69        const options = {
70            // root: root,
71            rootMargin: '0px',
72            threshold: [0.5]
73            // threshold: [0, 0.25, 0.5, 0.75, 1]
74        };
75        let needUpdate = false;
76        const observer = new IntersectionObserver((entries, theObserver) => {
77            for (const entry of entries) {
78                const index = this.getIndex(entry.target);
79                if (this.items[index].isIntersecting !== entry.isIntersecting) {
80                    this.items[index].isIntersecting = entry.isIntersecting;
81                    needUpdate = true;
82                }
83            }
84            if (needUpdate) {
85                this.updateGui();
86            }
87        }, options);
88        for (const item of items) {
89            observer.observe(item);
90        }
91    }
92
93    // Update nav-elements based on visibility of first / last item
94    updateGui() {
95        const itemCount = this.items.length;
96        let firstVisible = -1;
97        let firstInvisible = -1;
98        for (const item of this.items) {
99            const index = this.items.indexOf(item);
100            if (firstVisible === -1 && item.isIntersecting) {
101                firstVisible = index;
102            }
103            if (firstInvisible === -1 && firstVisible > -1 && !item.isIntersecting) {
104                firstInvisible = index;
105                this.updateGuiByRange(firstVisible, firstInvisible, itemCount);
106                return;
107            }
108        }
109    }
110
111    updateGuiByRange(start, end, count) {
112        const endIndex = (end > -1 ? end : count) + 1;
113        const percent = Number((endIndex / count).toFixed(2));
114        this.log(`Found progress-value of ${ percent }. Start-index: ${ start }, end-index: ${ endIndex }. Will update GUI.`);
115        this.progressElement.style.transform = (`scaleX(${ percent })`);
116    }
117
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.