xref: /plugin/searchindex/SearchIndexManager.js (revision 4773e14500fce3dfff8e66d291ab9dd93ca4eeb4)
1*4773e145SAndreas Gohr/**
2*4773e145SAndreas Gohr * Web component for searchindex manager plugin
3*4773e145SAndreas Gohr *
4*4773e145SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org>
5*4773e145SAndreas Gohr * @author Symon Bent <hendrybadao@gmail.com>
6*4773e145SAndreas Gohr */
7*4773e145SAndreas Gohr
8*4773e145SAndreas Gohrclass SearchIndexManager extends HTMLElement {
9*4773e145SAndreas Gohr    #pages = null;
10*4773e145SAndreas Gohr    #page = null;
11*4773e145SAndreas Gohr    #url = null;
12*4773e145SAndreas Gohr    #done = 1;
13*4773e145SAndreas Gohr    #count = 0;
14*4773e145SAndreas Gohr    #$msg = null;
15*4773e145SAndreas Gohr    #$buttons = null;
16*4773e145SAndreas Gohr    #force = '';
17*4773e145SAndreas Gohr    #lang = {
18*4773e145SAndreas Gohr        rebuild: '*Rebuild Index',
19*4773e145SAndreas Gohr        rebuild_tip: '*Clears the current index and then adds all pages from scratch.',
20*4773e145SAndreas Gohr        update: '*Update Index',
21*4773e145SAndreas Gohr        update_tip: '*Updates the index for any changed pages since the last update.',
22*4773e145SAndreas Gohr        finding: '*Finding pages...',
23*4773e145SAndreas Gohr        pages: '*Found %d pages.',
24*4773e145SAndreas Gohr        indexing: '*Indexing',
25*4773e145SAndreas Gohr        indexed: '*indexed',
26*4773e145SAndreas Gohr        notindexed: '*not indexed',
27*4773e145SAndreas Gohr        done: '*Finished indexing.',
28*4773e145SAndreas Gohr        clearing: '*Clearing index...',
29*4773e145SAndreas Gohr    };
30*4773e145SAndreas Gohr
31*4773e145SAndreas Gohr    connectedCallback() {
32*4773e145SAndreas Gohr        this.#lang = {...this.#lang, ...JSON.parse(this.getAttribute('lang'))};
33*4773e145SAndreas Gohr        this.#url = this.getAttribute('url');
34*4773e145SAndreas Gohr        this.#render();
35*4773e145SAndreas Gohr        this.#$msg = this.querySelector('.msg');
36*4773e145SAndreas Gohr        this.#$buttons = this.querySelector('.buttons');
37*4773e145SAndreas Gohr
38*4773e145SAndreas Gohr        this.querySelector('.rebuild').addEventListener('click', () => this.#rebuild());
39*4773e145SAndreas Gohr        this.querySelector('.update').addEventListener('click', () => this.#update());
40*4773e145SAndreas Gohr    }
41*4773e145SAndreas Gohr
42*4773e145SAndreas Gohr    /**
43*4773e145SAndreas Gohr     * Render the component HTML
44*4773e145SAndreas Gohr     */
45*4773e145SAndreas Gohr    #render() {
46*4773e145SAndreas Gohr        this.innerHTML = `
47*4773e145SAndreas Gohr            <div class="buttons">
48*4773e145SAndreas Gohr                <input type="button" class="button rebuild" value="${this.#lang.rebuild}">
49*4773e145SAndreas Gohr                <p>${this.#lang.rebuild_tip}</p>
50*4773e145SAndreas Gohr                <input type="button" class="button update" value="${this.#lang.update}">
51*4773e145SAndreas Gohr                <p>${this.#lang.update_tip}</p>
52*4773e145SAndreas Gohr            </div>
53*4773e145SAndreas Gohr            <div class="msg"></div>
54*4773e145SAndreas Gohr        `;
55*4773e145SAndreas Gohr    }
56*4773e145SAndreas Gohr
57*4773e145SAndreas Gohr    /**
58*4773e145SAndreas Gohr     * Gives textual feedback
59*4773e145SAndreas Gohr     */
60*4773e145SAndreas Gohr    #message(text) {
61*4773e145SAndreas Gohr        if (text.charAt(0) !== '<') {
62*4773e145SAndreas Gohr            text = `<p>${text}</p>`;
63*4773e145SAndreas Gohr        }
64*4773e145SAndreas Gohr        this.#$msg.innerHTML = text;
65*4773e145SAndreas Gohr    }
66*4773e145SAndreas Gohr
67*4773e145SAndreas Gohr    /**
68*4773e145SAndreas Gohr     * Send a POST request to the ajax endpoint
69*4773e145SAndreas Gohr     */
70*4773e145SAndreas Gohr    async #post(params) {
71*4773e145SAndreas Gohr        const response = await fetch(this.#url, {
72*4773e145SAndreas Gohr            method: 'POST',
73*4773e145SAndreas Gohr            headers: {
74*4773e145SAndreas Gohr                'Content-Type': 'application/x-www-form-urlencoded',
75*4773e145SAndreas Gohr            },
76*4773e145SAndreas Gohr            body: params
77*4773e145SAndreas Gohr        });
78*4773e145SAndreas Gohr        return response.json();
79*4773e145SAndreas Gohr    }
80*4773e145SAndreas Gohr
81*4773e145SAndreas Gohr    /**
82*4773e145SAndreas Gohr     * Starts the indexing of a page.
83*4773e145SAndreas Gohr     */
84*4773e145SAndreas Gohr    async #index() {
85*4773e145SAndreas Gohr        if (this.#page) {
86*4773e145SAndreas Gohr            const indexed = await this.#post(`call=indexpage&page=${encodeURIComponent(this.#page)}&force=${this.#force}`);
87*4773e145SAndreas Gohr            const wait = 250;
88*4773e145SAndreas Gohr            // next page from queue
89*4773e145SAndreas Gohr            this.#page = this.#pages.shift();
90*4773e145SAndreas Gohr            this.#done++;
91*4773e145SAndreas Gohr
92*4773e145SAndreas Gohr            const msg = indexed ? this.#lang.indexed : this.#lang.notindexed;
93*4773e145SAndreas Gohr            const status = `<p class="status">${msg}</p>`;
94*4773e145SAndreas Gohr            this.#message(`<p>${this.#lang.indexing} ${this.#done}/${this.#count}</p><p class="name">${this.#page}</p>${status}`);
95*4773e145SAndreas Gohr            // next index run
96*4773e145SAndreas Gohr            setTimeout(() => this.#index(), wait);
97*4773e145SAndreas Gohr        } else {
98*4773e145SAndreas Gohr            this.#finished();
99*4773e145SAndreas Gohr        }
100*4773e145SAndreas Gohr    }
101*4773e145SAndreas Gohr
102*4773e145SAndreas Gohr    /**
103*4773e145SAndreas Gohr     * Called when indexing is complete
104*4773e145SAndreas Gohr     */
105*4773e145SAndreas Gohr    #finished() {
106*4773e145SAndreas Gohr        this.#throbberOff();
107*4773e145SAndreas Gohr        this.#message(this.#lang.done);
108*4773e145SAndreas Gohr        setTimeout(() => {
109*4773e145SAndreas Gohr            this.#message('');
110*4773e145SAndreas Gohr            this.#$buttons.style.display = '';
111*4773e145SAndreas Gohr        }, 3000);
112*4773e145SAndreas Gohr    }
113*4773e145SAndreas Gohr
114*4773e145SAndreas Gohr    /**
115*4773e145SAndreas Gohr     * Cleans the index (ready for complete rebuild)
116*4773e145SAndreas Gohr     */
117*4773e145SAndreas Gohr    async #clear() {
118*4773e145SAndreas Gohr        this.#message(this.#lang.clearing);
119*4773e145SAndreas Gohr        const success = await this.#post('call=clearindex');
120*4773e145SAndreas Gohr        if (!success) {
121*4773e145SAndreas Gohr            this.#message(this.#lang.clearing + ' - failed, retrying...');
122*4773e145SAndreas Gohr            // retry
123*4773e145SAndreas Gohr            setTimeout(() => this.#clear(), 5000);
124*4773e145SAndreas Gohr        } else {
125*4773e145SAndreas Gohr            // start indexing
126*4773e145SAndreas Gohr            this.#force = 'true';
127*4773e145SAndreas Gohr            setTimeout(() => this.#index(), 1000);
128*4773e145SAndreas Gohr        }
129*4773e145SAndreas Gohr    }
130*4773e145SAndreas Gohr
131*4773e145SAndreas Gohr    /**
132*4773e145SAndreas Gohr     * Starts a full rebuild (clear + reindex)
133*4773e145SAndreas Gohr     */
134*4773e145SAndreas Gohr    #rebuild() {
135*4773e145SAndreas Gohr        this.#update(true);
136*4773e145SAndreas Gohr    }
137*4773e145SAndreas Gohr
138*4773e145SAndreas Gohr    /**
139*4773e145SAndreas Gohr     * Starts the index update
140*4773e145SAndreas Gohr     */
141*4773e145SAndreas Gohr    async #update(rebuild = false) {
142*4773e145SAndreas Gohr        this.#done = 1;
143*4773e145SAndreas Gohr        this.#$buttons.style.display = 'none';
144*4773e145SAndreas Gohr        this.#throbberOn();
145*4773e145SAndreas Gohr        this.#message(this.#lang.finding);
146*4773e145SAndreas Gohr        this.#pages = await this.#post('call=pagelist');
147*4773e145SAndreas Gohr        if (this.#pages.length) {
148*4773e145SAndreas Gohr            this.#count = this.#pages.length;
149*4773e145SAndreas Gohr            this.#message(this.#lang.pages.replace(/%d/, this.#pages.length));
150*4773e145SAndreas Gohr
151*4773e145SAndreas Gohr            // move the first page from the queue
152*4773e145SAndreas Gohr            this.#page = this.#pages.shift();
153*4773e145SAndreas Gohr
154*4773e145SAndreas Gohr            // complete index rebuild?
155*4773e145SAndreas Gohr            if (rebuild === true) {
156*4773e145SAndreas Gohr                this.#clear();
157*4773e145SAndreas Gohr            } else {
158*4773e145SAndreas Gohr                this.#force = '';
159*4773e145SAndreas Gohr                // just start indexing immediately
160*4773e145SAndreas Gohr                setTimeout(() => this.#index(), 1000);
161*4773e145SAndreas Gohr            }
162*4773e145SAndreas Gohr        } else {
163*4773e145SAndreas Gohr            this.#finished();
164*4773e145SAndreas Gohr        }
165*4773e145SAndreas Gohr    }
166*4773e145SAndreas Gohr
167*4773e145SAndreas Gohr    /**
168*4773e145SAndreas Gohr     * Add a throbber image
169*4773e145SAndreas Gohr     */
170*4773e145SAndreas Gohr    #throbberOn() {
171*4773e145SAndreas Gohr        this.#$msg.classList.add('updating');
172*4773e145SAndreas Gohr    }
173*4773e145SAndreas Gohr
174*4773e145SAndreas Gohr    /**
175*4773e145SAndreas Gohr     * Stop the throbber
176*4773e145SAndreas Gohr     */
177*4773e145SAndreas Gohr    #throbberOff() {
178*4773e145SAndreas Gohr        this.#$msg.classList.remove('updating');
179*4773e145SAndreas Gohr    }
180*4773e145SAndreas Gohr}
181*4773e145SAndreas Gohr
182*4773e145SAndreas GohrcustomElements.define('searchindex-manager', SearchIndexManager);
183