Blame | Letzte Änderung | Log anzeigen | RSS feed
/** searchtools.js* ~~~~~~~~~~~~~~~~** Sphinx JavaScript utilities for the full-text search.** :copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.* :license: BSD, see LICENSE for details.**/if (!Scorer) {/*** Simple result scoring code.*/var Scorer = {// Implement the following function to further tweak the score for each result// The function takes a result array [filename, title, anchor, descr, score]// and returns the new score./*score: function(result) {return result[4];},*/// query matches the full name of an objectobjNameMatch: 11,// or matches in the last dotted part of the object nameobjPartialMatch: 6,// Additive scores depending on the priority of the objectobjPrio: {0: 15, // used to be importantResults1: 5, // used to be objectResults2: -5}, // used to be unimportantResults// Used when the priority is not in the mapping.objPrioDefault: 0,// query found in titletitle: 15,partialTitle: 7,// query found in termsterm: 5,partialTerm: 2};}if (!splitQuery) {function splitQuery(query) {return query.split(/\s+/);}}/*** Search Module*/var Search = {_index : null,_queued_query : null,_pulse_status : -1,htmlToText : function(htmlString) {var htmlElement = document.createElement('span');htmlElement.innerHTML = htmlString;$(htmlElement).find('.headerlink').remove();docContent = $(htmlElement).find('[role=main]')[0];if(docContent === undefined) {console.warn("Content block not found. Sphinx search tries to obtain it " +"via '[role=main]'. Could you check your theme or template.");return "";}return docContent.textContent || docContent.innerText;},init : function() {var params = $.getQueryParameters();if (params.q) {var query = params.q[0];$('input[name="q"]')[0].value = query;this.performSearch(query);}},loadIndex : function(url) {$.ajax({type: "GET", url: url, data: null,dataType: "script", cache: true,complete: function(jqxhr, textstatus) {if (textstatus != "success") {document.getElementById("searchindexloader").src = url;}}});},setIndex : function(index) {var q;this._index = index;if ((q = this._queued_query) !== null) {this._queued_query = null;Search.query(q);}},hasIndex : function() {return this._index !== null;},deferQuery : function(query) {this._queued_query = query;},stopPulse : function() {this._pulse_status = 0;},startPulse : function() {if (this._pulse_status >= 0)return;function pulse() {var i;Search._pulse_status = (Search._pulse_status + 1) % 4;var dotString = '';for (i = 0; i < Search._pulse_status; i++)dotString += '.';Search.dots.text(dotString);if (Search._pulse_status > -1)window.setTimeout(pulse, 500);}pulse();},/*** perform a search for something (or wait until index is loaded)*/performSearch : function(query) {// create the required interface elementsthis.out = $('#search-results');this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);this.dots = $('<span></span>').appendTo(this.title);this.status = $('<p class="search-summary"> </p>').appendTo(this.out);this.output = $('<ul class="search"/>').appendTo(this.out);$('#search-progress').text(_('Preparing search...'));this.startPulse();// index already loaded, the browser was quick!if (this.hasIndex())this.query(query);elsethis.deferQuery(query);},/*** execute search (requires search index to be loaded)*/query : function(query) {var i;// stem the searchterms and add them to the correct listvar stemmer = new Stemmer();var searchterms = [];var excluded = [];var hlterms = [];var tmp = splitQuery(query);var objectterms = [];for (i = 0; i < tmp.length; i++) {if (tmp[i] !== "") {objectterms.push(tmp[i].toLowerCase());}if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i] === "") {// skip this "word"continue;}// stem the wordvar word = stemmer.stemWord(tmp[i].toLowerCase());// prevent stemmer from cutting word smaller than two charsif(word.length < 3 && tmp[i].length >= 3) {word = tmp[i];}var toAppend;// select the correct listif (word[0] == '-') {toAppend = excluded;word = word.substr(1);}else {toAppend = searchterms;hlterms.push(tmp[i].toLowerCase());}// only add if not already in the listif (!$u.contains(toAppend, word))toAppend.push(word);}var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));// console.debug('SEARCH: searching for:');// console.info('required: ', searchterms);// console.info('excluded: ', excluded);// prepare searchvar terms = this._index.terms;var titleterms = this._index.titleterms;// array of [filename, title, anchor, descr, score]var results = [];$('#search-progress').empty();// lookup as objectfor (i = 0; i < objectterms.length; i++) {var others = [].concat(objectterms.slice(0, i),objectterms.slice(i+1, objectterms.length));results = results.concat(this.performObjectSearch(objectterms[i], others));}// lookup as search terms in fulltextresults = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));// let the scorer override scores with a custom scoring functionif (Scorer.score) {for (i = 0; i < results.length; i++)results[i][4] = Scorer.score(results[i]);}// now sort the results by score (in opposite order of appearance, since the// display function below uses pop() to retrieve items) and then// alphabeticallyresults.sort(function(a, b) {var left = a[4];var right = b[4];if (left > right) {return 1;} else if (left < right) {return -1;} else {// same score: sort alphabeticallyleft = a[1].toLowerCase();right = b[1].toLowerCase();return (left > right) ? -1 : ((left < right) ? 1 : 0);}});// for debugging//Search.lastresults = results.slice(); // a copy//console.info('search results:', Search.lastresults);// print the resultsvar resultCount = results.length;function displayNextItem() {// results left, load the summary and display itif (results.length) {var item = results.pop();var listItem = $('<li style="display:none"></li>');var requestUrl = "";var linkUrl = "";if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {// dirhtml buildervar dirname = item[0] + '/';if (dirname.match(/\/index\/$/)) {dirname = dirname.substring(0, dirname.length-6);} else if (dirname == 'index/') {dirname = '';}requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;linkUrl = requestUrl;} else {// normal html buildersrequestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;}listItem.append($('<a/>').attr('href',linkUrl +highlightstring + item[2]).html(item[1]));if (item[3]) {listItem.append($('<span> (' + item[3] + ')</span>'));Search.output.append(listItem);listItem.slideDown(5, function() {displayNextItem();});} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {$.ajax({url: requestUrl,dataType: "text",complete: function(jqxhr, textstatus) {var data = jqxhr.responseText;if (data !== '' && data !== undefined) {listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));}Search.output.append(listItem);listItem.slideDown(5, function() {displayNextItem();});}});} else {// no source available, just display titleSearch.output.append(listItem);listItem.slideDown(5, function() {displayNextItem();});}}// search finished, update title and status messageelse {Search.stopPulse();Search.title.text(_('Search Results'));if (!resultCount)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.'));elseSearch.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));Search.status.fadeIn(500);}}displayNextItem();},/*** search for object names*/performObjectSearch : function(object, otherterms) {var filenames = this._index.filenames;var docnames = this._index.docnames;var objects = this._index.objects;var objnames = this._index.objnames;var titles = this._index.titles;var i;var results = [];for (var prefix in objects) {for (var name in objects[prefix]) {var fullname = (prefix ? prefix + '.' : '') + name;var fullnameLower = fullname.toLowerCase()if (fullnameLower.indexOf(object) > -1) {var score = 0;var parts = fullnameLower.split('.');// check for different match types: exact matches of full name or// "last name" (i.e. last dotted part)if (fullnameLower == object || parts[parts.length - 1] == object) {score += Scorer.objNameMatch;// matches in last name} else if (parts[parts.length - 1].indexOf(object) > -1) {score += Scorer.objPartialMatch;}var match = objects[prefix][name];var objname = objnames[match[1]][2];var title = titles[match[0]];// If more than one term searched for, we require other words to be// found in the name/title/descriptionif (otherterms.length > 0) {var haystack = (prefix + ' ' + name + ' ' +objname + ' ' + title).toLowerCase();var allfound = true;for (i = 0; i < otherterms.length; i++) {if (haystack.indexOf(otherterms[i]) == -1) {allfound = false;break;}}if (!allfound) {continue;}}var descr = objname + _(', in ') + title;var anchor = match[3];if (anchor === '')anchor = fullname;else if (anchor == '-')anchor = objnames[match[1]][1] + '-' + fullname;// add custom score for some objects according to scorerif (Scorer.objPrio.hasOwnProperty(match[2])) {score += Scorer.objPrio[match[2]];} else {score += Scorer.objPrioDefault;}results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);}}}return results;},/*** search for full-text terms in the index*/performTermsSearch : function(searchterms, excluded, terms, titleterms) {var docnames = this._index.docnames;var filenames = this._index.filenames;var titles = this._index.titles;var i, j, file;var fileMap = {};var scoreMap = {};var results = [];// perform the search on the required termsfor (i = 0; i < searchterms.length; i++) {var word = searchterms[i];var files = [];var _o = [{files: terms[word], score: Scorer.term},{files: titleterms[word], score: Scorer.title}];// add support for partial matchesif (word.length > 2) {for (var w in terms) {if (w.match(word) && !terms[word]) {_o.push({files: terms[w], score: Scorer.partialTerm})}}for (var w in titleterms) {if (w.match(word) && !titleterms[word]) {_o.push({files: titleterms[w], score: Scorer.partialTitle})}}}// no match but word was a required oneif ($u.every(_o, function(o){return o.files === undefined;})) {break;}// found search word in contents$u.each(_o, function(o) {var _files = o.files;if (_files === undefined)returnif (_files.length === undefined)_files = [_files];files = files.concat(_files);// set score for the word in each file to Scorer.termfor (j = 0; j < _files.length; j++) {file = _files[j];if (!(file in scoreMap))scoreMap[file] = {};scoreMap[file][word] = o.score;}});// create the mappingfor (j = 0; j < files.length; j++) {file = files[j];if (file in fileMap && fileMap[file].indexOf(word) === -1)fileMap[file].push(word);elsefileMap[file] = [word];}}// now check if the files don't contain excluded termsfor (file in fileMap) {var valid = true;// check if all requirements are matchedvar filteredTermCount = // as search terms with length < 3 are discarded: ignoresearchterms.filter(function(term){return term.length > 2}).lengthif (fileMap[file].length != searchterms.length &&fileMap[file].length != filteredTermCount) continue;// ensure that none of the excluded terms is in the search resultfor (i = 0; i < excluded.length; i++) {if (terms[excluded[i]] == file ||titleterms[excluded[i]] == file ||$u.contains(terms[excluded[i]] || [], file) ||$u.contains(titleterms[excluded[i]] || [], file)) {valid = false;break;}}// if we have still a valid result we can add it to the result listif (valid) {// select one (max) score for the file.// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));results.push([docnames[file], titles[file], '', null, score, filenames[file]]);}}return results;},/*** helper function to return a node containing the* search summary for a given text. keywords is a list* of stemmed words, hlwords is the list of normal, unstemmed* words. the first one is used to find the occurrence, the* latter for highlighting it.*/makeSearchSummary : function(htmlText, keywords, hlwords) {var text = Search.htmlToText(htmlText);var textLower = text.toLowerCase();var start = 0;$.each(keywords, function() {var i = textLower.indexOf(this.toLowerCase());if (i > -1)start = i;});start = Math.max(start - 120, 0);var excerpt = ((start > 0) ? '...' : '') +$.trim(text.substr(start, 240)) +((start + 240 - text.length) ? '...' : '');var rv = $('<div class="context"></div>').text(excerpt);$.each(hlwords, function() {rv = rv.highlightText(this, 'highlighted');});return rv;}};$(document).ready(function() {Search.init();});