|
| 1 | +// Parameters |
| 2 | +var BMAX = 250; // Max blurb size in characters |
| 3 | +var RMAX = 100; // Max number of results to display |
| 4 | + |
| 5 | +function getPositions(result, field) { |
| 6 | + var positions = []; |
| 7 | + var md = result.matchData.metadata; |
| 8 | + for (var searchTerm in md) { |
| 9 | + var fieldResults = md[searchTerm][field]; |
| 10 | + if (!fieldResults) continue; |
| 11 | + for (var p in fieldResults.position) sortedInsert(positions, fieldResults.position[p][0]); |
| 12 | + } |
| 13 | + return positions; |
| 14 | +} |
| 15 | + |
| 16 | +function resultMatched(result, fromfield) { |
| 17 | + let md = result.matchData.metadata; |
| 18 | + for (var st in md) { |
| 19 | + if (!md[st][fromfield]) continue; |
| 20 | + for (var mi in md[st][fromfield]['position']) return true; |
| 21 | + } |
| 22 | + return false; |
| 23 | +} |
| 24 | + |
| 25 | +function addMatchHighlights(result, blurb, fromfield, startindex, endindex) { |
| 26 | + var startindex = startindex || 0; |
| 27 | + var endindex = endindex || blurb.length; |
| 28 | + var ranges = new Ranges(); |
| 29 | + let md = result.matchData.metadata; |
| 30 | + var i = 0; var j = 0; |
| 31 | + for (var searchTerm in md) { |
| 32 | + if (!md[searchTerm][fromfield]) continue; |
| 33 | + for (var mi in md[searchTerm][fromfield].position) { |
| 34 | + let m = md[searchTerm][fromfield].position[mi]; |
| 35 | + i = m[0]; j = i+m[1]; |
| 36 | + if (i < endindex && j > startindex) { |
| 37 | + ranges.add([(startindex>i?startindex:i)-startindex, j-startindex]); |
| 38 | + } |
| 39 | + } |
| 40 | + } |
| 41 | + i = ranges.array.length - 1; |
| 42 | + var ret = blurb; |
| 43 | + while (i >= 0) { |
| 44 | + ret = ret.substring(0,ranges.array[i][0]) + |
| 45 | + '<strong>' + ret.substring(ranges.array[i][0],ranges.array[i][1]) + |
| 46 | + '</strong>' + ret.substring(ranges.array[i][1], ret.length); |
| 47 | + i--; |
| 48 | + } |
| 49 | + return ret; |
| 50 | +} |
| 51 | + |
| 52 | +function getBlurbForResult(result, item, positions) { |
| 53 | + var titleMatch = false; |
| 54 | + let md = result.matchData.metadata; |
| 55 | + for (var searchTerm in md) { |
| 56 | + if (md[searchTerm]['title']) { |
| 57 | + titleMatch = true; |
| 58 | + break; |
| 59 | + } |
| 60 | + } |
| 61 | + if ((titleMatch || positions.length == 0) && item.description){ |
| 62 | + var ret = item.description; |
| 63 | + if (item.description.length > BMAX) |
| 64 | + ret = item.description.substring(0, BMAX) + "..."; |
| 65 | + return addMatchHighlights(result, ret, 'description'); |
| 66 | + } |
| 67 | + // Calculate the best section of the content to blurb |
| 68 | + var best_i = -1; |
| 69 | + var best_n = 0; |
| 70 | + for (var i in positions) { |
| 71 | + var n = 1; |
| 72 | + var j = i+1; |
| 73 | + while (j < positions.length) { |
| 74 | + if (positions[j] > positions[i] + BMAX) break; |
| 75 | + n++; j++; |
| 76 | + } |
| 77 | + if (n > best_n) { |
| 78 | + best_n = n; |
| 79 | + best_i = i; |
| 80 | + } |
| 81 | + } |
| 82 | + i = positions[best_i]; |
| 83 | + j = positions[best_i + best_n - 1]; |
| 84 | + var m = (i+j)>>1; |
| 85 | + var startindex = m - (BMAX>>1); |
| 86 | + var pre = true; |
| 87 | + if (startindex <= 0){ startindex = 0; pre = false; } |
| 88 | + var endindex = startindex + BMAX; |
| 89 | + var ret = item.content.substring(startindex,endindex); |
| 90 | + ret = addMatchHighlights(result, ret, 'content', startindex, endindex); |
| 91 | + if (endindex >= item.content.length) return (pre?'...':'') + ret; |
| 92 | + return (pre?'...':'') + ret + "..."; |
| 93 | +} |
| 94 | + |
| 95 | +function categoryName(c) { |
| 96 | + switch(c) { |
| 97 | + case 'av': return '<i class="fas fa-volume-up"></i> Recording'; |
| 98 | + case 'articles': return '<i class="far fa-newspaper"></i> Article'; |
| 99 | + case 'booklets': return '<i class="fas fa-book-open"></i> Book'; |
| 100 | + case 'monographs': return '<i class="fas fa-book"></i> Book'; |
| 101 | + case 'papers': return '<i class="far fa-file-powerpoint"></i> Paper'; |
| 102 | + case 'essays': return '<i class="far fa-file-word"></i> Essay'; |
| 103 | + case 'canon': return '<i class="fas fa-dharmachakra"></i> Canonical Work'; |
| 104 | + case 'reference': return '<i class="fas fa-atlas"></i> Reference Work'; |
| 105 | + case 'excerpts': return '<i class="fas fa-book-reader"></i> Excerpt'; |
| 106 | + } |
| 107 | + return '<i class="far fa-file"></i> Library Item'; |
| 108 | +} |
| 109 | + |
| 110 | +function displaySearchResult(result, item) { |
| 111 | + var positions = getPositions(result, 'content'); |
| 112 | + var blurb = getBlurbForResult(result, item, positions); |
| 113 | + var type = null; |
| 114 | + switch (item.type) { |
| 115 | + case 'courses': type = '<i class="fas fa-chalkboard"></i> Course'; break; |
| 116 | + case 'content': type = categoryName(item.category); break; |
| 117 | + case 'posts': type = '<i class="fas fa-rss"></i> Blog Post'; break; |
| 118 | + case 'journals': type = '<i class="fas fa-newspaper"></i> Journal'; break; |
| 119 | + case 'authors': type = '<i class="far fa-address-book"></i> Author'; break; |
| 120 | + case 'publishers': type = '<i class="far fa-building"></i> Publisher'; break; |
| 121 | + case 'tags': type = '<i class="fas fa-tag"></i> Bibliography'; break; |
| 122 | + case 'series': type = '<i class="fas fa-list-ol"></i> Series'; break; |
| 123 | + } |
| 124 | + var ret = '<li><h3><a href="' + item.url + '">' + addMatchHighlights(result, item.title, 'title') + '</a></h3>'; |
| 125 | + if (type) ret += '<span class="Counter">' + type + '</span>'; |
| 126 | + var lc = 0; |
| 127 | + for (var i in item.authors) { |
| 128 | + ret += '<span class="Label ml-1">' + |
| 129 | + addMatchHighlights(result, item.authors[i], 'author', lc, lc + item.authors[i].length) + |
| 130 | + '</span>'; |
| 131 | + lc += 2 + item.authors[i].length; |
| 132 | + } |
| 133 | + if (resultMatched(result, 'translator')) ret += '<span class="Label ml-1">Translator: ' + |
| 134 | + addMatchHighlights(result, item.translator, 'translator') + |
| 135 | + '</span>'; |
| 136 | + if (resultMatched(result, 'tag')) { |
| 137 | + lc = 0; |
| 138 | + for (var i in item.tags) { |
| 139 | + if (!item.tags[i]) continue; |
| 140 | + let t = '<span class="Label ml-1 text-capitalize"><i class="fas fa-tag"></i> ' + |
| 141 | + addMatchHighlights(result, item.tags[i].replace('-',' '), 'tag', lc, lc + item.tags[i].length) + '</span>'; |
| 142 | + if (t.includes('<strong>')) ret += t; |
| 143 | + lc += 1 + item.tags[i].length; |
| 144 | + } |
| 145 | + } |
| 146 | + if (resultMatched(result, 'format')) { |
| 147 | + lc = 0; |
| 148 | + for (var i in item.formats) { |
| 149 | + if (!item.formats[i]) continue; |
| 150 | + let t = '<span class="Label ml-1"><i class="fas fa-file-arrow-down"></i> ' + |
| 151 | + addMatchHighlights(result, item.formats[i], 'format', lc, lc + item.formats[i].length) + '</span>'; |
| 152 | + if (t.includes('<strong>')) ret += t; |
| 153 | + lc += 2 + item.formats[i].length; |
| 154 | + } |
| 155 | + } |
| 156 | + return ret + '<p>' + blurb + '</p></li>'; |
| 157 | +} |
| 158 | + |
| 159 | +function displaySearchResults(results) { |
| 160 | + if (results.length) { |
| 161 | + var ret = ''; |
| 162 | + for (var i in results) { |
| 163 | + if (i >= RMAX) break; |
| 164 | + ret += displaySearchResult(results[i], store[results[i].ref]); |
| 165 | + } |
| 166 | + return ret; |
| 167 | + } else { |
| 168 | + return '<li>No results found</li>'; |
| 169 | + } |
| 170 | +} |
| 171 | + |
| 172 | +function handleSearchMessage(data, searchFn) { |
| 173 | + var results = []; |
| 174 | + var warning = ""; |
| 175 | + var words = data.q.trim().split(" "); |
| 176 | + for (var i = 0; i < words.length; i++) { |
| 177 | + const s = words[i].trim(); |
| 178 | + if (!s.startsWith("+") && !s.startsWith("-") && s.length > 1 && lunr.stopWordFilter(s)) { |
| 179 | + words[i] = "+" + s; |
| 180 | + } else { |
| 181 | + words[i] = s; |
| 182 | + } |
| 183 | + } |
| 184 | + var query = words.join(' ').trim(); |
| 185 | + try { |
| 186 | + results = searchFn(query); |
| 187 | + if (!results.length){ |
| 188 | + warning = "<li><strong>No results</strong> found matching all of your terms. Results found matching <em>any</em> term:</li>"; |
| 189 | + results = searchFn(data.q.trim()); |
| 190 | + } |
| 191 | + } catch (err) { |
| 192 | + if (err.message.indexOf("unrecognised field") >= 0 && query.indexOf(":") >= 0) { |
| 193 | + results = searchFn(data.q.replaceAll(":"," ")); |
| 194 | + } else { throw err; } |
| 195 | + } |
| 196 | + if (!results.length){ |
| 197 | + words = data.q.trim().split(" "); |
| 198 | + if (words.find(function(w){ return w.length <= 2; }) == undefined) { |
| 199 | + results = searchFn(words.join("~1 ") + "~1"); |
| 200 | + if (results.length) |
| 201 | + warning = "<li><strong>No results</strong> found for your query. Perhaps you meant:</li>"; |
| 202 | + else |
| 203 | + warning = ""; |
| 204 | + } else { |
| 205 | + warning = ""; |
| 206 | + } |
| 207 | + } |
| 208 | + if (data.filterquery && data.filterquery !== "") { |
| 209 | + var filteredResults = searchFn(data.filterquery); |
| 210 | + results = results.filter(function(result) { |
| 211 | + return filteredResults.some(function(filteredResult) { |
| 212 | + return filteredResult.ref === result.ref; |
| 213 | + }); |
| 214 | + }); |
| 215 | + } |
| 216 | + return { |
| 217 | + "warninghtml": warning, |
| 218 | + "html": displaySearchResults(results), |
| 219 | + "count": results ? results.length : 0, |
| 220 | + "q": data.q, |
| 221 | + "filterquery": data.filterquery, |
| 222 | + "qt": data.qt |
| 223 | + }; |
| 224 | +} |
0 commit comments