1*7f2fe78bSCy Schubert/* 2*7f2fe78bSCy Schubert * searchtools.js 3*7f2fe78bSCy Schubert * ~~~~~~~~~~~~~~~~ 4*7f2fe78bSCy Schubert * 5*7f2fe78bSCy Schubert * Sphinx JavaScript utilities for the full-text search. 6*7f2fe78bSCy Schubert * 7*7f2fe78bSCy Schubert * :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8*7f2fe78bSCy Schubert * :license: BSD, see LICENSE for details. 9*7f2fe78bSCy Schubert * 10*7f2fe78bSCy Schubert */ 11*7f2fe78bSCy Schubert 12*7f2fe78bSCy Schubertif (!Scorer) { 13*7f2fe78bSCy Schubert /** 14*7f2fe78bSCy Schubert * Simple result scoring code. 15*7f2fe78bSCy Schubert */ 16*7f2fe78bSCy Schubert var Scorer = { 17*7f2fe78bSCy Schubert // Implement the following function to further tweak the score for each result 18*7f2fe78bSCy Schubert // The function takes a result array [filename, title, anchor, descr, score] 19*7f2fe78bSCy Schubert // and returns the new score. 20*7f2fe78bSCy Schubert /* 21*7f2fe78bSCy Schubert score: function(result) { 22*7f2fe78bSCy Schubert return result[4]; 23*7f2fe78bSCy Schubert }, 24*7f2fe78bSCy Schubert */ 25*7f2fe78bSCy Schubert 26*7f2fe78bSCy Schubert // query matches the full name of an object 27*7f2fe78bSCy Schubert objNameMatch: 11, 28*7f2fe78bSCy Schubert // or matches in the last dotted part of the object name 29*7f2fe78bSCy Schubert objPartialMatch: 6, 30*7f2fe78bSCy Schubert // Additive scores depending on the priority of the object 31*7f2fe78bSCy Schubert objPrio: {0: 15, // used to be importantResults 32*7f2fe78bSCy Schubert 1: 5, // used to be objectResults 33*7f2fe78bSCy Schubert 2: -5}, // used to be unimportantResults 34*7f2fe78bSCy Schubert // Used when the priority is not in the mapping. 35*7f2fe78bSCy Schubert objPrioDefault: 0, 36*7f2fe78bSCy Schubert 37*7f2fe78bSCy Schubert // query found in title 38*7f2fe78bSCy Schubert title: 15, 39*7f2fe78bSCy Schubert partialTitle: 7, 40*7f2fe78bSCy Schubert // query found in terms 41*7f2fe78bSCy Schubert term: 5, 42*7f2fe78bSCy Schubert partialTerm: 2 43*7f2fe78bSCy Schubert }; 44*7f2fe78bSCy Schubert} 45*7f2fe78bSCy Schubert 46*7f2fe78bSCy Schubertif (!splitQuery) { 47*7f2fe78bSCy Schubert function splitQuery(query) { 48*7f2fe78bSCy Schubert return query.split(/\s+/); 49*7f2fe78bSCy Schubert } 50*7f2fe78bSCy Schubert} 51*7f2fe78bSCy Schubert 52*7f2fe78bSCy Schubert/** 53*7f2fe78bSCy Schubert * Search Module 54*7f2fe78bSCy Schubert */ 55*7f2fe78bSCy Schubertvar Search = { 56*7f2fe78bSCy Schubert 57*7f2fe78bSCy Schubert _index : null, 58*7f2fe78bSCy Schubert _queued_query : null, 59*7f2fe78bSCy Schubert _pulse_status : -1, 60*7f2fe78bSCy Schubert 61*7f2fe78bSCy Schubert htmlToText : function(htmlString) { 62*7f2fe78bSCy Schubert var virtualDocument = document.implementation.createHTMLDocument('virtual'); 63*7f2fe78bSCy Schubert var htmlElement = $(htmlString, virtualDocument); 64*7f2fe78bSCy Schubert htmlElement.find('.headerlink').remove(); 65*7f2fe78bSCy Schubert docContent = htmlElement.find('[role=main]')[0]; 66*7f2fe78bSCy Schubert if(docContent === undefined) { 67*7f2fe78bSCy Schubert console.warn("Content block not found. Sphinx search tries to obtain it " + 68*7f2fe78bSCy Schubert "via '[role=main]'. Could you check your theme or template."); 69*7f2fe78bSCy Schubert return ""; 70*7f2fe78bSCy Schubert } 71*7f2fe78bSCy Schubert return docContent.textContent || docContent.innerText; 72*7f2fe78bSCy Schubert }, 73*7f2fe78bSCy Schubert 74*7f2fe78bSCy Schubert init : function() { 75*7f2fe78bSCy Schubert var params = $.getQueryParameters(); 76*7f2fe78bSCy Schubert if (params.q) { 77*7f2fe78bSCy Schubert var query = params.q[0]; 78*7f2fe78bSCy Schubert $('input[name="q"]')[0].value = query; 79*7f2fe78bSCy Schubert this.performSearch(query); 80*7f2fe78bSCy Schubert } 81*7f2fe78bSCy Schubert }, 82*7f2fe78bSCy Schubert 83*7f2fe78bSCy Schubert loadIndex : function(url) { 84*7f2fe78bSCy Schubert $.ajax({type: "GET", url: url, data: null, 85*7f2fe78bSCy Schubert dataType: "script", cache: true, 86*7f2fe78bSCy Schubert complete: function(jqxhr, textstatus) { 87*7f2fe78bSCy Schubert if (textstatus != "success") { 88*7f2fe78bSCy Schubert document.getElementById("searchindexloader").src = url; 89*7f2fe78bSCy Schubert } 90*7f2fe78bSCy Schubert }}); 91*7f2fe78bSCy Schubert }, 92*7f2fe78bSCy Schubert 93*7f2fe78bSCy Schubert setIndex : function(index) { 94*7f2fe78bSCy Schubert var q; 95*7f2fe78bSCy Schubert this._index = index; 96*7f2fe78bSCy Schubert if ((q = this._queued_query) !== null) { 97*7f2fe78bSCy Schubert this._queued_query = null; 98*7f2fe78bSCy Schubert Search.query(q); 99*7f2fe78bSCy Schubert } 100*7f2fe78bSCy Schubert }, 101*7f2fe78bSCy Schubert 102*7f2fe78bSCy Schubert hasIndex : function() { 103*7f2fe78bSCy Schubert return this._index !== null; 104*7f2fe78bSCy Schubert }, 105*7f2fe78bSCy Schubert 106*7f2fe78bSCy Schubert deferQuery : function(query) { 107*7f2fe78bSCy Schubert this._queued_query = query; 108*7f2fe78bSCy Schubert }, 109*7f2fe78bSCy Schubert 110*7f2fe78bSCy Schubert stopPulse : function() { 111*7f2fe78bSCy Schubert this._pulse_status = 0; 112*7f2fe78bSCy Schubert }, 113*7f2fe78bSCy Schubert 114*7f2fe78bSCy Schubert startPulse : function() { 115*7f2fe78bSCy Schubert if (this._pulse_status >= 0) 116*7f2fe78bSCy Schubert return; 117*7f2fe78bSCy Schubert function pulse() { 118*7f2fe78bSCy Schubert var i; 119*7f2fe78bSCy Schubert Search._pulse_status = (Search._pulse_status + 1) % 4; 120*7f2fe78bSCy Schubert var dotString = ''; 121*7f2fe78bSCy Schubert for (i = 0; i < Search._pulse_status; i++) 122*7f2fe78bSCy Schubert dotString += '.'; 123*7f2fe78bSCy Schubert Search.dots.text(dotString); 124*7f2fe78bSCy Schubert if (Search._pulse_status > -1) 125*7f2fe78bSCy Schubert window.setTimeout(pulse, 500); 126*7f2fe78bSCy Schubert } 127*7f2fe78bSCy Schubert pulse(); 128*7f2fe78bSCy Schubert }, 129*7f2fe78bSCy Schubert 130*7f2fe78bSCy Schubert /** 131*7f2fe78bSCy Schubert * perform a search for something (or wait until index is loaded) 132*7f2fe78bSCy Schubert */ 133*7f2fe78bSCy Schubert performSearch : function(query) { 134*7f2fe78bSCy Schubert // create the required interface elements 135*7f2fe78bSCy Schubert this.out = $('#search-results'); 136*7f2fe78bSCy Schubert this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out); 137*7f2fe78bSCy Schubert this.dots = $('<span></span>').appendTo(this.title); 138*7f2fe78bSCy Schubert this.status = $('<p class="search-summary"> </p>').appendTo(this.out); 139*7f2fe78bSCy Schubert this.output = $('<ul class="search"/>').appendTo(this.out); 140*7f2fe78bSCy Schubert 141*7f2fe78bSCy Schubert $('#search-progress').text(_('Preparing search...')); 142*7f2fe78bSCy Schubert this.startPulse(); 143*7f2fe78bSCy Schubert 144*7f2fe78bSCy Schubert // index already loaded, the browser was quick! 145*7f2fe78bSCy Schubert if (this.hasIndex()) 146*7f2fe78bSCy Schubert this.query(query); 147*7f2fe78bSCy Schubert else 148*7f2fe78bSCy Schubert this.deferQuery(query); 149*7f2fe78bSCy Schubert }, 150*7f2fe78bSCy Schubert 151*7f2fe78bSCy Schubert /** 152*7f2fe78bSCy Schubert * execute search (requires search index to be loaded) 153*7f2fe78bSCy Schubert */ 154*7f2fe78bSCy Schubert query : function(query) { 155*7f2fe78bSCy Schubert var i; 156*7f2fe78bSCy Schubert 157*7f2fe78bSCy Schubert // stem the searchterms and add them to the correct list 158*7f2fe78bSCy Schubert var stemmer = new Stemmer(); 159*7f2fe78bSCy Schubert var searchterms = []; 160*7f2fe78bSCy Schubert var excluded = []; 161*7f2fe78bSCy Schubert var hlterms = []; 162*7f2fe78bSCy Schubert var tmp = splitQuery(query); 163*7f2fe78bSCy Schubert var objectterms = []; 164*7f2fe78bSCy Schubert for (i = 0; i < tmp.length; i++) { 165*7f2fe78bSCy Schubert if (tmp[i] !== "") { 166*7f2fe78bSCy Schubert objectterms.push(tmp[i].toLowerCase()); 167*7f2fe78bSCy Schubert } 168*7f2fe78bSCy Schubert 169*7f2fe78bSCy Schubert if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") { 170*7f2fe78bSCy Schubert // skip this "word" 171*7f2fe78bSCy Schubert continue; 172*7f2fe78bSCy Schubert } 173*7f2fe78bSCy Schubert // stem the word 174*7f2fe78bSCy Schubert var word = stemmer.stemWord(tmp[i].toLowerCase()); 175*7f2fe78bSCy Schubert // prevent stemmer from cutting word smaller than two chars 176*7f2fe78bSCy Schubert if(word.length < 3 && tmp[i].length >= 3) { 177*7f2fe78bSCy Schubert word = tmp[i]; 178*7f2fe78bSCy Schubert } 179*7f2fe78bSCy Schubert var toAppend; 180*7f2fe78bSCy Schubert // select the correct list 181*7f2fe78bSCy Schubert if (word[0] == '-') { 182*7f2fe78bSCy Schubert toAppend = excluded; 183*7f2fe78bSCy Schubert word = word.substr(1); 184*7f2fe78bSCy Schubert } 185*7f2fe78bSCy Schubert else { 186*7f2fe78bSCy Schubert toAppend = searchterms; 187*7f2fe78bSCy Schubert hlterms.push(tmp[i].toLowerCase()); 188*7f2fe78bSCy Schubert } 189*7f2fe78bSCy Schubert // only add if not already in the list 190*7f2fe78bSCy Schubert if (!$u.contains(toAppend, word)) 191*7f2fe78bSCy Schubert toAppend.push(word); 192*7f2fe78bSCy Schubert } 193*7f2fe78bSCy Schubert var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" ")); 194*7f2fe78bSCy Schubert 195*7f2fe78bSCy Schubert // console.debug('SEARCH: searching for:'); 196*7f2fe78bSCy Schubert // console.info('required: ', searchterms); 197*7f2fe78bSCy Schubert // console.info('excluded: ', excluded); 198*7f2fe78bSCy Schubert 199*7f2fe78bSCy Schubert // prepare search 200*7f2fe78bSCy Schubert var terms = this._index.terms; 201*7f2fe78bSCy Schubert var titleterms = this._index.titleterms; 202*7f2fe78bSCy Schubert 203*7f2fe78bSCy Schubert // array of [filename, title, anchor, descr, score] 204*7f2fe78bSCy Schubert var results = []; 205*7f2fe78bSCy Schubert $('#search-progress').empty(); 206*7f2fe78bSCy Schubert 207*7f2fe78bSCy Schubert // lookup as object 208*7f2fe78bSCy Schubert for (i = 0; i < objectterms.length; i++) { 209*7f2fe78bSCy Schubert var others = [].concat(objectterms.slice(0, i), 210*7f2fe78bSCy Schubert objectterms.slice(i+1, objectterms.length)); 211*7f2fe78bSCy Schubert results = results.concat(this.performObjectSearch(objectterms[i], others)); 212*7f2fe78bSCy Schubert } 213*7f2fe78bSCy Schubert 214*7f2fe78bSCy Schubert // lookup as search terms in fulltext 215*7f2fe78bSCy Schubert results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms)); 216*7f2fe78bSCy Schubert 217*7f2fe78bSCy Schubert // let the scorer override scores with a custom scoring function 218*7f2fe78bSCy Schubert if (Scorer.score) { 219*7f2fe78bSCy Schubert for (i = 0; i < results.length; i++) 220*7f2fe78bSCy Schubert results[i][4] = Scorer.score(results[i]); 221*7f2fe78bSCy Schubert } 222*7f2fe78bSCy Schubert 223*7f2fe78bSCy Schubert // now sort the results by score (in opposite order of appearance, since the 224*7f2fe78bSCy Schubert // display function below uses pop() to retrieve items) and then 225*7f2fe78bSCy Schubert // alphabetically 226*7f2fe78bSCy Schubert results.sort(function(a, b) { 227*7f2fe78bSCy Schubert var left = a[4]; 228*7f2fe78bSCy Schubert var right = b[4]; 229*7f2fe78bSCy Schubert if (left > right) { 230*7f2fe78bSCy Schubert return 1; 231*7f2fe78bSCy Schubert } else if (left < right) { 232*7f2fe78bSCy Schubert return -1; 233*7f2fe78bSCy Schubert } else { 234*7f2fe78bSCy Schubert // same score: sort alphabetically 235*7f2fe78bSCy Schubert left = a[1].toLowerCase(); 236*7f2fe78bSCy Schubert right = b[1].toLowerCase(); 237*7f2fe78bSCy Schubert return (left > right) ? -1 : ((left < right) ? 1 : 0); 238*7f2fe78bSCy Schubert } 239*7f2fe78bSCy Schubert }); 240*7f2fe78bSCy Schubert 241*7f2fe78bSCy Schubert // for debugging 242*7f2fe78bSCy Schubert //Search.lastresults = results.slice(); // a copy 243*7f2fe78bSCy Schubert //console.info('search results:', Search.lastresults); 244*7f2fe78bSCy Schubert 245*7f2fe78bSCy Schubert // print the results 246*7f2fe78bSCy Schubert var resultCount = results.length; 247*7f2fe78bSCy Schubert function displayNextItem() { 248*7f2fe78bSCy Schubert // results left, load the summary and display it 249*7f2fe78bSCy Schubert if (results.length) { 250*7f2fe78bSCy Schubert var item = results.pop(); 251*7f2fe78bSCy Schubert var listItem = $('<li></li>'); 252*7f2fe78bSCy Schubert var requestUrl = ""; 253*7f2fe78bSCy Schubert var linkUrl = ""; 254*7f2fe78bSCy Schubert if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') { 255*7f2fe78bSCy Schubert // dirhtml builder 256*7f2fe78bSCy Schubert var dirname = item[0] + '/'; 257*7f2fe78bSCy Schubert if (dirname.match(/\/index\/$/)) { 258*7f2fe78bSCy Schubert dirname = dirname.substring(0, dirname.length-6); 259*7f2fe78bSCy Schubert } else if (dirname == 'index/') { 260*7f2fe78bSCy Schubert dirname = ''; 261*7f2fe78bSCy Schubert } 262*7f2fe78bSCy Schubert requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname; 263*7f2fe78bSCy Schubert linkUrl = requestUrl; 264*7f2fe78bSCy Schubert 265*7f2fe78bSCy Schubert } else { 266*7f2fe78bSCy Schubert // normal html builders 267*7f2fe78bSCy Schubert requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX; 268*7f2fe78bSCy Schubert linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX; 269*7f2fe78bSCy Schubert } 270*7f2fe78bSCy Schubert listItem.append($('<a/>').attr('href', 271*7f2fe78bSCy Schubert linkUrl + 272*7f2fe78bSCy Schubert highlightstring + item[2]).html(item[1])); 273*7f2fe78bSCy Schubert if (item[3]) { 274*7f2fe78bSCy Schubert listItem.append($('<span> (' + item[3] + ')</span>')); 275*7f2fe78bSCy Schubert Search.output.append(listItem); 276*7f2fe78bSCy Schubert setTimeout(function() { 277*7f2fe78bSCy Schubert displayNextItem(); 278*7f2fe78bSCy Schubert }, 5); 279*7f2fe78bSCy Schubert } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { 280*7f2fe78bSCy Schubert $.ajax({url: requestUrl, 281*7f2fe78bSCy Schubert dataType: "text", 282*7f2fe78bSCy Schubert complete: function(jqxhr, textstatus) { 283*7f2fe78bSCy Schubert var data = jqxhr.responseText; 284*7f2fe78bSCy Schubert if (data !== '' && data !== undefined) { 285*7f2fe78bSCy Schubert var summary = Search.makeSearchSummary(data, searchterms, hlterms); 286*7f2fe78bSCy Schubert if (summary) { 287*7f2fe78bSCy Schubert listItem.append(summary); 288*7f2fe78bSCy Schubert } 289*7f2fe78bSCy Schubert } 290*7f2fe78bSCy Schubert Search.output.append(listItem); 291*7f2fe78bSCy Schubert setTimeout(function() { 292*7f2fe78bSCy Schubert displayNextItem(); 293*7f2fe78bSCy Schubert }, 5); 294*7f2fe78bSCy Schubert }}); 295*7f2fe78bSCy Schubert } else { 296*7f2fe78bSCy Schubert // no source available, just display title 297*7f2fe78bSCy Schubert Search.output.append(listItem); 298*7f2fe78bSCy Schubert setTimeout(function() { 299*7f2fe78bSCy Schubert displayNextItem(); 300*7f2fe78bSCy Schubert }, 5); 301*7f2fe78bSCy Schubert } 302*7f2fe78bSCy Schubert } 303*7f2fe78bSCy Schubert // search finished, update title and status message 304*7f2fe78bSCy Schubert else { 305*7f2fe78bSCy Schubert Search.stopPulse(); 306*7f2fe78bSCy Schubert Search.title.text(_('Search Results')); 307*7f2fe78bSCy Schubert if (!resultCount) 308*7f2fe78bSCy Schubert Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.')); 309*7f2fe78bSCy Schubert else 310*7f2fe78bSCy Schubert Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount)); 311*7f2fe78bSCy Schubert Search.status.fadeIn(500); 312*7f2fe78bSCy Schubert } 313*7f2fe78bSCy Schubert } 314*7f2fe78bSCy Schubert displayNextItem(); 315*7f2fe78bSCy Schubert }, 316*7f2fe78bSCy Schubert 317*7f2fe78bSCy Schubert /** 318*7f2fe78bSCy Schubert * search for object names 319*7f2fe78bSCy Schubert */ 320*7f2fe78bSCy Schubert performObjectSearch : function(object, otherterms) { 321*7f2fe78bSCy Schubert var filenames = this._index.filenames; 322*7f2fe78bSCy Schubert var docnames = this._index.docnames; 323*7f2fe78bSCy Schubert var objects = this._index.objects; 324*7f2fe78bSCy Schubert var objnames = this._index.objnames; 325*7f2fe78bSCy Schubert var titles = this._index.titles; 326*7f2fe78bSCy Schubert 327*7f2fe78bSCy Schubert var i; 328*7f2fe78bSCy Schubert var results = []; 329*7f2fe78bSCy Schubert 330*7f2fe78bSCy Schubert for (var prefix in objects) { 331*7f2fe78bSCy Schubert if (!(objects[prefix] instanceof Array)) { 332*7f2fe78bSCy Schubert objects[prefix] = Object.entries(objects[prefix]).map(([name, match]) => [...match, name]); 333*7f2fe78bSCy Schubert } 334*7f2fe78bSCy Schubert for (var iMatch = 0; iMatch != objects[prefix].length; ++iMatch) { 335*7f2fe78bSCy Schubert var match = objects[prefix][iMatch]; 336*7f2fe78bSCy Schubert var name = match[4]; 337*7f2fe78bSCy Schubert var fullname = (prefix ? prefix + '.' : '') + name; 338*7f2fe78bSCy Schubert var fullnameLower = fullname.toLowerCase() 339*7f2fe78bSCy Schubert if (fullnameLower.indexOf(object) > -1) { 340*7f2fe78bSCy Schubert var score = 0; 341*7f2fe78bSCy Schubert var parts = fullnameLower.split('.'); 342*7f2fe78bSCy Schubert // check for different match types: exact matches of full name or 343*7f2fe78bSCy Schubert // "last name" (i.e. last dotted part) 344*7f2fe78bSCy Schubert if (fullnameLower == object || parts[parts.length - 1] == object) { 345*7f2fe78bSCy Schubert score += Scorer.objNameMatch; 346*7f2fe78bSCy Schubert // matches in last name 347*7f2fe78bSCy Schubert } else if (parts[parts.length - 1].indexOf(object) > -1) { 348*7f2fe78bSCy Schubert score += Scorer.objPartialMatch; 349*7f2fe78bSCy Schubert } 350*7f2fe78bSCy Schubert var objname = objnames[match[1]][2]; 351*7f2fe78bSCy Schubert var title = titles[match[0]]; 352*7f2fe78bSCy Schubert // If more than one term searched for, we require other words to be 353*7f2fe78bSCy Schubert // found in the name/title/description 354*7f2fe78bSCy Schubert if (otherterms.length > 0) { 355*7f2fe78bSCy Schubert var haystack = (prefix + ' ' + name + ' ' + 356*7f2fe78bSCy Schubert objname + ' ' + title).toLowerCase(); 357*7f2fe78bSCy Schubert var allfound = true; 358*7f2fe78bSCy Schubert for (i = 0; i < otherterms.length; i++) { 359*7f2fe78bSCy Schubert if (haystack.indexOf(otherterms[i]) == -1) { 360*7f2fe78bSCy Schubert allfound = false; 361*7f2fe78bSCy Schubert break; 362*7f2fe78bSCy Schubert } 363*7f2fe78bSCy Schubert } 364*7f2fe78bSCy Schubert if (!allfound) { 365*7f2fe78bSCy Schubert continue; 366*7f2fe78bSCy Schubert } 367*7f2fe78bSCy Schubert } 368*7f2fe78bSCy Schubert var descr = objname + _(', in ') + title; 369*7f2fe78bSCy Schubert 370*7f2fe78bSCy Schubert var anchor = match[3]; 371*7f2fe78bSCy Schubert if (anchor === '') 372*7f2fe78bSCy Schubert anchor = fullname; 373*7f2fe78bSCy Schubert else if (anchor == '-') 374*7f2fe78bSCy Schubert anchor = objnames[match[1]][1] + '-' + fullname; 375*7f2fe78bSCy Schubert // add custom score for some objects according to scorer 376*7f2fe78bSCy Schubert if (Scorer.objPrio.hasOwnProperty(match[2])) { 377*7f2fe78bSCy Schubert score += Scorer.objPrio[match[2]]; 378*7f2fe78bSCy Schubert } else { 379*7f2fe78bSCy Schubert score += Scorer.objPrioDefault; 380*7f2fe78bSCy Schubert } 381*7f2fe78bSCy Schubert results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]); 382*7f2fe78bSCy Schubert } 383*7f2fe78bSCy Schubert } 384*7f2fe78bSCy Schubert } 385*7f2fe78bSCy Schubert 386*7f2fe78bSCy Schubert return results; 387*7f2fe78bSCy Schubert }, 388*7f2fe78bSCy Schubert 389*7f2fe78bSCy Schubert /** 390*7f2fe78bSCy Schubert * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions 391*7f2fe78bSCy Schubert */ 392*7f2fe78bSCy Schubert escapeRegExp : function(string) { 393*7f2fe78bSCy Schubert return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string 394*7f2fe78bSCy Schubert }, 395*7f2fe78bSCy Schubert 396*7f2fe78bSCy Schubert /** 397*7f2fe78bSCy Schubert * search for full-text terms in the index 398*7f2fe78bSCy Schubert */ 399*7f2fe78bSCy Schubert performTermsSearch : function(searchterms, excluded, terms, titleterms) { 400*7f2fe78bSCy Schubert var docnames = this._index.docnames; 401*7f2fe78bSCy Schubert var filenames = this._index.filenames; 402*7f2fe78bSCy Schubert var titles = this._index.titles; 403*7f2fe78bSCy Schubert 404*7f2fe78bSCy Schubert var i, j, file; 405*7f2fe78bSCy Schubert var fileMap = {}; 406*7f2fe78bSCy Schubert var scoreMap = {}; 407*7f2fe78bSCy Schubert var results = []; 408*7f2fe78bSCy Schubert 409*7f2fe78bSCy Schubert // perform the search on the required terms 410*7f2fe78bSCy Schubert for (i = 0; i < searchterms.length; i++) { 411*7f2fe78bSCy Schubert var word = searchterms[i]; 412*7f2fe78bSCy Schubert var files = []; 413*7f2fe78bSCy Schubert var _o = [ 414*7f2fe78bSCy Schubert {files: terms[word], score: Scorer.term}, 415*7f2fe78bSCy Schubert {files: titleterms[word], score: Scorer.title} 416*7f2fe78bSCy Schubert ]; 417*7f2fe78bSCy Schubert // add support for partial matches 418*7f2fe78bSCy Schubert if (word.length > 2) { 419*7f2fe78bSCy Schubert var word_regex = this.escapeRegExp(word); 420*7f2fe78bSCy Schubert for (var w in terms) { 421*7f2fe78bSCy Schubert if (w.match(word_regex) && !terms[word]) { 422*7f2fe78bSCy Schubert _o.push({files: terms[w], score: Scorer.partialTerm}) 423*7f2fe78bSCy Schubert } 424*7f2fe78bSCy Schubert } 425*7f2fe78bSCy Schubert for (var w in titleterms) { 426*7f2fe78bSCy Schubert if (w.match(word_regex) && !titleterms[word]) { 427*7f2fe78bSCy Schubert _o.push({files: titleterms[w], score: Scorer.partialTitle}) 428*7f2fe78bSCy Schubert } 429*7f2fe78bSCy Schubert } 430*7f2fe78bSCy Schubert } 431*7f2fe78bSCy Schubert 432*7f2fe78bSCy Schubert // no match but word was a required one 433*7f2fe78bSCy Schubert if ($u.every(_o, function(o){return o.files === undefined;})) { 434*7f2fe78bSCy Schubert break; 435*7f2fe78bSCy Schubert } 436*7f2fe78bSCy Schubert // found search word in contents 437*7f2fe78bSCy Schubert $u.each(_o, function(o) { 438*7f2fe78bSCy Schubert var _files = o.files; 439*7f2fe78bSCy Schubert if (_files === undefined) 440*7f2fe78bSCy Schubert return 441*7f2fe78bSCy Schubert 442*7f2fe78bSCy Schubert if (_files.length === undefined) 443*7f2fe78bSCy Schubert _files = [_files]; 444*7f2fe78bSCy Schubert files = files.concat(_files); 445*7f2fe78bSCy Schubert 446*7f2fe78bSCy Schubert // set score for the word in each file to Scorer.term 447*7f2fe78bSCy Schubert for (j = 0; j < _files.length; j++) { 448*7f2fe78bSCy Schubert file = _files[j]; 449*7f2fe78bSCy Schubert if (!(file in scoreMap)) 450*7f2fe78bSCy Schubert scoreMap[file] = {}; 451*7f2fe78bSCy Schubert scoreMap[file][word] = o.score; 452*7f2fe78bSCy Schubert } 453*7f2fe78bSCy Schubert }); 454*7f2fe78bSCy Schubert 455*7f2fe78bSCy Schubert // create the mapping 456*7f2fe78bSCy Schubert for (j = 0; j < files.length; j++) { 457*7f2fe78bSCy Schubert file = files[j]; 458*7f2fe78bSCy Schubert if (file in fileMap && fileMap[file].indexOf(word) === -1) 459*7f2fe78bSCy Schubert fileMap[file].push(word); 460*7f2fe78bSCy Schubert else 461*7f2fe78bSCy Schubert fileMap[file] = [word]; 462*7f2fe78bSCy Schubert } 463*7f2fe78bSCy Schubert } 464*7f2fe78bSCy Schubert 465*7f2fe78bSCy Schubert // now check if the files don't contain excluded terms 466*7f2fe78bSCy Schubert for (file in fileMap) { 467*7f2fe78bSCy Schubert var valid = true; 468*7f2fe78bSCy Schubert 469*7f2fe78bSCy Schubert // check if all requirements are matched 470*7f2fe78bSCy Schubert var filteredTermCount = // as search terms with length < 3 are discarded: ignore 471*7f2fe78bSCy Schubert searchterms.filter(function(term){return term.length > 2}).length 472*7f2fe78bSCy Schubert if ( 473*7f2fe78bSCy Schubert fileMap[file].length != searchterms.length && 474*7f2fe78bSCy Schubert fileMap[file].length != filteredTermCount 475*7f2fe78bSCy Schubert ) continue; 476*7f2fe78bSCy Schubert 477*7f2fe78bSCy Schubert // ensure that none of the excluded terms is in the search result 478*7f2fe78bSCy Schubert for (i = 0; i < excluded.length; i++) { 479*7f2fe78bSCy Schubert if (terms[excluded[i]] == file || 480*7f2fe78bSCy Schubert titleterms[excluded[i]] == file || 481*7f2fe78bSCy Schubert $u.contains(terms[excluded[i]] || [], file) || 482*7f2fe78bSCy Schubert $u.contains(titleterms[excluded[i]] || [], file)) { 483*7f2fe78bSCy Schubert valid = false; 484*7f2fe78bSCy Schubert break; 485*7f2fe78bSCy Schubert } 486*7f2fe78bSCy Schubert } 487*7f2fe78bSCy Schubert 488*7f2fe78bSCy Schubert // if we have still a valid result we can add it to the result list 489*7f2fe78bSCy Schubert if (valid) { 490*7f2fe78bSCy Schubert // select one (max) score for the file. 491*7f2fe78bSCy Schubert // for better ranking, we should calculate ranking by using words statistics like basic tf-idf... 492*7f2fe78bSCy Schubert var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]})); 493*7f2fe78bSCy Schubert results.push([docnames[file], titles[file], '', null, score, filenames[file]]); 494*7f2fe78bSCy Schubert } 495*7f2fe78bSCy Schubert } 496*7f2fe78bSCy Schubert return results; 497*7f2fe78bSCy Schubert }, 498*7f2fe78bSCy Schubert 499*7f2fe78bSCy Schubert /** 500*7f2fe78bSCy Schubert * helper function to return a node containing the 501*7f2fe78bSCy Schubert * search summary for a given text. keywords is a list 502*7f2fe78bSCy Schubert * of stemmed words, hlwords is the list of normal, unstemmed 503*7f2fe78bSCy Schubert * words. the first one is used to find the occurrence, the 504*7f2fe78bSCy Schubert * latter for highlighting it. 505*7f2fe78bSCy Schubert */ 506*7f2fe78bSCy Schubert makeSearchSummary : function(htmlText, keywords, hlwords) { 507*7f2fe78bSCy Schubert var text = Search.htmlToText(htmlText); 508*7f2fe78bSCy Schubert if (text == "") { 509*7f2fe78bSCy Schubert return null; 510*7f2fe78bSCy Schubert } 511*7f2fe78bSCy Schubert var textLower = text.toLowerCase(); 512*7f2fe78bSCy Schubert var start = 0; 513*7f2fe78bSCy Schubert $.each(keywords, function() { 514*7f2fe78bSCy Schubert var i = textLower.indexOf(this.toLowerCase()); 515*7f2fe78bSCy Schubert if (i > -1) 516*7f2fe78bSCy Schubert start = i; 517*7f2fe78bSCy Schubert }); 518*7f2fe78bSCy Schubert start = Math.max(start - 120, 0); 519*7f2fe78bSCy Schubert var excerpt = ((start > 0) ? '...' : '') + 520*7f2fe78bSCy Schubert $.trim(text.substr(start, 240)) + 521*7f2fe78bSCy Schubert ((start + 240 - text.length) ? '...' : ''); 522*7f2fe78bSCy Schubert var rv = $('<p class="context"></p>').text(excerpt); 523*7f2fe78bSCy Schubert $.each(hlwords, function() { 524*7f2fe78bSCy Schubert rv = rv.highlightText(this, 'highlighted'); 525*7f2fe78bSCy Schubert }); 526*7f2fe78bSCy Schubert return rv; 527*7f2fe78bSCy Schubert } 528*7f2fe78bSCy Schubert}; 529*7f2fe78bSCy Schubert 530*7f2fe78bSCy Schubert$(document).ready(function() { 531*7f2fe78bSCy Schubert Search.init(); 532*7f2fe78bSCy Schubert}); 533