• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 @licstart  The following is the entire license notice for the JavaScript code in this file.
3
4 The MIT License (MIT)
5
6 Copyright (C) 1997-2020 by Dimitri van Heesch
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy of this software
9 and associated documentation files (the "Software"), to deal in the Software without restriction,
10 including without limitation the rights to use, copy, modify, merge, publish, distribute,
11 sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in all copies or
15 substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
18 BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
23 @licend  The above is the entire license notice for the JavaScript code in this file
24 */
25function convertToId(search)
26{
27  var result = '';
28  for (i=0;i<search.length;i++)
29  {
30    var c = search.charAt(i);
31    var cn = c.charCodeAt(0);
32    if (c.match(/[a-z0-9\u0080-\uFFFF]/))
33    {
34      result+=c;
35    }
36    else if (cn<16)
37    {
38      result+="_0"+cn.toString(16);
39    }
40    else
41    {
42      result+="_"+cn.toString(16);
43    }
44  }
45  return result;
46}
47
48function getXPos(item)
49{
50  var x = 0;
51  if (item.offsetWidth)
52  {
53    while (item && item!=document.body)
54    {
55      x   += item.offsetLeft;
56      item = item.offsetParent;
57    }
58  }
59  return x;
60}
61
62function getYPos(item)
63{
64  var y = 0;
65  if (item.offsetWidth)
66  {
67     while (item && item!=document.body)
68     {
69       y   += item.offsetTop;
70       item = item.offsetParent;
71     }
72  }
73  return y;
74}
75
76var searchResults = new SearchResults("searchResults");
77
78/* A class handling everything associated with the search panel.
79
80   Parameters:
81   name - The name of the global variable that will be
82          storing this instance.  Is needed to be able to set timeouts.
83   resultPath - path to use for external files
84*/
85function SearchBox(name, resultsPath, extension)
86{
87  if (!name || !resultsPath) {  alert("Missing parameters to SearchBox."); }
88  if (!extension || extension == "") { extension = ".html"; }
89
90  // ---------- Instance variables
91  this.name                  = name;
92  this.resultsPath           = resultsPath;
93  this.keyTimeout            = 0;
94  this.keyTimeoutLength      = 500;
95  this.closeSelectionTimeout = 300;
96  this.lastSearchValue       = "";
97  this.lastResultsPage       = "";
98  this.hideTimeout           = 0;
99  this.searchIndex           = 0;
100  this.searchActive          = false;
101  this.extension             = extension;
102
103  // ----------- DOM Elements
104
105  this.DOMSearchField = function()
106  {  return document.getElementById("MSearchField");  }
107
108  this.DOMSearchSelect = function()
109  {  return document.getElementById("MSearchSelect");  }
110
111  this.DOMSearchSelectWindow = function()
112  {  return document.getElementById("MSearchSelectWindow");  }
113
114  this.DOMPopupSearchResults = function()
115  {  return document.getElementById("MSearchResults");  }
116
117  this.DOMPopupSearchResultsWindow = function()
118  {  return document.getElementById("MSearchResultsWindow");  }
119
120  this.DOMSearchClose = function()
121  {  return document.getElementById("MSearchClose"); }
122
123  this.DOMSearchBox = function()
124  {  return document.getElementById("MSearchBox");  }
125
126  // ------------ Event Handlers
127
128  // Called when focus is added or removed from the search field.
129  this.OnSearchFieldFocus = function(isActive)
130  {
131    this.Activate(isActive);
132  }
133
134  this.OnSearchSelectShow = function()
135  {
136    var searchSelectWindow = this.DOMSearchSelectWindow();
137    var searchField        = this.DOMSearchSelect();
138
139    var left = getXPos(searchField);
140    var top  = getYPos(searchField);
141    top += searchField.offsetHeight;
142
143    // show search selection popup
144    searchSelectWindow.style.display='block';
145    searchSelectWindow.style.left =  left + 'px';
146    searchSelectWindow.style.top  =  top  + 'px';
147
148    // stop selection hide timer
149    if (this.hideTimeout)
150    {
151      clearTimeout(this.hideTimeout);
152      this.hideTimeout=0;
153    }
154    return false; // to avoid "image drag" default event
155  }
156
157  this.OnSearchSelectHide = function()
158  {
159    this.hideTimeout = setTimeout(this.name +".CloseSelectionWindow()",
160                                  this.closeSelectionTimeout);
161  }
162
163  // Called when the content of the search field is changed.
164  this.OnSearchFieldChange = function(evt)
165  {
166    if (this.keyTimeout) // kill running timer
167    {
168      clearTimeout(this.keyTimeout);
169      this.keyTimeout = 0;
170    }
171
172    var e  = (evt) ? evt : window.event; // for IE
173    if (e.keyCode==40 || e.keyCode==13)
174    {
175      if (e.shiftKey==1)
176      {
177        this.OnSearchSelectShow();
178        var win=this.DOMSearchSelectWindow();
179        for (i=0;i<win.childNodes.length;i++)
180        {
181          var child = win.childNodes[i]; // get span within a
182          if (child.className=='SelectItem')
183          {
184            child.focus();
185            return;
186          }
187        }
188        return;
189      }
190      else
191      {
192        var elem = searchResults.NavNext(0);
193        if (elem) elem.focus();
194      }
195    }
196    else if (e.keyCode==27) // Escape out of the search field
197    {
198      this.DOMSearchField().blur();
199      this.DOMPopupSearchResultsWindow().style.display = 'none';
200      this.DOMSearchClose().style.display = 'none';
201      this.lastSearchValue = '';
202      this.Activate(false);
203      return;
204    }
205
206    // strip whitespaces
207    var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
208
209    if (searchValue != this.lastSearchValue) // search value has changed
210    {
211      if (searchValue != "") // non-empty search
212      {
213        // set timer for search update
214        this.keyTimeout = setTimeout(this.name + '.Search()',
215                                     this.keyTimeoutLength);
216      }
217      else // empty search field
218      {
219        this.DOMPopupSearchResultsWindow().style.display = 'none';
220        this.DOMSearchClose().style.display = 'none';
221        this.lastSearchValue = '';
222      }
223    }
224  }
225
226  this.SelectItemCount = function(id)
227  {
228    var count=0;
229    var win=this.DOMSearchSelectWindow();
230    for (i=0;i<win.childNodes.length;i++)
231    {
232      var child = win.childNodes[i]; // get span within a
233      if (child.className=='SelectItem')
234      {
235        count++;
236      }
237    }
238    return count;
239  }
240
241  this.SelectItemSet = function(id)
242  {
243    var i,j=0;
244    var win=this.DOMSearchSelectWindow();
245    for (i=0;i<win.childNodes.length;i++)
246    {
247      var child = win.childNodes[i]; // get span within a
248      if (child.className=='SelectItem')
249      {
250        var node = child.firstChild;
251        if (j==id)
252        {
253          node.innerHTML='&#8226;';
254        }
255        else
256        {
257          node.innerHTML='&#160;';
258        }
259        j++;
260      }
261    }
262  }
263
264  // Called when an search filter selection is made.
265  // set item with index id as the active item
266  this.OnSelectItem = function(id)
267  {
268    this.searchIndex = id;
269    this.SelectItemSet(id);
270    var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
271    if (searchValue!="" && this.searchActive) // something was found -> do a search
272    {
273      this.Search();
274    }
275  }
276
277  this.OnSearchSelectKey = function(evt)
278  {
279    var e = (evt) ? evt : window.event; // for IE
280    if (e.keyCode==40 && this.searchIndex<this.SelectItemCount()) // Down
281    {
282      this.searchIndex++;
283      this.OnSelectItem(this.searchIndex);
284    }
285    else if (e.keyCode==38 && this.searchIndex>0) // Up
286    {
287      this.searchIndex--;
288      this.OnSelectItem(this.searchIndex);
289    }
290    else if (e.keyCode==13 || e.keyCode==27)
291    {
292      this.OnSelectItem(this.searchIndex);
293      this.CloseSelectionWindow();
294      this.DOMSearchField().focus();
295    }
296    return false;
297  }
298
299  // --------- Actions
300
301  // Closes the results window.
302  this.CloseResultsWindow = function()
303  {
304    this.DOMPopupSearchResultsWindow().style.display = 'none';
305    this.DOMSearchClose().style.display = 'none';
306    this.Activate(false);
307  }
308
309  this.CloseSelectionWindow = function()
310  {
311    this.DOMSearchSelectWindow().style.display = 'none';
312  }
313
314  // Performs a search.
315  this.Search = function()
316  {
317    this.keyTimeout = 0;
318
319    // strip leading whitespace
320    var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
321
322    var code = searchValue.toLowerCase().charCodeAt(0);
323    var idxChar = searchValue.substr(0, 1).toLowerCase();
324    if ( 0xD800 <= code && code <= 0xDBFF && searchValue > 1) // surrogate pair
325    {
326      idxChar = searchValue.substr(0, 2);
327    }
328
329    var jsFile;
330
331    var idx = indexSectionsWithContent[this.searchIndex].indexOf(idxChar);
332    if (idx!=-1)
333    {
334       var hexCode=idx.toString(16);
335       jsFile = this.resultsPath + indexSectionNames[this.searchIndex] + '_' + hexCode + '.js';
336    }
337
338    var loadJS = function(url, impl, loc){
339      var scriptTag = document.createElement('script');
340      scriptTag.src = url;
341      scriptTag.onload = impl;
342      scriptTag.onreadystatechange = impl;
343      loc.appendChild(scriptTag);
344    }
345
346    var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow();
347    var domSearchBox = this.DOMSearchBox();
348    var domPopupSearchResults = this.DOMPopupSearchResults();
349    var domSearchClose = this.DOMSearchClose();
350    var resultsPath = this.resultsPath;
351
352    var handleResults = function() {
353      document.getElementById("Loading").style.display="none";
354      if (typeof searchData !== 'undefined') {
355        createResults(resultsPath);
356        document.getElementById("NoMatches").style.display="none";
357      }
358
359      searchResults.Search(searchValue);
360
361      if (domPopupSearchResultsWindow.style.display!='block')
362      {
363        domSearchClose.style.display = 'inline-block';
364        var left = getXPos(domSearchBox) + 150;
365        var top  = getYPos(domSearchBox) + 20;
366        domPopupSearchResultsWindow.style.display = 'block';
367        left -= domPopupSearchResults.offsetWidth;
368        var maxWidth  = document.body.clientWidth;
369        var maxHeight = document.body.clientHeight;
370        var width = 300;
371        if (left<10) left=10;
372        if (width+left+8>maxWidth) width=maxWidth-left-8;
373        var height = 400;
374        if (height+top+8>maxHeight) height=maxHeight-top-8;
375        domPopupSearchResultsWindow.style.top     = top  + 'px';
376        domPopupSearchResultsWindow.style.left    = left + 'px';
377        domPopupSearchResultsWindow.style.width   = width + 'px';
378        domPopupSearchResultsWindow.style.height  = height + 'px';
379      }
380    }
381
382    if (jsFile) {
383      loadJS(jsFile, handleResults, this.DOMPopupSearchResultsWindow());
384    } else {
385      handleResults();
386    }
387
388    this.lastSearchValue = searchValue;
389  }
390
391  // -------- Activation Functions
392
393  // Activates or deactivates the search panel, resetting things to
394  // their default values if necessary.
395  this.Activate = function(isActive)
396  {
397    if (isActive || // open it
398        this.DOMPopupSearchResultsWindow().style.display == 'block'
399       )
400    {
401      this.DOMSearchBox().className = 'MSearchBoxActive';
402      this.searchActive = true;
403    }
404    else if (!isActive) // directly remove the panel
405    {
406      this.DOMSearchBox().className = 'MSearchBoxInactive';
407      this.searchActive             = false;
408      this.lastSearchValue          = ''
409      this.lastResultsPage          = '';
410      this.DOMSearchField().value   = '';
411    }
412  }
413}
414
415// -----------------------------------------------------------------------
416
417// The class that handles everything on the search results page.
418function SearchResults(name)
419{
420    // The number of matches from the last run of <Search()>.
421    this.lastMatchCount = 0;
422    this.lastKey = 0;
423    this.repeatOn = false;
424
425    // Toggles the visibility of the passed element ID.
426    this.FindChildElement = function(id)
427    {
428      var parentElement = document.getElementById(id);
429      var element = parentElement.firstChild;
430
431      while (element && element!=parentElement)
432      {
433        if (element.nodeName.toLowerCase() == 'div' && element.className == 'SRChildren')
434        {
435          return element;
436        }
437
438        if (element.nodeName.toLowerCase() == 'div' && element.hasChildNodes())
439        {
440           element = element.firstChild;
441        }
442        else if (element.nextSibling)
443        {
444           element = element.nextSibling;
445        }
446        else
447        {
448          do
449          {
450            element = element.parentNode;
451          }
452          while (element && element!=parentElement && !element.nextSibling);
453
454          if (element && element!=parentElement)
455          {
456            element = element.nextSibling;
457          }
458        }
459      }
460    }
461
462    this.Toggle = function(id)
463    {
464      var element = this.FindChildElement(id);
465      if (element)
466      {
467        if (element.style.display == 'block')
468        {
469          element.style.display = 'none';
470        }
471        else
472        {
473          element.style.display = 'block';
474        }
475      }
476    }
477
478    // Searches for the passed string.  If there is no parameter,
479    // it takes it from the URL query.
480    //
481    // Always returns true, since other documents may try to call it
482    // and that may or may not be possible.
483    this.Search = function(search)
484    {
485      if (!search) // get search word from URL
486      {
487        search = window.location.search;
488        search = search.substring(1);  // Remove the leading '?'
489        search = unescape(search);
490      }
491
492      search = search.replace(/^ +/, ""); // strip leading spaces
493      search = search.replace(/ +$/, ""); // strip trailing spaces
494      search = search.toLowerCase();
495      search = convertToId(search);
496
497      var resultRows = document.getElementsByTagName("div");
498      var matches = 0;
499
500      var i = 0;
501      while (i < resultRows.length)
502      {
503        var row = resultRows.item(i);
504        if (row.className == "SRResult")
505        {
506          var rowMatchName = row.id.toLowerCase();
507          rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_'
508
509          if (search.length<=rowMatchName.length &&
510             rowMatchName.substr(0, search.length)==search)
511          {
512            row.style.display = 'block';
513            matches++;
514          }
515          else
516          {
517            row.style.display = 'none';
518          }
519        }
520        i++;
521      }
522      document.getElementById("Searching").style.display='none';
523      if (matches == 0) // no results
524      {
525        document.getElementById("NoMatches").style.display='block';
526      }
527      else // at least one result
528      {
529        document.getElementById("NoMatches").style.display='none';
530      }
531      this.lastMatchCount = matches;
532      return true;
533    }
534
535    // return the first item with index index or higher that is visible
536    this.NavNext = function(index)
537    {
538      var focusItem;
539      while (1)
540      {
541        var focusName = 'Item'+index;
542        focusItem = document.getElementById(focusName);
543        if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
544        {
545          break;
546        }
547        else if (!focusItem) // last element
548        {
549          break;
550        }
551        focusItem=null;
552        index++;
553      }
554      return focusItem;
555    }
556
557    this.NavPrev = function(index)
558    {
559      var focusItem;
560      while (1)
561      {
562        var focusName = 'Item'+index;
563        focusItem = document.getElementById(focusName);
564        if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
565        {
566          break;
567        }
568        else if (!focusItem) // last element
569        {
570          break;
571        }
572        focusItem=null;
573        index--;
574      }
575      return focusItem;
576    }
577
578    this.ProcessKeys = function(e)
579    {
580      if (e.type == "keydown")
581      {
582        this.repeatOn = false;
583        this.lastKey = e.keyCode;
584      }
585      else if (e.type == "keypress")
586      {
587        if (!this.repeatOn)
588        {
589          if (this.lastKey) this.repeatOn = true;
590          return false; // ignore first keypress after keydown
591        }
592      }
593      else if (e.type == "keyup")
594      {
595        this.lastKey = 0;
596        this.repeatOn = false;
597      }
598      return this.lastKey!=0;
599    }
600
601    this.Nav = function(evt,itemIndex)
602    {
603      var e  = (evt) ? evt : window.event; // for IE
604      if (e.keyCode==13) return true;
605      if (!this.ProcessKeys(e)) return false;
606
607      if (this.lastKey==38) // Up
608      {
609        var newIndex = itemIndex-1;
610        var focusItem = this.NavPrev(newIndex);
611        if (focusItem)
612        {
613          var child = this.FindChildElement(focusItem.parentNode.parentNode.id);
614          if (child && child.style.display == 'block') // children visible
615          {
616            var n=0;
617            var tmpElem;
618            while (1) // search for last child
619            {
620              tmpElem = document.getElementById('Item'+newIndex+'_c'+n);
621              if (tmpElem)
622              {
623                focusItem = tmpElem;
624              }
625              else // found it!
626              {
627                break;
628              }
629              n++;
630            }
631          }
632        }
633        if (focusItem)
634        {
635          focusItem.focus();
636        }
637        else // return focus to search field
638        {
639           document.getElementById("MSearchField").focus();
640        }
641      }
642      else if (this.lastKey==40) // Down
643      {
644        var newIndex = itemIndex+1;
645        var focusItem;
646        var item = document.getElementById('Item'+itemIndex);
647        var elem = this.FindChildElement(item.parentNode.parentNode.id);
648        if (elem && elem.style.display == 'block') // children visible
649        {
650          focusItem = document.getElementById('Item'+itemIndex+'_c0');
651        }
652        if (!focusItem) focusItem = this.NavNext(newIndex);
653        if (focusItem)  focusItem.focus();
654      }
655      else if (this.lastKey==39) // Right
656      {
657        var item = document.getElementById('Item'+itemIndex);
658        var elem = this.FindChildElement(item.parentNode.parentNode.id);
659        if (elem) elem.style.display = 'block';
660      }
661      else if (this.lastKey==37) // Left
662      {
663        var item = document.getElementById('Item'+itemIndex);
664        var elem = this.FindChildElement(item.parentNode.parentNode.id);
665        if (elem) elem.style.display = 'none';
666      }
667      else if (this.lastKey==27) // Escape
668      {
669        searchBox.CloseResultsWindow();
670        document.getElementById("MSearchField").focus();
671      }
672      else if (this.lastKey==13) // Enter
673      {
674        return true;
675      }
676      return false;
677    }
678
679    this.NavChild = function(evt,itemIndex,childIndex)
680    {
681      var e  = (evt) ? evt : window.event; // for IE
682      if (e.keyCode==13) return true;
683      if (!this.ProcessKeys(e)) return false;
684
685      if (this.lastKey==38) // Up
686      {
687        if (childIndex>0)
688        {
689          var newIndex = childIndex-1;
690          document.getElementById('Item'+itemIndex+'_c'+newIndex).focus();
691        }
692        else // already at first child, jump to parent
693        {
694          document.getElementById('Item'+itemIndex).focus();
695        }
696      }
697      else if (this.lastKey==40) // Down
698      {
699        var newIndex = childIndex+1;
700        var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex);
701        if (!elem) // last child, jump to parent next parent
702        {
703          elem = this.NavNext(itemIndex+1);
704        }
705        if (elem)
706        {
707          elem.focus();
708        }
709      }
710      else if (this.lastKey==27) // Escape
711      {
712        searchBox.CloseResultsWindow();
713        document.getElementById("MSearchField").focus();
714      }
715      else if (this.lastKey==13) // Enter
716      {
717        return true;
718      }
719      return false;
720    }
721}
722
723function setKeyActions(elem,action)
724{
725  elem.setAttribute('onkeydown',action);
726  elem.setAttribute('onkeypress',action);
727  elem.setAttribute('onkeyup',action);
728}
729
730function setClassAttr(elem,attr)
731{
732  elem.setAttribute('class',attr);
733  elem.setAttribute('className',attr);
734}
735
736function createResults(resultsPath)
737{
738  var results = document.getElementById("SRResults");
739  results.innerHTML = '';
740  for (var e=0; e<searchData.length; e++)
741  {
742    var id = searchData[e][0];
743    var srResult = document.createElement('div');
744    srResult.setAttribute('id','SR_'+id);
745    setClassAttr(srResult,'SRResult');
746    var srEntry = document.createElement('div');
747    setClassAttr(srEntry,'SREntry');
748    var srLink = document.createElement('a');
749    srLink.setAttribute('id','Item'+e);
750    setKeyActions(srLink,'return searchResults.Nav(event,'+e+')');
751    setClassAttr(srLink,'SRSymbol');
752    srLink.innerHTML = searchData[e][1][0];
753    srEntry.appendChild(srLink);
754    if (searchData[e][1].length==2) // single result
755    {
756      srLink.setAttribute('href',resultsPath+searchData[e][1][1][0]);
757      srLink.setAttribute('onclick','searchBox.CloseResultsWindow()');
758      if (searchData[e][1][1][1])
759      {
760       srLink.setAttribute('target','_parent');
761      }
762      else
763      {
764       srLink.setAttribute('target','_blank');
765      }
766      var srScope = document.createElement('span');
767      setClassAttr(srScope,'SRScope');
768      srScope.innerHTML = searchData[e][1][1][2];
769      srEntry.appendChild(srScope);
770    }
771    else // multiple results
772    {
773      srLink.setAttribute('href','javascript:searchResults.Toggle("SR_'+id+'")');
774      var srChildren = document.createElement('div');
775      setClassAttr(srChildren,'SRChildren');
776      for (var c=0; c<searchData[e][1].length-1; c++)
777      {
778        var srChild = document.createElement('a');
779        srChild.setAttribute('id','Item'+e+'_c'+c);
780        setKeyActions(srChild,'return searchResults.NavChild(event,'+e+','+c+')');
781        setClassAttr(srChild,'SRScope');
782        srChild.setAttribute('href',resultsPath+searchData[e][1][c+1][0]);
783        srChild.setAttribute('onclick','searchBox.CloseResultsWindow()');
784        if (searchData[e][1][c+1][1])
785        {
786         srChild.setAttribute('target','_parent');
787        }
788        else
789        {
790         srChild.setAttribute('target','_blank');
791        }
792        srChild.innerHTML = searchData[e][1][c+1][2];
793        srChildren.appendChild(srChild);
794      }
795      srEntry.appendChild(srChildren);
796    }
797    srResult.appendChild(srEntry);
798    results.appendChild(srResult);
799  }
800}
801
802function init_search()
803{
804  var results = document.getElementById("MSearchSelectWindow");
805  for (var key in indexSectionLabels)
806  {
807    var link = document.createElement('a');
808    link.setAttribute('class','SelectItem');
809    link.setAttribute('onclick','searchBox.OnSelectItem('+key+')');
810    link.href='javascript:void(0)';
811    link.innerHTML='<span class="SelectionMark">&#160;</span>'+indexSectionLabels[key];
812    results.appendChild(link);
813  }
814  searchBox.OnSelectItem(0);
815}
816/* @license-end */
817