]> ToastFreeware Gitweb - chrisu/seepark.git/blob - web/static/doc/_static/searchtools.js
Convert string format function calls to f-strings.
[chrisu/seepark.git] / web / static / doc / _static / searchtools.js
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
231
232 var splitChars = (function() {
233     var result = {};
234     var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
235          1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
236          2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
237          2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
238          3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
239          3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
240          4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
241          8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
242          11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
243          43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
244     var i, j, start, end;
245     for (i = 0; i < singles.length; i++) {
246         result[singles[i]] = true;
247     }
248     var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
249          [722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
250          [1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
251          [1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
252          [1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
253          [2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
254          [2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
255          [2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
256          [2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
257          [2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
258          [2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
259          [2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
260          [3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
261          [3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
262          [3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
263          [3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
264          [3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
265          [3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
266          [4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
267          [4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
268          [4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
269          [4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
270          [5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
271          [6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
272          [6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
273          [6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
274          [6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
275          [7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
276          [7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
277          [8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
278          [8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
279          [8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
280          [10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
281          [11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
282          [12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
283          [12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
284          [12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
285          [19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
286          [42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
287          [42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
288          [43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
289          [43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
290          [43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
291          [43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
292          [44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
293          [57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
294          [64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
295          [65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
296          [65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
297     for (i = 0; i < ranges.length; i++) {
298         start = ranges[i][0];
299         end = ranges[i][1];
300         for (j = start; j <= end; j++) {
301             result[j] = true;
302         }
303     }
304     return result;
305 })();
306
307 function splitQuery(query) {
308     var result = [];
309     var start = -1;
310     for (var i = 0; i < query.length; i++) {
311         if (splitChars[query.charCodeAt(i)]) {
312             if (start !== -1) {
313                 result.push(query.slice(start, i));
314                 start = -1;
315             }
316         } else if (start === -1) {
317             start = i;
318         }
319     }
320     if (start !== -1) {
321         result.push(query.slice(start));
322     }
323     return result;
324 }
325
326
327
328
329 /**
330  * Search Module
331  */
332 var Search = {
333
334   _index : null,
335   _queued_query : null,
336   _pulse_status : -1,
337
338   init : function() {
339       var params = $.getQueryParameters();
340       if (params.q) {
341           var query = params.q[0];
342           $('input[name="q"]')[0].value = query;
343           this.performSearch(query);
344       }
345   },
346
347   loadIndex : function(url) {
348     $.ajax({type: "GET", url: url, data: null,
349             dataType: "script", cache: true,
350             complete: function(jqxhr, textstatus) {
351               if (textstatus != "success") {
352                 document.getElementById("searchindexloader").src = url;
353               }
354             }});
355   },
356
357   setIndex : function(index) {
358     var q;
359     this._index = index;
360     if ((q = this._queued_query) !== null) {
361       this._queued_query = null;
362       Search.query(q);
363     }
364   },
365
366   hasIndex : function() {
367       return this._index !== null;
368   },
369
370   deferQuery : function(query) {
371       this._queued_query = query;
372   },
373
374   stopPulse : function() {
375       this._pulse_status = 0;
376   },
377
378   startPulse : function() {
379     if (this._pulse_status >= 0)
380         return;
381     function pulse() {
382       var i;
383       Search._pulse_status = (Search._pulse_status + 1) % 4;
384       var dotString = '';
385       for (i = 0; i < Search._pulse_status; i++)
386         dotString += '.';
387       Search.dots.text(dotString);
388       if (Search._pulse_status > -1)
389         window.setTimeout(pulse, 500);
390     }
391     pulse();
392   },
393
394   /**
395    * perform a search for something (or wait until index is loaded)
396    */
397   performSearch : function(query) {
398     // create the required interface elements
399     this.out = $('#search-results');
400     this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
401     this.dots = $('<span></span>').appendTo(this.title);
402     this.status = $('<p style="display: none"></p>').appendTo(this.out);
403     this.output = $('<ul class="search"/>').appendTo(this.out);
404
405     $('#search-progress').text(_('Preparing search...'));
406     this.startPulse();
407
408     // index already loaded, the browser was quick!
409     if (this.hasIndex())
410       this.query(query);
411     else
412       this.deferQuery(query);
413   },
414
415   /**
416    * execute search (requires search index to be loaded)
417    */
418   query : function(query) {
419     var i;
420     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"];
421
422     // stem the searchterms and add them to the correct list
423     var stemmer = new Stemmer();
424     var searchterms = [];
425     var excluded = [];
426     var hlterms = [];
427     var tmp = splitQuery(query);
428     var objectterms = [];
429     for (i = 0; i < tmp.length; i++) {
430       if (tmp[i] !== "") {
431           objectterms.push(tmp[i].toLowerCase());
432       }
433
434       if ($u.indexOf(stopwords, tmp[i].toLowerCase()) != -1 || tmp[i].match(/^\d+$/) ||
435           tmp[i] === "") {
436         // skip this "word"
437         continue;
438       }
439       // stem the word
440       var word = stemmer.stemWord(tmp[i].toLowerCase());
441       var toAppend;
442       // select the correct list
443       if (word[0] == '-') {
444         toAppend = excluded;
445         word = word.substr(1);
446       }
447       else {
448         toAppend = searchterms;
449         hlterms.push(tmp[i].toLowerCase());
450       }
451       // only add if not already in the list
452       if (!$u.contains(toAppend, word))
453         toAppend.push(word);
454     }
455     var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
456
457     // console.debug('SEARCH: searching for:');
458     // console.info('required: ', searchterms);
459     // console.info('excluded: ', excluded);
460
461     // prepare search
462     var terms = this._index.terms;
463     var titleterms = this._index.titleterms;
464
465     // array of [filename, title, anchor, descr, score]
466     var results = [];
467     $('#search-progress').empty();
468
469     // lookup as object
470     for (i = 0; i < objectterms.length; i++) {
471       var others = [].concat(objectterms.slice(0, i),
472                              objectterms.slice(i+1, objectterms.length));
473       results = results.concat(this.performObjectSearch(objectterms[i], others));
474     }
475
476     // lookup as search terms in fulltext
477     results = results.concat(this.performTermsSearch(searchterms, excluded, terms, titleterms));
478
479     // let the scorer override scores with a custom scoring function
480     if (Scorer.score) {
481       for (i = 0; i < results.length; i++)
482         results[i][4] = Scorer.score(results[i]);
483     }
484
485     // now sort the results by score (in opposite order of appearance, since the
486     // display function below uses pop() to retrieve items) and then
487     // alphabetically
488     results.sort(function(a, b) {
489       var left = a[4];
490       var right = b[4];
491       if (left > right) {
492         return 1;
493       } else if (left < right) {
494         return -1;
495       } else {
496         // same score: sort alphabetically
497         left = a[1].toLowerCase();
498         right = b[1].toLowerCase();
499         return (left > right) ? -1 : ((left < right) ? 1 : 0);
500       }
501     });
502
503     // for debugging
504     //Search.lastresults = results.slice();  // a copy
505     //console.info('search results:', Search.lastresults);
506
507     // print the results
508     var resultCount = results.length;
509     function displayNextItem() {
510       // results left, load the summary and display it
511       if (results.length) {
512         var item = results.pop();
513         var listItem = $('<li style="display:none"></li>');
514         if (DOCUMENTATION_OPTIONS.FILE_SUFFIX === '') {
515           // dirhtml builder
516           var dirname = item[0] + '/';
517           if (dirname.match(/\/index\/$/)) {
518             dirname = dirname.substring(0, dirname.length-6);
519           } else if (dirname == 'index/') {
520             dirname = '';
521           }
522           listItem.append($('<a/>').attr('href',
523             DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
524             highlightstring + item[2]).html(item[1]));
525         } else {
526           // normal html builders
527           listItem.append($('<a/>').attr('href',
528             item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
529             highlightstring + item[2]).html(item[1]));
530         }
531         if (item[3]) {
532           listItem.append($('<span> (' + item[3] + ')</span>'));
533           Search.output.append(listItem);
534           listItem.slideDown(5, function() {
535             displayNextItem();
536           });
537         } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
538           $.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt',
539                   dataType: "text",
540                   complete: function(jqxhr, textstatus) {
541                     var data = jqxhr.responseText;
542                     if (data !== '' && data !== undefined) {
543                       listItem.append(Search.makeSearchSummary(data, searchterms, hlterms));
544                     }
545                     Search.output.append(listItem);
546                     listItem.slideDown(5, function() {
547                       displayNextItem();
548                     });
549                   }});
550         } else {
551           // no source available, just display title
552           Search.output.append(listItem);
553           listItem.slideDown(5, function() {
554             displayNextItem();
555           });
556         }
557       }
558       // search finished, update title and status message
559       else {
560         Search.stopPulse();
561         Search.title.text(_('Search Results'));
562         if (!resultCount)
563           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.'));
564         else
565             Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
566         Search.status.fadeIn(500);
567       }
568     }
569     displayNextItem();
570   },
571
572   /**
573    * search for object names
574    */
575   performObjectSearch : function(object, otherterms) {
576     var filenames = this._index.filenames;
577     var objects = this._index.objects;
578     var objnames = this._index.objnames;
579     var titles = this._index.titles;
580
581     var i;
582     var results = [];
583
584     for (var prefix in objects) {
585       for (var name in objects[prefix]) {
586         var fullname = (prefix ? prefix + '.' : '') + name;
587         if (fullname.toLowerCase().indexOf(object) > -1) {
588           var score = 0;
589           var parts = fullname.split('.');
590           // check for different match types: exact matches of full name or
591           // "last name" (i.e. last dotted part)
592           if (fullname == object || parts[parts.length - 1] == object) {
593             score += Scorer.objNameMatch;
594           // matches in last name
595           } else if (parts[parts.length - 1].indexOf(object) > -1) {
596             score += Scorer.objPartialMatch;
597           }
598           var match = objects[prefix][name];
599           var objname = objnames[match[1]][2];
600           var title = titles[match[0]];
601           // If more than one term searched for, we require other words to be
602           // found in the name/title/description
603           if (otherterms.length > 0) {
604             var haystack = (prefix + ' ' + name + ' ' +
605                             objname + ' ' + title).toLowerCase();
606             var allfound = true;
607             for (i = 0; i < otherterms.length; i++) {
608               if (haystack.indexOf(otherterms[i]) == -1) {
609                 allfound = false;
610                 break;
611               }
612             }
613             if (!allfound) {
614               continue;
615             }
616           }
617           var descr = objname + _(', in ') + title;
618
619           var anchor = match[3];
620           if (anchor === '')
621             anchor = fullname;
622           else if (anchor == '-')
623             anchor = objnames[match[1]][1] + '-' + fullname;
624           // add custom score for some objects according to scorer
625           if (Scorer.objPrio.hasOwnProperty(match[2])) {
626             score += Scorer.objPrio[match[2]];
627           } else {
628             score += Scorer.objPrioDefault;
629           }
630           results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
631         }
632       }
633     }
634
635     return results;
636   },
637
638   /**
639    * search for full-text terms in the index
640    */
641   performTermsSearch : function(searchterms, excluded, terms, titleterms) {
642     var filenames = this._index.filenames;
643     var titles = this._index.titles;
644
645     var i, j, file;
646     var fileMap = {};
647     var scoreMap = {};
648     var results = [];
649
650     // perform the search on the required terms
651     for (i = 0; i < searchterms.length; i++) {
652       var word = searchterms[i];
653       var files = [];
654       var _o = [
655         {files: terms[word], score: Scorer.term},
656         {files: titleterms[word], score: Scorer.title}
657       ];
658
659       // no match but word was a required one
660       if ($u.every(_o, function(o){return o.files === undefined;})) {
661         break;
662       }
663       // found search word in contents
664       $u.each(_o, function(o) {
665         var _files = o.files;
666         if (_files === undefined)
667           return
668
669         if (_files.length === undefined)
670           _files = [_files];
671         files = files.concat(_files);
672
673         // set score for the word in each file to Scorer.term
674         for (j = 0; j < _files.length; j++) {
675           file = _files[j];
676           if (!(file in scoreMap))
677             scoreMap[file] = {}
678           scoreMap[file][word] = o.score;
679         }
680       });
681
682       // create the mapping
683       for (j = 0; j < files.length; j++) {
684         file = files[j];
685         if (file in fileMap)
686           fileMap[file].push(word);
687         else
688           fileMap[file] = [word];
689       }
690     }
691
692     // now check if the files don't contain excluded terms
693     for (file in fileMap) {
694       var valid = true;
695
696       // check if all requirements are matched
697       if (fileMap[file].length != searchterms.length)
698           continue;
699
700       // ensure that none of the excluded terms is in the search result
701       for (i = 0; i < excluded.length; i++) {
702         if (terms[excluded[i]] == file ||
703             titleterms[excluded[i]] == file ||
704             $u.contains(terms[excluded[i]] || [], file) ||
705             $u.contains(titleterms[excluded[i]] || [], file)) {
706           valid = false;
707           break;
708         }
709       }
710
711       // if we have still a valid result we can add it to the result list
712       if (valid) {
713         // select one (max) score for the file.
714         // for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
715         var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
716         results.push([filenames[file], titles[file], '', null, score]);
717       }
718     }
719     return results;
720   },
721
722   /**
723    * helper function to return a node containing the
724    * search summary for a given text. keywords is a list
725    * of stemmed words, hlwords is the list of normal, unstemmed
726    * words. the first one is used to find the occurrence, the
727    * latter for highlighting it.
728    */
729   makeSearchSummary : function(text, keywords, hlwords) {
730     var textLower = text.toLowerCase();
731     var start = 0;
732     $.each(keywords, function() {
733       var i = textLower.indexOf(this.toLowerCase());
734       if (i > -1)
735         start = i;
736     });
737     start = Math.max(start - 120, 0);
738     var excerpt = ((start > 0) ? '...' : '') +
739       $.trim(text.substr(start, 240)) +
740       ((start + 240 - text.length) ? '...' : '');
741     var rv = $('<div class="context"></div>').text(excerpt);
742     $.each(hlwords, function() {
743       rv = rv.highlightText(this, 'highlighted');
744     });
745     return rv;
746   }
747 };
748
749 $(document).ready(function() {
750   Search.init();
751 });