• 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
76/* A class handling everything associated with the search panel.
77
78   Parameters:
79   name - The name of the global variable that will be
80          storing this instance.  Is needed to be able to set timeouts.
81   resultPath - path to use for external files
82*/
83function SearchBox(name, resultsPath, inFrame, label, extension)
84{
85  if (!name || !resultsPath) {  alert("Missing parameters to SearchBox."); }
86  if (!extension || extension == "") { extension = ".html"; }
87
88  // ---------- Instance variables
89  this.name                  = name;
90  this.resultsPath           = resultsPath;
91  this.keyTimeout            = 0;
92  this.keyTimeoutLength      = 500;
93  this.closeSelectionTimeout = 300;
94  this.lastSearchValue       = "";
95  this.lastResultsPage       = "";
96  this.hideTimeout           = 0;
97  this.searchIndex           = 0;
98  this.searchActive          = false;
99  this.insideFrame           = inFrame;
100  this.searchLabel           = label;
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    if (this.insideFrame)
140    {
141      var left = getXPos(searchField);
142      var top  = getYPos(searchField);
143      left += searchField.offsetWidth + 6;
144      top += searchField.offsetHeight;
145
146      // show search selection popup
147      searchSelectWindow.style.display='block';
148      left -= searchSelectWindow.offsetWidth;
149      searchSelectWindow.style.left =  left + 'px';
150      searchSelectWindow.style.top  =  top  + 'px';
151    }
152    else
153    {
154      var left = getXPos(searchField);
155      var top  = getYPos(searchField);
156      top += searchField.offsetHeight;
157
158      // show search selection popup
159      searchSelectWindow.style.display='block';
160      searchSelectWindow.style.left =  left + 'px';
161      searchSelectWindow.style.top  =  top  + 'px';
162    }
163
164    // stop selection hide timer
165    if (this.hideTimeout)
166    {
167      clearTimeout(this.hideTimeout);
168      this.hideTimeout=0;
169    }
170    return false; // to avoid "image drag" default event
171  }
172
173  this.OnSearchSelectHide = function()
174  {
175    this.hideTimeout = setTimeout(this.name +".CloseSelectionWindow()",
176                                  this.closeSelectionTimeout);
177  }
178
179  // Called when the content of the search field is changed.
180  this.OnSearchFieldChange = function(evt)
181  {
182    if (this.keyTimeout) // kill running timer
183    {
184      clearTimeout(this.keyTimeout);
185      this.keyTimeout = 0;
186    }
187
188    var e  = (evt) ? evt : window.event; // for IE
189    if (e.keyCode==40 || e.keyCode==13)
190    {
191      if (e.shiftKey==1)
192      {
193        this.OnSearchSelectShow();
194        var win=this.DOMSearchSelectWindow();
195        for (i=0;i<win.childNodes.length;i++)
196        {
197          var child = win.childNodes[i]; // get span within a
198          if (child.className=='SelectItem')
199          {
200            child.focus();
201            return;
202          }
203        }
204        return;
205      }
206      else
207      {
208        window.frames.MSearchResults.postMessage("take_focus", "*");
209      }
210    }
211    else if (e.keyCode==27) // Escape out of the search field
212    {
213      this.DOMSearchField().blur();
214      this.DOMPopupSearchResultsWindow().style.display = 'none';
215      this.DOMSearchClose().style.display = 'none';
216      this.lastSearchValue = '';
217      this.Activate(false);
218      return;
219    }
220
221    // strip whitespaces
222    var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
223
224    if (searchValue != this.lastSearchValue) // search value has changed
225    {
226      if (searchValue != "") // non-empty search
227      {
228        // set timer for search update
229        this.keyTimeout = setTimeout(this.name + '.Search()',
230                                     this.keyTimeoutLength);
231      }
232      else // empty search field
233      {
234        this.DOMPopupSearchResultsWindow().style.display = 'none';
235        this.DOMSearchClose().style.display = 'none';
236        this.lastSearchValue = '';
237      }
238    }
239  }
240
241  this.SelectItemCount = function(id)
242  {
243    var count=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        count++;
251      }
252    }
253    return count;
254  }
255
256  this.SelectItemSet = function(id)
257  {
258    var i,j=0;
259    var win=this.DOMSearchSelectWindow();
260    for (i=0;i<win.childNodes.length;i++)
261    {
262      var child = win.childNodes[i]; // get span within a
263      if (child.className=='SelectItem')
264      {
265        var node = child.firstChild;
266        if (j==id)
267        {
268          node.innerHTML='&#8226;';
269        }
270        else
271        {
272          node.innerHTML='&#160;';
273        }
274        j++;
275      }
276    }
277  }
278
279  // Called when an search filter selection is made.
280  // set item with index id as the active item
281  this.OnSelectItem = function(id)
282  {
283    this.searchIndex = id;
284    this.SelectItemSet(id);
285    var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
286    if (searchValue!="" && this.searchActive) // something was found -> do a search
287    {
288      this.Search();
289    }
290  }
291
292  this.OnSearchSelectKey = function(evt)
293  {
294    var e = (evt) ? evt : window.event; // for IE
295    if (e.keyCode==40 && this.searchIndex<this.SelectItemCount()) // Down
296    {
297      this.searchIndex++;
298      this.OnSelectItem(this.searchIndex);
299    }
300    else if (e.keyCode==38 && this.searchIndex>0) // Up
301    {
302      this.searchIndex--;
303      this.OnSelectItem(this.searchIndex);
304    }
305    else if (e.keyCode==13 || e.keyCode==27)
306    {
307      this.OnSelectItem(this.searchIndex);
308      this.CloseSelectionWindow();
309      this.DOMSearchField().focus();
310    }
311    return false;
312  }
313
314  // --------- Actions
315
316  // Closes the results window.
317  this.CloseResultsWindow = function()
318  {
319    this.DOMPopupSearchResultsWindow().style.display = 'none';
320    this.DOMSearchClose().style.display = 'none';
321    this.Activate(false);
322  }
323
324  this.CloseSelectionWindow = function()
325  {
326    this.DOMSearchSelectWindow().style.display = 'none';
327  }
328
329  // Performs a search.
330  this.Search = function()
331  {
332    this.keyTimeout = 0;
333
334    // strip leading whitespace
335    var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
336
337    var code = searchValue.toLowerCase().charCodeAt(0);
338    var idxChar = searchValue.substr(0, 1).toLowerCase();
339    if ( 0xD800 <= code && code <= 0xDBFF && searchValue > 1) // surrogate pair
340    {
341      idxChar = searchValue.substr(0, 2);
342    }
343
344    var resultsPage;
345    var resultsPageWithSearch;
346    var hasResultsPage;
347
348    var idx = indexSectionsWithContent[this.searchIndex].indexOf(idxChar);
349    if (idx!=-1)
350    {
351       var hexCode=idx.toString(16);
352       resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + this.extension;
353       resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
354       hasResultsPage = true;
355    }
356    else // nothing available for this search term
357    {
358       resultsPage = this.resultsPath + '/nomatches' + this.extension;
359       resultsPageWithSearch = resultsPage;
360       hasResultsPage = false;
361    }
362
363    window.frames.MSearchResults.location = 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-block';
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) + 150; // domSearchBox.offsetWidth;
383         var top  = getYPos(domSearchBox) + 20;  // 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.
429function 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.toLowerCase() == 'div' && element.className == 'SRChildren')
445        {
446          return element;
447        }
448
449        if (element.nodeName.toLowerCase() == '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}
733
734function setKeyActions(elem,action)
735{
736  elem.setAttribute('onkeydown',action);
737  elem.setAttribute('onkeypress',action);
738  elem.setAttribute('onkeyup',action);
739}
740
741function setClassAttr(elem,attr)
742{
743  elem.setAttribute('class',attr);
744  elem.setAttribute('className',attr);
745}
746
747function createResults()
748{
749  var results = document.getElementById("SRResults");
750  for (var e=0; e<searchData.length; e++)
751  {
752    var id = searchData[e][0];
753    var srResult = document.createElement('div');
754    srResult.setAttribute('id','SR_'+id);
755    setClassAttr(srResult,'SRResult');
756    var srEntry = document.createElement('div');
757    setClassAttr(srEntry,'SREntry');
758    var srLink = document.createElement('a');
759    srLink.setAttribute('id','Item'+e);
760    setKeyActions(srLink,'return searchResults.Nav(event,'+e+')');
761    setClassAttr(srLink,'SRSymbol');
762    srLink.innerHTML = searchData[e][1][0];
763    srEntry.appendChild(srLink);
764    if (searchData[e][1].length==2) // single result
765    {
766      srLink.setAttribute('href',searchData[e][1][1][0]);
767      if (searchData[e][1][1][1])
768      {
769       srLink.setAttribute('target','_parent');
770      }
771      var srScope = document.createElement('span');
772      setClassAttr(srScope,'SRScope');
773      srScope.innerHTML = searchData[e][1][1][2];
774      srEntry.appendChild(srScope);
775    }
776    else // multiple results
777    {
778      srLink.setAttribute('href','javascript:searchResults.Toggle("SR_'+id+'")');
779      var srChildren = document.createElement('div');
780      setClassAttr(srChildren,'SRChildren');
781      for (var c=0; c<searchData[e][1].length-1; c++)
782      {
783        var srChild = document.createElement('a');
784        srChild.setAttribute('id','Item'+e+'_c'+c);
785        setKeyActions(srChild,'return searchResults.NavChild(event,'+e+','+c+')');
786        setClassAttr(srChild,'SRScope');
787        srChild.setAttribute('href',searchData[e][1][c+1][0]);
788        if (searchData[e][1][c+1][1])
789        {
790         srChild.setAttribute('target','_parent');
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