git.fiddlerwoaroof.com
doc/build/html/_static/searchtools.js
e88cd377
 /*
  * searchtools.js
  * ~~~~~~~~~~~~~~
  *
  * Sphinx JavaScript utilties for the full-text search.
  *
  * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
  * :license: BSD, see LICENSE for details.
  *
  */
 
 /**
  * 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 occurance, the
  * latter for highlighting it.
  */
 
 jQuery.makeSearchSummary = function(text, keywords, hlwords) {
   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;
 }
 
 /**
  * Porter Stemmer
  */
 var PorterStemmer = function() {
 
   var step2list = {
     ational: 'ate',
     tional: 'tion',
     enci: 'ence',
     anci: 'ance',
     izer: 'ize',
     bli: 'ble',
     alli: 'al',
     entli: 'ent',
     eli: 'e',
     ousli: 'ous',
     ization: 'ize',
     ation: 'ate',
     ator: 'ate',
     alism: 'al',
     iveness: 'ive',
     fulness: 'ful',
     ousness: 'ous',
     aliti: 'al',
     iviti: 'ive',
     biliti: 'ble',
     logi: 'log'
   };
 
   var step3list = {
     icate: 'ic',
     ative: '',
     alize: 'al',
     iciti: 'ic',
     ical: 'ic',
     ful: '',
     ness: ''
   };
 
   var c = "[^aeiou]";          // consonant
   var v = "[aeiouy]";          // vowel
   var C = c + "[^aeiouy]*";    // consonant sequence
   var V = v + "[aeiou]*";      // vowel sequence
 
   var mgr0 = "^(" + C + ")?" + V + C;                      // [C]VC... is m>0
   var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$";    // [C]VC[V] is m=1
   var mgr1 = "^(" + C + ")?" + V + C + V + C;              // [C]VCVC... is m>1
   var s_v   = "^(" + C + ")?" + v;                         // vowel in stem
 
   this.stemWord = function (w) {
     var stem;
     var suffix;
     var firstch;
     var origword = w;
 
     if (w.length < 3)
       return w;
 
     var re;
     var re2;
     var re3;
     var re4;
 
     firstch = w.substr(0,1);
     if (firstch == "y")
       w = firstch.toUpperCase() + w.substr(1);
 
     // Step 1a
     re = /^(.+?)(ss|i)es$/;
     re2 = /^(.+?)([^s])s$/;
 
     if (re.test(w))
       w = w.replace(re,"$1$2");
     else if (re2.test(w))
       w = w.replace(re2,"$1$2");
 
     // Step 1b
     re = /^(.+?)eed$/;
     re2 = /^(.+?)(ed|ing)$/;
     if (re.test(w)) {
       var fp = re.exec(w);
       re = new RegExp(mgr0);
       if (re.test(fp[1])) {
         re = /.$/;
         w = w.replace(re,"");
       }
     }
     else if (re2.test(w)) {
       var fp = re2.exec(w);
       stem = fp[1];
       re2 = new RegExp(s_v);
       if (re2.test(stem)) {
         w = stem;
         re2 = /(at|bl|iz)$/;
         re3 = new RegExp("([^aeiouylsz])\\1$");
         re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
         if (re2.test(w))
           w = w + "e";
         else if (re3.test(w)) {
           re = /.$/;
           w = w.replace(re,"");
         }
         else if (re4.test(w))
           w = w + "e";
       }
     }
 
     // Step 1c
     re = /^(.+?)y$/;
     if (re.test(w)) {
       var fp = re.exec(w);
       stem = fp[1];
       re = new RegExp(s_v);
       if (re.test(stem))
         w = stem + "i";
     }
 
     // Step 2
     re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
     if (re.test(w)) {
       var fp = re.exec(w);
       stem = fp[1];
       suffix = fp[2];
       re = new RegExp(mgr0);
       if (re.test(stem))
         w = stem + step2list[suffix];
     }
 
     // Step 3
     re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
     if (re.test(w)) {
       var fp = re.exec(w);
       stem = fp[1];
       suffix = fp[2];
       re = new RegExp(mgr0);
       if (re.test(stem))
         w = stem + step3list[suffix];
     }
 
     // Step 4
     re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
     re2 = /^(.+?)(s|t)(ion)$/;
     if (re.test(w)) {
       var fp = re.exec(w);
       stem = fp[1];
       re = new RegExp(mgr1);
       if (re.test(stem))
         w = stem;
     }
     else if (re2.test(w)) {
       var fp = re2.exec(w);
       stem = fp[1] + fp[2];
       re2 = new RegExp(mgr1);
       if (re2.test(stem))
         w = stem;
     }
 
     // Step 5
     re = /^(.+?)e$/;
     if (re.test(w)) {
       var fp = re.exec(w);
       stem = fp[1];
       re = new RegExp(mgr1);
       re2 = new RegExp(meq1);
       re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
       if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
         w = stem;
     }
     re = /ll$/;
     re2 = new RegExp(mgr1);
     if (re.test(w) && re2.test(w)) {
       re = /.$/;
       w = w.replace(re,"");
     }
 
     // and turn initial Y back to y
     if (firstch == "y")
       w = firstch.toLowerCase() + w.substr(1);
     return w;
   }
 }
 
 
 /**
  * Search Module
  */
 var Search = {
 
   _index : null,
   _queued_query : null,
   _pulse_status : -1,
 
   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, success: null,
             dataType: "script", cache: true});
   },
 
   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() {
       Search._pulse_status = (Search._pulse_status + 1) % 4;
       var dotString = '';
       for (var 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
    */
   performSearch : function(query) {
     // create the required interface elements
     this.out = $('#search-results');
     this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
     this.dots = $('<span></span>').appendTo(this.title);
     this.status = $('<p style="display: none"></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);
     else
       this.deferQuery(query);
   },
 
   query : function(query) {
     var stopwords = ['and', 'then', 'into', 'it', 'as', 'are', 'in',
                      'if', 'for', 'no', 'there', 'their', 'was', 'is',
                      'be', 'to', 'that', 'but', 'they', 'not', 'such',
                      'with', 'by', 'a', 'on', 'these', 'of', 'will',
                      'this', 'near', 'the', 'or', 'at'];
 
     // stem the searchterms and add them to the correct list
     var stemmer = new PorterStemmer();
     var searchterms = [];
     var excluded = [];
     var hlterms = [];
     var tmp = query.split(/\s+/);
     var object = (tmp.length == 1) ? tmp[0].toLowerCase() : null;
     for (var i = 0; i < tmp.length; i++) {
       if ($u.indexOf(stopwords, tmp[i]) != -1 || tmp[i].match(/^\d+$/) ||
           tmp[i] == "") {
         // skip this "word"
         continue;
       }
       // stem the word
       var word = stemmer.stemWord(tmp[i]).toLowerCase();
       // select the correct list
       if (word[0] == '-') {
         var toAppend = excluded;
         word = word.substr(1);
       }
       else {
         var toAppend = searchterms;
         hlterms.push(tmp[i].toLowerCase());
       }
       // only add if not already in the list
       if (!$.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 search
     var filenames = this._index.filenames;
     var titles = this._index.titles;
     var terms = this._index.terms;
     var objects = this._index.objects;
     var objtypes = this._index.objtypes;
     var objnames = this._index.objnames;
     var fileMap = {};
     var files = null;
     // different result priorities
     var importantResults = [];
     var objectResults = [];
     var regularResults = [];
     var unimportantResults = [];
     $('#search-progress').empty();
 
     // lookup as object
     if (object != null) {
       for (var prefix in objects) {
         for (var name in objects[prefix]) {
           var fullname = (prefix ? prefix + '.' : '') + name;
           if (fullname.toLowerCase().indexOf(object) > -1) {
             match = objects[prefix][name];
             descr = objnames[match[1]] + _(', in ') + titles[match[0]];
             // XXX the generated anchors are not generally correct
             // XXX there may be custom prefixes
             result = [filenames[match[0]], fullname, '#'+fullname, descr];
             switch (match[2]) {
             case 1: objectResults.push(result); break;
             case 0: importantResults.push(result); break;
             case 2: unimportantResults.push(result); break;
             }
           }
         }
       }
     }
 
     // sort results descending
     objectResults.sort(function(a, b) {
       return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
     });
 
     importantResults.sort(function(a, b) {
       return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
     });
 
     unimportantResults.sort(function(a, b) {
       return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
     });
 
 
     // perform the search on the required terms
     for (var i = 0; i < searchterms.length; i++) {
       var word = searchterms[i];
       // no match but word was a required one
       if ((files = terms[word]) == null)
         break;
       if (files.length == undefined) {
         files = [files];
       }
       // create the mapping
       for (var j = 0; j < files.length; j++) {
         var file = files[j];
         if (file in fileMap)
           fileMap[file].push(word);
         else
           fileMap[file] = [word];
       }
     }
 
     // now check if the files don't contain excluded terms
     for (var file in fileMap) {
       var valid = true;
 
       // check if all requirements are matched
       if (fileMap[file].length != searchterms.length)
         continue;
 
       // ensure that none of the excluded terms is in the
       // search result.
       for (var i = 0; i < excluded.length; i++) {
         if (terms[excluded[i]] == file ||
             $.contains(terms[excluded[i]] || [], file)) {
           valid = false;
           break;
         }
       }
 
       // if we have still a valid result we can add it
       // to the result list
       if (valid)
         regularResults.push([filenames[file], titles[file], '', null]);
     }
 
     // delete unused variables in order to not waste
     // memory until list is retrieved completely
     delete filenames, titles, terms;
 
     // now sort the regular results descending by title
     regularResults.sort(function(a, b) {
       var left = a[1].toLowerCase();
       var right = b[1].toLowerCase();
       return (left > right) ? -1 : ((left < right) ? 1 : 0);
     });
 
     // combine all results
     var results = unimportantResults.concat(regularResults)
       .concat(objectResults).concat(importantResults);
 
     // print the results
     var resultCount = results.length;
     function displayNextItem() {
       // results left, load the summary and display it
       if (results.length) {
         var item = results.pop();
         var listItem = $('<li style="display:none"></li>');
         if (DOCUMENTATION_OPTIONS.FILE_SUFFIX == '') {
           // dirhtml builder
           var dirname = item[0] + '/';
           if (dirname.match(/\/index\/$/)) {
             dirname = dirname.substring(0, dirname.length-6);
           } else if (dirname == 'index/') {
             dirname = '';
           }
           listItem.append($('<a/>').attr('href',
             DOCUMENTATION_OPTIONS.URL_ROOT + dirname +
             highlightstring + item[2]).html(item[1]));
         } else {
           // normal html builders
           listItem.append($('<a/>').attr('href',
             item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
             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) {
           $.get(DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' +
                 item[0] + '.txt', function(data) {
             if (data != '') {
               listItem.append($.makeSearchSummary(data, searchterms, hlterms));
               Search.output.append(listItem);
             }
             listItem.slideDown(5, function() {
               displayNextItem();
             });
           });
         } else {
           // no source available, just display title
           Search.output.append(listItem);
           listItem.slideDown(5, function() {
             displayNextItem();
           });
         }
       }
       // search finished, update title and status message
       else {
         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.'));
         else
             Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
         Search.status.fadeIn(500);
       }
     }
     displayNextItem();
   }
 }
 
 $(document).ready(function() {
   Search.init();
 });