Mercurial > repos > rmarenco > hubarchivecreator
comparison doc/_build/html/_static/searchtools.js @ 20:40469b265ddb draft
planemo upload for repository https://github.com/goeckslab/hub-archive-creator commit 3af31e043f5b82636015c18e013d2f22ce6c9077-dirty
author | yating-l |
---|---|
date | Fri, 20 Jan 2017 17:12:03 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
19:0152500d9acd | 20:40469b265ddb |
---|---|
1 /* | |
2 * searchtools.js_t | |
3 * ~~~~~~~~~~~~~~~~ | |
4 * | |
5 * Sphinx JavaScript utilities for the full-text search. | |
6 * | |
7 * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. | |
8 * :license: BSD, see LICENSE for details. | |
9 * | |
10 */ | |
11 | |
12 | |
13 /* Non-minified version JS is _stemmer.js if file is provided */ | |
14 /** | |
15 * Porter Stemmer | |
16 */ | |
17 var Stemmer = function() { | |
18 | |
19 var step2list = { | |
20 ational: 'ate', | |
21 tional: 'tion', | |
22 enci: 'ence', | |
23 anci: 'ance', | |
24 izer: 'ize', | |
25 bli: 'ble', | |
26 alli: 'al', | |
27 entli: 'ent', | |
28 eli: 'e', | |
29 ousli: 'ous', | |
30 ization: 'ize', | |
31 ation: 'ate', | |
32 ator: 'ate', | |
33 alism: 'al', | |
34 iveness: 'ive', | |
35 fulness: 'ful', | |
36 ousness: 'ous', | |
37 aliti: 'al', | |
38 iviti: 'ive', | |
39 biliti: 'ble', | |
40 logi: 'log' | |
41 }; | |
42 | |
43 var step3list = { | |
44 icate: 'ic', | |
45 ative: '', | |
46 alize: 'al', | |
47 iciti: 'ic', | |
48 ical: 'ic', | |
49 ful: '', | |
50 ness: '' | |
51 }; | |
52 | |
53 var c = "[^aeiou]"; // consonant | |
54 var v = "[aeiouy]"; // vowel | |
55 var C = c + "[^aeiouy]*"; // consonant sequence | |
56 var V = v + "[aeiou]*"; // vowel sequence | |
57 | |
58 var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 | |
59 var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 | |
60 var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 | |
61 var s_v = "^(" + C + ")?" + v; // vowel in stem | |
62 | |
63 this.stemWord = function (w) { | |
64 var stem; | |
65 var suffix; | |
66 var firstch; | |
67 var origword = w; | |
68 | |
69 if (w.length < 3) | |
70 return w; | |
71 | |
72 var re; | |
73 var re2; | |
74 var re3; | |
75 var re4; | |
76 | |
77 firstch = w.substr(0,1); | |
78 if (firstch == "y") | |
79 w = firstch.toUpperCase() + w.substr(1); | |
80 | |
81 // Step 1a | |
82 re = /^(.+?)(ss|i)es$/; | |
83 re2 = /^(.+?)([^s])s$/; | |
84 | |
85 if (re.test(w)) | |
86 w = w.replace(re,"$1$2"); | |
87 else if (re2.test(w)) | |
88 w = w.replace(re2,"$1$2"); | |
89 | |
90 // Step 1b | |
91 re = /^(.+?)eed$/; | |
92 re2 = /^(.+?)(ed|ing)$/; | |
93 if (re.test(w)) { | |
94 var fp = re.exec(w); | |
95 re = new RegExp(mgr0); | |
96 if (re.test(fp[1])) { | |
97 re = /.$/; | |
98 w = w.replace(re,""); | |
99 } | |
100 } | |
101 else if (re2.test(w)) { | |
102 var fp = re2.exec(w); | |
103 stem = fp[1]; | |
104 re2 = new RegExp(s_v); | |
105 if (re2.test(stem)) { | |
106 w = stem; | |
107 re2 = /(at|bl|iz)$/; | |
108 re3 = new RegExp("([^aeiouylsz])\\1$"); | |
109 re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); | |
110 if (re2.test(w)) | |
111 w = w + "e"; | |
112 else if (re3.test(w)) { | |
113 re = /.$/; | |
114 w = w.replace(re,""); | |
115 } | |
116 else if (re4.test(w)) | |
117 w = w + "e"; | |
118 } | |
119 } | |
120 | |
121 // Step 1c | |
122 re = /^(.+?)y$/; | |
123 if (re.test(w)) { | |
124 var fp = re.exec(w); | |
125 stem = fp[1]; | |
126 re = new RegExp(s_v); | |
127 if (re.test(stem)) | |
128 w = stem + "i"; | |
129 } | |
130 | |
131 // Step 2 | |
132 re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; | |
133 if (re.test(w)) { | |
134 var fp = re.exec(w); | |
135 stem = fp[1]; | |
136 suffix = fp[2]; | |
137 re = new RegExp(mgr0); | |
138 if (re.test(stem)) | |
139 w = stem + step2list[suffix]; | |
140 } | |
141 | |
142 // Step 3 | |
143 re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; | |
144 if (re.test(w)) { | |
145 var fp = re.exec(w); | |
146 stem = fp[1]; | |
147 suffix = fp[2]; | |
148 re = new RegExp(mgr0); | |
149 if (re.test(stem)) | |
150 w = stem + step3list[suffix]; | |
151 } | |
152 | |
153 // Step 4 | |
154 re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; | |
155 re2 = /^(.+?)(s|t)(ion)$/; | |
156 if (re.test(w)) { | |
157 var fp = re.exec(w); | |
158 stem = fp[1]; | |
159 re = new RegExp(mgr1); | |
160 if (re.test(stem)) | |
161 w = stem; | |
162 } | |
163 else if (re2.test(w)) { | |
164 var fp = re2.exec(w); | |
165 stem = fp[1] + fp[2]; | |
166 re2 = new RegExp(mgr1); | |
167 if (re2.test(stem)) | |
168 w = stem; | |
169 } | |
170 | |
171 // Step 5 | |
172 re = /^(.+?)e$/; | |
173 if (re.test(w)) { | |
174 var fp = re.exec(w); | |
175 stem = fp[1]; | |
176 re = new RegExp(mgr1); | |
177 re2 = new RegExp(meq1); | |
178 re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); | |
179 if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) | |
180 w = stem; | |
181 } | |
182 re = /ll$/; | |
183 re2 = new RegExp(mgr1); | |
184 if (re.test(w) && re2.test(w)) { | |
185 re = /.$/; | |
186 w = w.replace(re,""); | |
187 } | |
188 | |
189 // and turn initial Y back to y | |
190 if (firstch == "y") | |
191 w = firstch.toLowerCase() + w.substr(1); | |
192 return w; | |
193 } | |
194 } | |
195 | |
196 | |
197 | |
198 /** | |
199 * Simple result scoring code. | |
200 */ | |
201 var Scorer = { | |
202 // Implement the following function to further tweak the score for each result | |
203 // The function takes a result array [filename, title, anchor, descr, score] | |
204 // and returns the new score. | |
205 /* | |
206 score: function(result) { | |
207 return result[4]; | |
208 }, | |
209 */ | |
210 | |
211 // query matches the full name of an object | |
212 objNameMatch: 11, | |
213 // or matches in the last dotted part of the object name | |
214 objPartialMatch: 6, | |
215 // Additive scores depending on the priority of the object | |
216 objPrio: {0: 15, // used to be importantResults | |
217 1: 5, // used to be objectResults | |
218 2: -5}, // used to be unimportantResults | |
219 // Used when the priority is not in the mapping. | |
220 objPrioDefault: 0, | |
221 | |
222 // query found in title | |
223 title: 15, | |
224 // query found in terms | |
225 term: 5 | |
226 }; | |
227 | |
228 | |
229 /** | |
230 * Search Module | |
231 */ | |
232 var Search = { | |
233 | |
234 _index : null, | |
235 _queued_query : null, | |
236 _pulse_status : -1, | |
237 | |
238 init : function() { | |
239 var params = $.getQueryParameters(); | |
240 if (params.q) { | |
241 var query = params.q[0]; | |
242 $('input[name="q"]')[0].value = query; | |
243 this.performSearch(query); | |
244 } | |
245 }, | |
246 | |
247 loadIndex : function(url) { | |
248 $.ajax({type: "GET", url: url, data: null, | |
249 dataType: "script", cache: true, | |
250 complete: function(jqxhr, textstatus) { | |
251 if (textstatus != "success") { | |
252 document.getElementById("searchindexloader").src = url; | |
253 } | |
254 }}); | |
255 }, | |
256 | |
257 setIndex : function(index) { | |
258 var q; | |
259 this._index = index; | |
260 if ((q = this._queued_query) !== null) { | |
261 this._queued_query = null; | |
262 Search.query(q); | |
263 } | |
264 }, | |
265 | |
266 hasIndex : function() { | |
267 return this._index !== null; | |
268 }, | |
269 | |
270 deferQuery : function(query) { | |
271 this._queued_query = query; | |
272 }, | |
273 | |
274 stopPulse : function() { | |
275 this._pulse_status = 0; | |
276 }, | |
277 | |
278 startPulse : function() { | |
279 if (this._pulse_status >= 0) | |
280 return; | |
281 function pulse() { | |
282 var i; | |
283 Search._pulse_status = (Search._pulse_status + 1) % 4; | |
284 var dotString = ''; | |
285 for (i = 0; i < Search._pulse_status; i++) | |
286 dotString += '.'; | |
287 Search.dots.text(dotString); | |
288 if (Search._pulse_status > -1) | |
289 window.setTimeout(pulse, 500); | |
290 } | |
291 pulse(); | |
292 }, | |
293 | |
294 /** | |
295 * perform a search for something (or wait until index is loaded) | |
296 */ | |
297 performSearch : function(query) { | |
298 // create the required interface elements | |
299 this.out = $('#search-results'); | |
300 this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out); | |
301 this.dots = $('<span></span>').appendTo(this.title); | |
302 this.status = $('<p style="display: none"></p>').appendTo(this.out); | |
303 this.output = $('<ul class="search"/>').appendTo(this.out); | |
304 | |
305 $('#search-progress').text(_('Preparing search...')); | |
306 this.startPulse(); | |
307 | |
308 // index already loaded, the browser was quick! | |
309 if (this.hasIndex()) | |
310 this.query(query); | |
311 else | |
312 this.deferQuery(query); | |
313 }, | |
314 | |
315 /** | |
316 * execute search (requires search index to be loaded) | |
317 */ | |
318 query : function(query) { | |
319 var i; | |
320 var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"]; | |
321 | |
322 // stem the searchterms and add them to the correct list | |
323 var stemmer = new Stemmer(); | |
324 var searchterms = []; | |
325 var excluded = []; | |
326 var hlterms = []; | |
327 var tmp = query.split(/\W+/); | |
328 var objectterms = []; | |
329 for (i = 0; i < tmp.length; i++) { | |
330 if (tmp[i] !== "") { | |
331 objectterms.push(tmp[i].toLowerCase()); | |
332 } | |
333 | |
334 if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) || | |
335 tmp[i] === "") { | |
336 // skip this "word" | |
337 continue; | |
338 } | |
339 // stem the word | |
340 var word = stemmer.stemWord(tmp[i].toLowerCase()); | |
341 var toAppend; | |
342 // select the correct list | |
343 if (word[0] == '-') { | |
344 toAppend = excluded; | |
345 word = word.substr(1); | |
346 } | |
347 else { | |
348 toAppend = searchterms; | |
349 hlterms.push(tmp[i].toLowerCase()); | |
350 } | |
351 // only add if not already in the list | |
352 if (!$u.contains(toAppend, word)) | |
353 toAppend.push(word); | |
354 } | |
355 var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" ")); | |
356 | |
357 // console.debug('SEARCH: searching for:'); | |
358 // console.info('required: ', searchterms); | |
359 // console.info('excluded: ', excluded); | |
360 | |
361 // prepare search | |
362 var terms = this._index.terms; | |
363 var titleterms = this._index.titleterms; | |
364 | |
365 // array of [filename, title, anchor, descr, score] | |
366 var results = []; | |
367 $('#search-progress').empty(); | |
368 | |
369 // lookup as object | |
370 for (i = 0; i < objectterms.length; i++) { | |
371 var others = [].concat(objectterms.slice(0, i), | |
372 objectterms.slice(i+1, objectterms.length)); | |
373 results = results.concat(this.performObjectSearch(objectterms[i], others)); | |
374 } | |
375 | |
376 // lookup as search terms in fulltext | |
377 results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms)); | |
378 | |
379 // let the scorer override scores with a custom scoring function | |
380 if (Scorer.score) { | |
381 for (i = 0; i < results.length; i++) | |
382 results[i][4] = Scorer.score(results[i]); | |
383 } | |
384 | |
385 // now sort the results by score (in opposite order of appearance, since the | |
386 // display function below uses pop() to retrieve items) and then | |
387 // alphabetically | |
388 results.sort(function(a, b) { | |
389 var left = a[4]; | |
390 var right = b[4]; | |
391 if (left > right) { | |
392 return 1; | |
393 } else if (left < right) { | |
394 return -1; | |
395 } else { | |
396 // same score: sort alphabetically | |
397 left = a[1].toLowerCase(); | |
398 right = b[1].toLowerCase(); | |
399 return (left > right) ? -1 : ((left < right) ? 1 : 0); | |
400 } | |
401 }); | |
402 | |
403 // for debugging | |
404 //Search.lastresults = results.slice(); // a copy | |
405 //console.info('search results:', Search.lastresults); | |
406 | |
407 // print the results | |
408 var resultCount = results.length; | |
409 function displayNextItem() { | |
410 // results left, load the summary and display it | |
411 if (results.length) { | |
412 var item = results.pop(); | |
413 var listItem = $('<li style="display:none"></li>'); | |
414 if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') { | |
415 // dirhtml builder | |
416 var dirname = item[0] + '/'; | |
417 if (dirname.match(/\/index\/$/)) { | |
418 dirname = dirname.substring(0, dirname.length-6); | |
419 } else if (dirname == 'index/') { | |
420 dirname = ''; | |
421 } | |
422 listItem.append($('<a/>').attr('href', | |
423 DOCUMENTATION_OPTIONS.URL_ROOT + dirname + | |
424 highlightstring + item[2]).html(item[1])); | |
425 } else { | |
426 // normal html builders | |
427 listItem.append($('<a/>').attr('href', | |
428 item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX + | |
429 highlightstring + item[2]).html(item[1])); | |
430 } | |
431 if (item[3]) { | |
432 listItem.append($('<span> (' + item[3] + ')</span>')); | |
433 Search.output.append(listItem); | |
434 listItem.slideDown(5, function() { | |
435 displayNextItem(); | |
436 }); | |
437 } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) { | |
438 $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt', | |
439 dataType: "text", | |
440 complete: function(jqxhr, textstatus) { | |
441 var data = jqxhr.responseText; | |
442 if (data !== '' && data !== undefined) { | |
443 listItem.append(Search.makeSearchSummary(data, searchterms, hlterms)); | |
444 } | |
445 Search.output.append(listItem); | |
446 listItem.slideDown(5, function() { | |
447 displayNextItem(); | |
448 }); | |
449 }}); | |
450 } else { | |
451 // no source available, just display title | |
452 Search.output.append(listItem); | |
453 listItem.slideDown(5, function() { | |
454 displayNextItem(); | |
455 }); | |
456 } | |
457 } | |
458 // search finished, update title and status message | |
459 else { | |
460 Search.stopPulse(); | |
461 Search.title.text(_('Search Results')); | |
462 if (!resultCount) | |
463 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.')); | |
464 else | |
465 Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount)); | |
466 Search.status.fadeIn(500); | |
467 } | |
468 } | |
469 displayNextItem(); | |
470 }, | |
471 | |
472 /** | |
473 * search for object names | |
474 */ | |
475 performObjectSearch : function(object, otherterms) { | |
476 var filenames = this._index.filenames; | |
477 var objects = this._index.objects; | |
478 var objnames = this._index.objnames; | |
479 var titles = this._index.titles; | |
480 | |
481 var i; | |
482 var results = []; | |
483 | |
484 for (var prefix in objects) { | |
485 for (var name in objects[prefix]) { | |
486 var fullname = (prefix ? prefix + '.' : '') + name; | |
487 if (fullname.toLowerCase().indexOf(object) > -1) { | |
488 var score = 0; | |
489 var parts = fullname.split('.'); | |
490 // check for different match types: exact matches of full name or | |
491 // "last name" (i.e. last dotted part) | |
492 if (fullname == object || parts[parts.length - 1] == object) { | |
493 score += Scorer.objNameMatch; | |
494 // matches in last name | |
495 } else if (parts[parts.length - 1].indexOf(object) > -1) { | |
496 score += Scorer.objPartialMatch; | |
497 } | |
498 var match = objects[prefix][name]; | |
499 var objname = objnames[match[1]][2]; | |
500 var title = titles[match[0]]; | |
501 // If more than one term searched for, we require other words to be | |
502 // found in the name/title/description | |
503 if (otherterms.length > 0) { | |
504 var haystack = (prefix + ' ' + name + ' ' + | |
505 objname + ' ' + title).toLowerCase(); | |
506 var allfound = true; | |
507 for (i = 0; i < otherterms.length; i++) { | |
508 if (haystack.indexOf(otherterms[i]) == -1) { | |
509 allfound = false; | |
510 break; | |
511 } | |
512 } | |
513 if (!allfound) { | |
514 continue; | |
515 } | |
516 } | |
517 var descr = objname + _(', in ') + title; | |
518 | |
519 var anchor = match[3]; | |
520 if (anchor === '') | |
521 anchor = fullname; | |
522 else if (anchor == '-') | |
523 anchor = objnames[match[1]][1] + '-' + fullname; | |
524 // add custom score for some objects according to scorer | |
525 if (Scorer.objPrio.hasOwnProperty(match[2])) { | |
526 score += Scorer.objPrio[match[2]]; | |
527 } else { | |
528 score += Scorer.objPrioDefault; | |
529 } | |
530 results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]); | |
531 } | |
532 } | |
533 } | |
534 | |
535 return results; | |
536 }, | |
537 | |
538 /** | |
539 * search for full-text terms in the index | |
540 */ | |
541 performTermsSearch : function(searchterms, excluded, terms, titleterms) { | |
542 var filenames = this._index.filenames; | |
543 var titles = this._index.titles; | |
544 | |
545 var i, j, file; | |
546 var fileMap = {}; | |
547 var scoreMap = {}; | |
548 var results = []; | |
549 | |
550 // perform the search on the required terms | |
551 for (i = 0; i < searchterms.length; i++) { | |
552 var word = searchterms[i]; | |
553 var files = []; | |
554 var _o = [ | |
555 {files: terms[word], score: Scorer.term}, | |
556 {files: titleterms[word], score: Scorer.title} | |
557 ]; | |
558 | |
559 // no match but word was a required one | |
560 if ($u.every(_o, function(o){return o.files === undefined;})) { | |
561 break; | |
562 } | |
563 // found search word in contents | |
564 $u.each(_o, function(o) { | |
565 var _files = o.files; | |
566 if (_files === undefined) | |
567 return | |
568 | |
569 if (_files.length === undefined) | |
570 _files = [_files]; | |
571 files = files.concat(_files); | |
572 | |
573 // set score for the word in each file to Scorer.term | |
574 for (j = 0; j < _files.length; j++) { | |
575 file = _files[j]; | |
576 if (!(file in scoreMap)) | |
577 scoreMap[file] = {} | |
578 scoreMap[file][word] = o.score; | |
579 } | |
580 }); | |
581 | |
582 // create the mapping | |
583 for (j = 0; j < files.length; j++) { | |
584 file = files[j]; | |
585 if (file in fileMap) | |
586 fileMap[file].push(word); | |
587 else | |
588 fileMap[file] = [word]; | |
589 } | |
590 } | |
591 | |
592 // now check if the files don't contain excluded terms | |
593 for (file in fileMap) { | |
594 var valid = true; | |
595 | |
596 // check if all requirements are matched | |
597 if (fileMap[file].length != searchterms.length) | |
598 continue; | |
599 | |
600 // ensure that none of the excluded terms is in the search result | |
601 for (i = 0; i < excluded.length; i++) { | |
602 if (terms[excluded[i]] == file || | |
603 titleterms[excluded[i]] == file || | |
604 $u.contains(terms[excluded[i]] || [], file) || | |
605 $u.contains(titleterms[excluded[i]] || [], file)) { | |
606 valid = false; | |
607 break; | |
608 } | |
609 } | |
610 | |
611 // if we have still a valid result we can add it to the result list | |
612 if (valid) { | |
613 // select one (max) score for the file. | |
614 // for better ranking, we should calculate ranking by using words statistics like basic tf-idf... | |
615 var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]})); | |
616 results.push([filenames[file], titles[file], '', null, score]); | |
617 } | |
618 } | |
619 return results; | |
620 }, | |
621 | |
622 /** | |
623 * helper function to return a node containing the | |
624 * search summary for a given text. keywords is a list | |
625 * of stemmed words, hlwords is the list of normal, unstemmed | |
626 * words. the first one is used to find the occurrence, the | |
627 * latter for highlighting it. | |
628 */ | |
629 makeSearchSummary : function(text, keywords, hlwords) { | |
630 var textLower = text.toLowerCase(); | |
631 var start = 0; | |
632 $.each(keywords, function() { | |
633 var i = textLower.indexOf(this.toLowerCase()); | |
634 if (i > -1) | |
635 start = i; | |
636 }); | |
637 start = Math.max(start - 120, 0); | |
638 var excerpt = ((start > 0) ? '...' : '') + | |
639 $.trim(text.substr(start, 240)) + | |
640 ((start + 240 - text.length) ? '...' : ''); | |
641 var rv = $('<div class="context"></div>').text(excerpt); | |
642 $.each(hlwords, function() { | |
643 rv = rv.highlightText(this, 'highlighted'); | |
644 }); | |
645 return rv; | |
646 } | |
647 }; | |
648 | |
649 $(document).ready(function() { | |
650 Search.init(); | |
651 }); |