1/* lws-fts.js - JS supporting lws fulltext search 2 * 3 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and associated documentation files (the "Software"), to 7 * deal in the Software without restriction, including without limitation the 8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 * sell copies of the Software, and to permit persons to whom the Software is 10 * furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 * IN THE SOFTWARE. 22 */ 23 24 25(function() { 26 27 var last_ac = ""; 28 29 function san(s) 30 { 31 s.replace(/</g, "!"); 32 s.replace(/%/g, "!"); 33 34 return s; 35 } 36 37 function lws_fts_choose() 38 { 39 var xhr = new XMLHttpRequest(); 40 var sr = document.getElementById("searchresults"); 41 var ac = document.getElementById("acomplete"); 42 var inp = document.getElementById("lws_fts"); 43 44 xhr.onopen = function(e) { 45 xhr.setRequestHeader("cache-control", "max-age=0"); 46 }; 47 48 xhr.onload = function(e) { 49 var jj, n, m, s = "", lic = 0; 50 var sr = document.getElementById("searchresults"); 51 var inp = document.getElementById("lws_fts"); 52 sr.style.width = (parseInt(sr.parentNode.offsetWidth, 10) - 88) + "px"; 53 sr.style.opacity = "1"; 54 inp.blur(); 55 56 // console.log(xhr.responseText); 57 jj = JSON.parse(xhr.responseText); 58 59 if (jj.fp) { 60 lic = jj.fp.length; 61 for (n = 0; n < lic; n++) { 62 63 s += "<div class='filepath'>" + jj.fp[n].path + "</div>"; 64 65 s += "<table>"; 66 for (m = 0; m < jj.fp[n].hits.length; m++) 67 s += "<tr><td class='r'>" + jj.fp[n].hits[m].l + 68 "</td><td>" + jj.fp[n].hits[m].s + 69 "</td></tr>"; 70 71 s += "</table>"; 72 73 } 74 } 75 76 sr.innerHTML = s; 77 }; 78 79 inp.blur(); 80 ac.style.opacity = "0"; 81 sr.style.innerHTML = ""; 82 xhr.open("GET", "../fts/r/" + document.getElementById("lws_fts").value); 83 xhr.send(); 84 } 85 86 function lws_fts_ac_select(e) 87 { 88 var t = e.target; 89 90 while (t) { 91 if (t.getAttribute && t.getAttribute("string")) { 92 document.getElementById("lws_fts").value = 93 t.getAttribute("string"); 94 95 lws_fts_choose(); 96 } 97 98 t = t.parentNode; 99 } 100 } 101 102 function lws_fts_search_input() 103 { 104 var ac = document.getElementById("acomplete"), 105 sb = document.getElementById("lws_fts"); 106 107 if (last_ac === sb.value) 108 return; 109 110 last_ac = sb.value; 111 112 ac.style.width = (parseInt(sb.offsetWidth, 10) - 2) + "px"; 113 ac.style.opacity = "1"; 114 115 /* detect loss of focus for popup menu */ 116 sb.addEventListener("focusout", function(e) { 117 ac.style.opacity = "0"; 118 }); 119 120 121 var xhr = new XMLHttpRequest(); 122 123 xhr.onopen = function(e) { 124 xhr.setRequestHeader("cache-control", "max-age=0"); 125 }; 126 xhr.onload = function(e) { 127 var jj, n, s = "", lic = 0; 128 var inp = document.getElementById("lws_fts"); 129 var ac = document.getElementById("acomplete"); 130 131 // console.log(xhr.responseText); 132 jj = JSON.parse(xhr.responseText); 133 134 switch(parseInt(jj.indexed, 10)) { 135 case 0: /* there is no index */ 136 break; 137 138 case 1: /* yay there is an index */ 139 140 if (jj.ac) { 141 lic = jj.ac.length; 142 s += "<ul id='menu-ul'>"; 143 for (n = 0; n < lic; n++) { 144 145 if (jj.ac[n] && parseInt(jj.ac[n].matches, 10)) 146 s += "<li id='mi_ac" + n + "' string='" + 147 san(jj.ac[n].ac) + 148 "'><table><tr><td>" + san(jj.ac[n].ac) + 149 "</td><td class='r'>" + 150 parseInt(jj.ac[n].matches, 10) + 151 "</td></tr></table></li>"; 152 } 153 154 s += "</ul>"; 155 156 if (!lic) { 157 //s = "<img class='noentry'>"; 158 inp.className = "nonviable"; 159 ac.style.opacity = "0"; 160 } else { 161 inp.className = "viable"; 162 ac.style.opacity = "1"; 163 } 164 } 165 166 break; 167 168 default: 169 170 /* an index is being built... */ 171 172 s = "<table><tr><td><img class='spinner'></td><td>" + 173 "<table><tr><td>Indexing</td></tr><tr><td>" + 174 "<div id='bar1' class='bar1'>" + 175 "<div id='bar2' class='bar2'>" + 176 jj.index_done + " / " + jj.index_files + 177 "</div></div></td></tr></table>" + 178 "</td></tr></table>"; 179 180 setTimeout(lws_fts_search_input, 300); 181 182 break; 183 } 184 185 ac.innerHTML = s; 186 187 for (n = 0; n < lic; n++) 188 if (document.getElementById("mi_ac" + n)) 189 document.getElementById("mi_ac" + n). 190 addEventListener("click", lws_fts_ac_select); 191 if (jj.index_files) { 192 document.getElementById("bar2").style.width = 193 ((150 * jj.index_done) / (jj.index_files + 1)) + "px"; 194 } 195 }; 196 197 xhr.open("GET", "../fts/a/" + document.getElementById("lws_fts").value); 198 xhr.send(); 199 } 200 201 document.addEventListener("DOMContentLoaded", function() { 202 var inp = document.getElementById("lws_fts"); 203 204 inp.addEventListener("input", lws_fts_search_input, false); 205 206 inp.addEventListener("keydown", 207 function(e) { 208 var inp = document.getElementById("lws_fts"); 209 var sr = document.getElementById("searchresults"); 210 var ac = document.getElementById("acomplete"); 211 if (e.key === "Enter" && inp.className === "viable") { 212 lws_fts_choose(); 213 sr.focus(); 214 ac.style.opacity = "0"; 215 } 216 }, false); 217 218 }, false); 219 220}()); 221