6127f67b07db53bc415cba8473ab933ccfeeb443
[mdictionary] / doc / html / search / search.js
1 // Search script generated by doxygen
2 // Copyright (C) 2009 by Dimitri van Heesch.
3
4 // The code in this file is loosly based on main.js, part of Natural Docs,
5 // which is Copyright (C) 2003-2008 Greg Valure
6 // Natural Docs is licensed under the GPL.
7
8 var indexSectionsWithContent =
9 {
10   0: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111110111011110011111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
11   1: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100110000100000110011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
12   2: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100110000100000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
13   3: "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111110111011110011111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
14 };
15
16 var indexSectionNames =
17 {
18   0: "all",
19   1: "classes",
20   2: "files",
21   3: "functions"
22 };
23
24 function convertToId(search)
25 {
26   var result = '';
27   for (i=0;i<search.length;i++)
28   {
29     var c = search.charAt(i);
30     var cn = c.charCodeAt(0);
31     if (c.match(/[a-z0-9]/))
32     {
33       result+=c;
34     }
35     else if (cn<16) 
36     {
37       result+="_0"+cn.toString(16);
38     }
39     else 
40     {
41       result+="_"+cn.toString(16);
42     }
43   }
44   return result;
45 }
46
47 function getXPos(item)
48 {
49   var x = 0;
50   if (item.offsetWidth)
51   {
52     while (item && item!=document.body)
53     {
54       x   += item.offsetLeft;
55       item = item.offsetParent;
56     }
57   }
58   return x;
59 }
60
61 function getYPos(item)
62 {
63   var y = 0;
64   if (item.offsetWidth)
65   {
66      while (item && item!=document.body)
67      {
68        y   += item.offsetTop;
69        item = item.offsetParent;
70      }
71   }
72   return y;
73 }
74
75 /* A class handling everything associated with the search panel.
76
77    Parameters:
78    name - The name of the global variable that will be 
79           storing this instance.  Is needed to be able to set timeouts.
80    resultPath - path to use for external files
81 */
82 function SearchBox(name, resultsPath, inFrame, label)
83 {
84   if (!name || !resultsPath) {  alert("Missing parameters to SearchBox."); }
85    
86   // ---------- Instance variables
87   this.name                  = name;
88   this.resultsPath           = resultsPath;
89   this.keyTimeout            = 0;
90   this.keyTimeoutLength      = 500;
91   this.closeSelectionTimeout = 300;
92   this.lastSearchValue       = "";
93   this.lastResultsPage       = "";
94   this.hideTimeout           = 0;
95   this.searchIndex           = 0;
96   this.searchActive          = false;
97   this.insideFrame           = inFrame;
98   this.searchLabel           = label;
99
100   // ----------- DOM Elements
101
102   this.DOMSearchField = function()
103   {  return document.getElementById("MSearchField");  }
104
105   this.DOMSearchSelect = function()
106   {  return document.getElementById("MSearchSelect");  }
107
108   this.DOMSearchSelectWindow = function()
109   {  return document.getElementById("MSearchSelectWindow");  }
110
111   this.DOMPopupSearchResults = function()
112   {  return document.getElementById("MSearchResults");  }
113
114   this.DOMPopupSearchResultsWindow = function()
115   {  return document.getElementById("MSearchResultsWindow");  }
116
117   this.DOMSearchClose = function()
118   {  return document.getElementById("MSearchClose"); }
119
120   this.DOMSearchBox = function()
121   {  return document.getElementById("MSearchBox");  }
122
123   // ------------ Event Handlers
124
125   // Called when focus is added or removed from the search field.
126   this.OnSearchFieldFocus = function(isActive)
127   {
128     this.Activate(isActive);
129   }
130
131   this.OnSearchSelectShow = function()
132   {
133     var searchSelectWindow = this.DOMSearchSelectWindow();
134     var searchField        = this.DOMSearchSelect();
135
136     if (this.insideFrame)
137     {
138       var left = getXPos(searchField);
139       var top  = getYPos(searchField);
140       left += searchField.offsetWidth + 6;
141       top += searchField.offsetHeight;
142
143       // show search selection popup
144       searchSelectWindow.style.display='block';
145       left -= searchSelectWindow.offsetWidth;
146       searchSelectWindow.style.left =  left + 'px';
147       searchSelectWindow.style.top  =  top  + 'px';
148     }
149     else
150     {
151       var left = getXPos(searchField);
152       var top  = getYPos(searchField);
153       top += searchField.offsetHeight;
154
155       // show search selection popup
156       searchSelectWindow.style.display='block';
157       searchSelectWindow.style.left =  left + 'px';
158       searchSelectWindow.style.top  =  top  + 'px';
159     }
160
161     // stop selection hide timer
162     if (this.hideTimeout) 
163     {
164       clearTimeout(this.hideTimeout);
165       this.hideTimeout=0;
166     }
167     return false; // to avoid "image drag" default event
168   }
169
170   this.OnSearchSelectHide = function()
171   {
172     this.hideTimeout = setTimeout(this.name +".CloseSelectionWindow()",
173                                   this.closeSelectionTimeout);
174   }
175
176   // Called when the content of the search field is changed.
177   this.OnSearchFieldChange = function(evt)
178   {
179     if (this.keyTimeout) // kill running timer
180     {
181       clearTimeout(this.keyTimeout);
182       this.keyTimeout = 0;
183     }
184
185     var e  = (evt) ? evt : window.event; // for IE
186     if (e.keyCode==40 || e.keyCode==13)
187     {
188       if (e.shiftKey==1)
189       {
190         this.OnSearchSelectShow();
191         var win=this.DOMSearchSelectWindow(); 
192         for (i=0;i<win.childNodes.length;i++)
193         {
194           var child = win.childNodes[i]; // get span within a
195           if (child.className=='SelectItem')
196           {
197             child.focus();
198             return;
199           }
200         }
201         return;
202       }
203       else if (window.frames.MSearchResults.searchResults)
204       {
205         var elem = window.frames.MSearchResults.searchResults.NavNext(0);
206         if (elem) elem.focus();
207       }
208     }
209     else if (e.keyCode==27) // Escape out of the search field
210     {
211       this.DOMSearchField().blur();
212       this.DOMPopupSearchResultsWindow().style.display = 'none';
213       this.DOMSearchClose().style.display = 'none';
214       this.lastSearchValue = '';
215       this.Activate(false);
216       return;
217     }
218
219     // strip whitespaces
220     var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
221
222     if (searchValue != this.lastSearchValue) // search value has changed
223     {
224       if (searchValue != "") // non-empty search
225       {
226         // set timer for search update
227         this.keyTimeout = setTimeout(this.name + '.Search()',
228                                      this.keyTimeoutLength);
229       }
230       else // empty search field
231       {
232         this.DOMPopupSearchResultsWindow().style.display = 'none';
233         this.DOMSearchClose().style.display = 'none';
234         this.lastSearchValue = '';
235       }
236     }
237   }
238
239   this.SelectItemCount = function(id)
240   {
241     var count=0;
242     var win=this.DOMSearchSelectWindow(); 
243     for (i=0;i<win.childNodes.length;i++)
244     {
245       var child = win.childNodes[i]; // get span within a
246       if (child.className=='SelectItem')
247       {
248         count++;
249       }
250     }
251     return count;
252   }
253
254   this.SelectItemSet = function(id)
255   {
256     var i,j=0;
257     var win=this.DOMSearchSelectWindow(); 
258     for (i=0;i<win.childNodes.length;i++)
259     {
260       var child = win.childNodes[i]; // get span within a
261       if (child.className=='SelectItem')
262       {
263         var node = child.firstChild;
264         if (j==id)
265         {
266           node.innerHTML='&bull;';
267         }
268         else
269         {
270           node.innerHTML='&nbsp;';
271         }
272         j++;
273       }
274     }
275   }
276
277   // Called when an search filter selection is made.
278   // set item with index id as the active item
279   this.OnSelectItem = function(id)
280   {
281     this.searchIndex = id;
282     this.SelectItemSet(id);
283     var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
284     if (searchValue!="" && this.searchActive) // something was found -> do a search
285     {
286       this.Search();
287     }
288   }
289
290   this.OnSearchSelectKey = function(evt)
291   {
292     var e = (evt) ? evt : window.event; // for IE
293     if (e.keyCode==40 && this.searchIndex<this.SelectItemCount()) // Down
294     {
295       this.searchIndex++;
296       this.OnSelectItem(this.searchIndex);
297     }
298     else if (e.keyCode==38 && this.searchIndex>0) // Up
299     {
300       this.searchIndex--;
301       this.OnSelectItem(this.searchIndex);
302     }
303     else if (e.keyCode==13 || e.keyCode==27)
304     {
305       this.OnSelectItem(this.searchIndex);
306       this.CloseSelectionWindow();
307       this.DOMSearchField().focus();
308     }
309     return false;
310   }
311
312   // --------- Actions
313
314   // Closes the results window.
315   this.CloseResultsWindow = function()
316   {
317     this.DOMPopupSearchResultsWindow().style.display = 'none';
318     this.DOMSearchClose().style.display = 'none';
319     this.Activate(false);
320   }
321
322   this.CloseSelectionWindow = function()
323   {
324     this.DOMSearchSelectWindow().style.display = 'none';
325   }
326
327   // Performs a search.
328   this.Search = function()
329   {
330     this.keyTimeout = 0;
331
332     // strip leading whitespace
333     var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
334
335     var code = searchValue.toLowerCase().charCodeAt(0);
336     var hexCode;
337     if (code<16) 
338     {
339       hexCode="0"+code.toString(16);
340     }
341     else 
342     {
343       hexCode=code.toString(16);
344     }
345
346     var resultsPage;
347     var resultsPageWithSearch;
348     var hasResultsPage;
349
350     if (indexSectionsWithContent[this.searchIndex].charAt(code) == '1')
351     {
352        resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html';
353        resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
354        hasResultsPage = true;
355     }
356     else // nothing available for this search term
357     {
358        resultsPage = this.resultsPath + '/nomatches.html';
359        resultsPageWithSearch = resultsPage;
360        hasResultsPage = false;
361     }
362
363     window.frames.MSearchResults.location.href = resultsPageWithSearch;  
364     var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow();
365
366     if (domPopupSearchResultsWindow.style.display!='block')
367     {
368        var domSearchBox = this.DOMSearchBox();
369        this.DOMSearchClose().style.display = 'inline';
370        if (this.insideFrame)
371        {
372          var domPopupSearchResults = this.DOMPopupSearchResults();
373          domPopupSearchResultsWindow.style.position = 'relative';
374          domPopupSearchResultsWindow.style.display  = 'block';
375          var width = document.body.clientWidth - 8; // the -8 is for IE :-(
376          domPopupSearchResultsWindow.style.width    = width + 'px';
377          domPopupSearchResults.style.width          = width + 'px';
378        }
379        else
380        {
381          var domPopupSearchResults = this.DOMPopupSearchResults();
382          var left = getXPos(domSearchBox) + domSearchBox.offsetWidth;
383          var top  = getYPos(domSearchBox) + domSearchBox.offsetHeight + 1;
384          domPopupSearchResultsWindow.style.display = 'block';
385          left -= domPopupSearchResults.offsetWidth;
386          domPopupSearchResultsWindow.style.top     = top  + 'px';
387          domPopupSearchResultsWindow.style.left    = left + 'px';
388        }
389     }
390
391     this.lastSearchValue = searchValue;
392     this.lastResultsPage = resultsPage;
393   }
394
395   // -------- Activation Functions
396
397   // Activates or deactivates the search panel, resetting things to 
398   // their default values if necessary. 
399   this.Activate = function(isActive)
400   {
401     if (isActive || // open it
402         this.DOMPopupSearchResultsWindow().style.display == 'block' 
403        )
404     {
405       this.DOMSearchBox().className = 'MSearchBoxActive';
406
407       var searchField = this.DOMSearchField();
408
409       if (searchField.value == this.searchLabel) // clear "Search" term upon entry
410       {  
411         searchField.value = '';  
412         this.searchActive = true;
413       }
414     }
415     else if (!isActive) // directly remove the panel
416     {
417       this.DOMSearchBox().className = 'MSearchBoxInactive';
418       this.DOMSearchField().value   = this.searchLabel;
419       this.searchActive             = false;
420       this.lastSearchValue          = ''
421       this.lastResultsPage          = '';
422     }
423   }
424 }
425
426 // -----------------------------------------------------------------------
427
428 // The class that handles everything on the search results page.
429 function SearchResults(name)
430 {
431     // The number of matches from the last run of <Search()>.
432     this.lastMatchCount = 0;
433     this.lastKey = 0;
434     this.repeatOn = false;
435
436     // Toggles the visibility of the passed element ID.
437     this.FindChildElement = function(id)
438     {
439       var parentElement = document.getElementById(id);
440       var element = parentElement.firstChild;
441
442       while (element && element!=parentElement)
443       {
444         if (element.nodeName == 'DIV' && element.className == 'SRChildren')
445         {
446           return element;
447         }
448
449         if (element.nodeName == 'DIV' && element.hasChildNodes())
450         {  
451            element = element.firstChild;  
452         }
453         else if (element.nextSibling)
454         {  
455            element = element.nextSibling;  
456         }
457         else
458         {
459           do
460           {
461             element = element.parentNode;
462           }
463           while (element && element!=parentElement && !element.nextSibling);
464
465           if (element && element!=parentElement)
466           {  
467             element = element.nextSibling;  
468           }
469         }
470       }
471     }
472
473     this.Toggle = function(id)
474     {
475       var element = this.FindChildElement(id);
476       if (element)
477       {
478         if (element.style.display == 'block')
479         {
480           element.style.display = 'none';
481         }
482         else
483         {
484           element.style.display = 'block';
485         }
486       }
487     }
488
489     // Searches for the passed string.  If there is no parameter,
490     // it takes it from the URL query.
491     //
492     // Always returns true, since other documents may try to call it
493     // and that may or may not be possible.
494     this.Search = function(search)
495     {
496       if (!search) // get search word from URL
497       {
498         search = window.location.search;
499         search = search.substring(1);  // Remove the leading '?'
500         search = unescape(search);
501       }
502
503       search = search.replace(/^ +/, ""); // strip leading spaces
504       search = search.replace(/ +$/, ""); // strip trailing spaces
505       search = search.toLowerCase();
506       search = convertToId(search);
507
508       var resultRows = document.getElementsByTagName("div");
509       var matches = 0;
510
511       var i = 0;
512       while (i < resultRows.length)
513       {
514         var row = resultRows.item(i);
515         if (row.className == "SRResult")
516         {
517           var rowMatchName = row.id.toLowerCase();
518           rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_'
519
520           if (search.length<=rowMatchName.length && 
521              rowMatchName.substr(0, search.length)==search)
522           {
523             row.style.display = 'block';
524             matches++;
525           }
526           else
527           {
528             row.style.display = 'none';
529           }
530         }
531         i++;
532       }
533       document.getElementById("Searching").style.display='none';
534       if (matches == 0) // no results
535       {
536         document.getElementById("NoMatches").style.display='block';
537       }
538       else // at least one result
539       {
540         document.getElementById("NoMatches").style.display='none';
541       }
542       this.lastMatchCount = matches;
543       return true;
544     }
545
546     // return the first item with index index or higher that is visible
547     this.NavNext = function(index)
548     {
549       var focusItem;
550       while (1)
551       {
552         var focusName = 'Item'+index;
553         focusItem = document.getElementById(focusName);
554         if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
555         {
556           break;
557         }
558         else if (!focusItem) // last element
559         {
560           break;
561         }
562         focusItem=null;
563         index++;
564       }
565       return focusItem;
566     }
567
568     this.NavPrev = function(index)
569     {
570       var focusItem;
571       while (1)
572       {
573         var focusName = 'Item'+index;
574         focusItem = document.getElementById(focusName);
575         if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
576         {
577           break;
578         }
579         else if (!focusItem) // last element
580         {
581           break;
582         }
583         focusItem=null;
584         index--;
585       }
586       return focusItem;
587     }
588
589     this.ProcessKeys = function(e)
590     {
591       if (e.type == "keydown") 
592       {
593         this.repeatOn = false;
594         this.lastKey = e.keyCode;
595       }
596       else if (e.type == "keypress")
597       {
598         if (!this.repeatOn)
599         {
600           if (this.lastKey) this.repeatOn = true;
601           return false; // ignore first keypress after keydown
602         }
603       }
604       else if (e.type == "keyup")
605       {
606         this.lastKey = 0;
607         this.repeatOn = false;
608       }
609       return this.lastKey!=0;
610     }
611
612     this.Nav = function(evt,itemIndex) 
613     {
614       var e  = (evt) ? evt : window.event; // for IE
615       if (e.keyCode==13) return true;
616       if (!this.ProcessKeys(e)) return false;
617
618       if (this.lastKey==38) // Up
619       {
620         var newIndex = itemIndex-1;
621         var focusItem = this.NavPrev(newIndex);
622         if (focusItem)
623         {
624           var child = this.FindChildElement(focusItem.parentNode.parentNode.id);
625           if (child && child.style.display == 'block') // children visible
626           { 
627             var n=0;
628             var tmpElem;
629             while (1) // search for last child
630             {
631               tmpElem = document.getElementById('Item'+newIndex+'_c'+n);
632               if (tmpElem)
633               {
634                 focusItem = tmpElem;
635               }
636               else // found it!
637               {
638                 break;
639               }
640               n++;
641             }
642           }
643         }
644         if (focusItem)
645         {
646           focusItem.focus();
647         }
648         else // return focus to search field
649         {
650            parent.document.getElementById("MSearchField").focus();
651         }
652       }
653       else if (this.lastKey==40) // Down
654       {
655         var newIndex = itemIndex+1;
656         var focusItem;
657         var item = document.getElementById('Item'+itemIndex);
658         var elem = this.FindChildElement(item.parentNode.parentNode.id);
659         if (elem && elem.style.display == 'block') // children visible
660         {
661           focusItem = document.getElementById('Item'+itemIndex+'_c0');
662         }
663         if (!focusItem) focusItem = this.NavNext(newIndex);
664         if (focusItem)  focusItem.focus();
665       }
666       else if (this.lastKey==39) // Right
667       {
668         var item = document.getElementById('Item'+itemIndex);
669         var elem = this.FindChildElement(item.parentNode.parentNode.id);
670         if (elem) elem.style.display = 'block';
671       }
672       else if (this.lastKey==37) // Left
673       {
674         var item = document.getElementById('Item'+itemIndex);
675         var elem = this.FindChildElement(item.parentNode.parentNode.id);
676         if (elem) elem.style.display = 'none';
677       }
678       else if (this.lastKey==27) // Escape
679       {
680         parent.searchBox.CloseResultsWindow();
681         parent.document.getElementById("MSearchField").focus();
682       }
683       else if (this.lastKey==13) // Enter
684       {
685         return true;
686       }
687       return false;
688     }
689
690     this.NavChild = function(evt,itemIndex,childIndex)
691     {
692       var e  = (evt) ? evt : window.event; // for IE
693       if (e.keyCode==13) return true;
694       if (!this.ProcessKeys(e)) return false;
695
696       if (this.lastKey==38) // Up
697       {
698         if (childIndex>0)
699         {
700           var newIndex = childIndex-1;
701           document.getElementById('Item'+itemIndex+'_c'+newIndex).focus();
702         }
703         else // already at first child, jump to parent
704         {
705           document.getElementById('Item'+itemIndex).focus();
706         }
707       }
708       else if (this.lastKey==40) // Down
709       {
710         var newIndex = childIndex+1;
711         var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex);
712         if (!elem) // last child, jump to parent next parent
713         {
714           elem = this.NavNext(itemIndex+1);
715         }
716         if (elem)
717         {
718           elem.focus();
719         } 
720       }
721       else if (this.lastKey==27) // Escape
722       {
723         parent.searchBox.CloseResultsWindow();
724         parent.document.getElementById("MSearchField").focus();
725       }
726       else if (this.lastKey==13) // Enter
727       {
728         return true;
729       }
730       return false;
731     }
732 }