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