2 @licstart The following is the entire license notice for the JavaScript code in this file.
6 Copyright (C) 1997-2020 by Dimitri van Heesch
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:
14 The above copyright notice and this permission notice shall be included in all copies or
15 substantial portions of the Software.
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.
23 @licend The above is the entire license notice for the JavaScript code in this file
25 function convertToId(search)
28 for (i=0;i<search.length;i++)
30 var c = search.charAt(i);
31 var cn = c.charCodeAt(0);
32 if (c.match(/[a-z0-9\u0080-\uFFFF]/))
38 result+="_0"+cn.toString(16);
42 result+="_"+cn.toString(16);
48 function getXPos(item)
53 while (item && item!=document.body)
56 item = item.offsetParent;
62 function getYPos(item)
67 while (item && item!=document.body)
70 item = item.offsetParent;
76 var searchResults = new SearchResults("searchResults");
78 /* A class handling everything associated with the search panel.
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
85 function SearchBox(name, resultsPath, extension)
87 if (!name || !resultsPath) { alert("Missing parameters to SearchBox."); }
88 if (!extension || extension == "") { extension = ".html"; }
90 // ---------- Instance variables
92 this.resultsPath = resultsPath;
94 this.keyTimeoutLength = 500;
95 this.closeSelectionTimeout = 300;
96 this.lastSearchValue = "";
97 this.lastResultsPage = "";
100 this.searchActive = false;
101 this.extension = extension;
103 // ----------- DOM Elements
105 this.DOMSearchField = function()
106 { return document.getElementById("MSearchField"); }
108 this.DOMSearchSelect = function()
109 { return document.getElementById("MSearchSelect"); }
111 this.DOMSearchSelectWindow = function()
112 { return document.getElementById("MSearchSelectWindow"); }
114 this.DOMPopupSearchResults = function()
115 { return document.getElementById("MSearchResults"); }
117 this.DOMPopupSearchResultsWindow = function()
118 { return document.getElementById("MSearchResultsWindow"); }
120 this.DOMSearchClose = function()
121 { return document.getElementById("MSearchClose"); }
123 this.DOMSearchBox = function()
124 { return document.getElementById("MSearchBox"); }
126 // ------------ Event Handlers
128 // Called when focus is added or removed from the search field.
129 this.OnSearchFieldFocus = function(isActive)
131 this.Activate(isActive);
134 this.OnSearchSelectShow = function()
136 var searchSelectWindow = this.DOMSearchSelectWindow();
137 var searchField = this.DOMSearchSelect();
139 var left = getXPos(searchField);
140 var top = getYPos(searchField);
141 top += searchField.offsetHeight;
143 // show search selection popup
144 searchSelectWindow.style.display='block';
145 searchSelectWindow.style.left = left + 'px';
146 searchSelectWindow.style.top = top + 'px';
148 // stop selection hide timer
149 if (this.hideTimeout)
151 clearTimeout(this.hideTimeout);
154 return false; // to avoid "image drag" default event
157 this.OnSearchSelectHide = function()
159 this.hideTimeout = setTimeout(this.name +".CloseSelectionWindow()",
160 this.closeSelectionTimeout);
163 // Called when the content of the search field is changed.
164 this.OnSearchFieldChange = function(evt)
166 if (this.keyTimeout) // kill running timer
168 clearTimeout(this.keyTimeout);
172 var e = (evt) ? evt : window.event; // for IE
173 if (e.keyCode==40 || e.keyCode==13)
177 this.OnSearchSelectShow();
178 var win=this.DOMSearchSelectWindow();
179 for (i=0;i<win.childNodes.length;i++)
181 var child = win.childNodes[i]; // get span within a
182 if (child.className=='SelectItem')
192 var elem = searchResults.NavNext(0);
193 if (elem) elem.focus();
196 else if (e.keyCode==27) // Escape out of the search field
198 this.DOMSearchField().blur();
199 this.DOMPopupSearchResultsWindow().style.display = 'none';
200 this.DOMSearchClose().style.display = 'none';
201 this.lastSearchValue = '';
202 this.Activate(false);
207 var searchValue = this.DOMSearchField().value.replace(/ +/g, "");
209 if (searchValue != this.lastSearchValue) // search value has changed
211 if (searchValue != "") // non-empty search
213 // set timer for search update
214 this.keyTimeout = setTimeout(this.name + '.Search()',
215 this.keyTimeoutLength);
217 else // empty search field
219 this.DOMPopupSearchResultsWindow().style.display = 'none';
220 this.DOMSearchClose().style.display = 'none';
221 this.lastSearchValue = '';
226 this.SelectItemCount = function(id)
229 var win=this.DOMSearchSelectWindow();
230 for (i=0;i<win.childNodes.length;i++)
232 var child = win.childNodes[i]; // get span within a
233 if (child.className=='SelectItem')
241 this.SelectItemSet = function(id)
244 var win=this.DOMSearchSelectWindow();
245 for (i=0;i<win.childNodes.length;i++)
247 var child = win.childNodes[i]; // get span within a
248 if (child.className=='SelectItem')
250 var node = child.firstChild;
253 node.innerHTML='•';
257 node.innerHTML=' ';
264 // Called when an search filter selection is made.
265 // set item with index id as the active item
266 this.OnSelectItem = function(id)
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
277 this.OnSearchSelectKey = function(evt)
279 var e = (evt) ? evt : window.event; // for IE
280 if (e.keyCode==40 && this.searchIndex<this.SelectItemCount()) // Down
283 this.OnSelectItem(this.searchIndex);
285 else if (e.keyCode==38 && this.searchIndex>0) // Up
288 this.OnSelectItem(this.searchIndex);
290 else if (e.keyCode==13 || e.keyCode==27)
292 this.OnSelectItem(this.searchIndex);
293 this.CloseSelectionWindow();
294 this.DOMSearchField().focus();
301 // Closes the results window.
302 this.CloseResultsWindow = function()
304 this.DOMPopupSearchResultsWindow().style.display = 'none';
305 this.DOMSearchClose().style.display = 'none';
306 this.Activate(false);
309 this.CloseSelectionWindow = function()
311 this.DOMSearchSelectWindow().style.display = 'none';
314 // Performs a search.
315 this.Search = function()
319 // strip leading whitespace
320 var searchValue = this.DOMSearchField().value.replace(/^ +/, "");
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
326 idxChar = searchValue.substr(0, 2);
331 var idx = indexSectionsWithContent[this.searchIndex].indexOf(idxChar);
334 var hexCode=idx.toString(16);
335 jsFile = this.resultsPath + indexSectionNames[this.searchIndex] + '_' + hexCode + '.js';
338 var loadJS = function(url, impl, loc){
339 var scriptTag = document.createElement('script');
341 scriptTag.onload = impl;
342 scriptTag.onreadystatechange = impl;
343 loc.appendChild(scriptTag);
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;
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";
359 searchResults.Search(searchValue);
361 if (domPopupSearchResultsWindow.style.display!='block')
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;
371 if (left<10) left=10;
372 if (width+left+8>maxWidth) width=maxWidth-left-8;
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';
383 loadJS(jsFile, handleResults, this.DOMPopupSearchResultsWindow());
388 this.lastSearchValue = searchValue;
391 // -------- Activation Functions
393 // Activates or deactivates the search panel, resetting things to
394 // their default values if necessary.
395 this.Activate = function(isActive)
397 if (isActive || // open it
398 this.DOMPopupSearchResultsWindow().style.display == 'block'
401 this.DOMSearchBox().className = 'MSearchBoxActive';
402 this.searchActive = true;
404 else if (!isActive) // directly remove the panel
406 this.DOMSearchBox().className = 'MSearchBoxInactive';
407 this.searchActive = false;
408 this.lastSearchValue = ''
409 this.lastResultsPage = '';
410 this.DOMSearchField().value = '';
415 // -----------------------------------------------------------------------
417 // The class that handles everything on the search results page.
418 function SearchResults(name)
420 // The number of matches from the last run of <Search()>.
421 this.lastMatchCount = 0;
423 this.repeatOn = false;
425 // Toggles the visibility of the passed element ID.
426 this.FindChildElement = function(id)
428 var parentElement = document.getElementById(id);
429 var element = parentElement.firstChild;
431 while (element && element!=parentElement)
433 if (element.nodeName.toLowerCase() == 'div' && element.className == 'SRChildren')
438 if (element.nodeName.toLowerCase() == 'div' && element.hasChildNodes())
440 element = element.firstChild;
442 else if (element.nextSibling)
444 element = element.nextSibling;
450 element = element.parentNode;
452 while (element && element!=parentElement && !element.nextSibling);
454 if (element && element!=parentElement)
456 element = element.nextSibling;
462 this.Toggle = function(id)
464 var element = this.FindChildElement(id);
467 if (element.style.display == 'block')
469 element.style.display = 'none';
473 element.style.display = 'block';
478 // Searches for the passed string. If there is no parameter,
479 // it takes it from the URL query.
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)
485 if (!search) // get search word from URL
487 search = window.location.search;
488 search = search.substring(1); // Remove the leading '?'
489 search = unescape(search);
492 search = search.replace(/^ +/, ""); // strip leading spaces
493 search = search.replace(/ +$/, ""); // strip trailing spaces
494 search = search.toLowerCase();
495 search = convertToId(search);
497 var resultRows = document.getElementsByTagName("div");
501 while (i < resultRows.length)
503 var row = resultRows.item(i);
504 if (row.className == "SRResult")
506 var rowMatchName = row.id.toLowerCase();
507 rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_'
509 if (search.length<=rowMatchName.length &&
510 rowMatchName.substr(0, search.length)==search)
512 row.style.display = 'block';
517 row.style.display = 'none';
522 document.getElementById("Searching").style.display='none';
523 if (matches == 0) // no results
525 document.getElementById("NoMatches").style.display='block';
527 else // at least one result
529 document.getElementById("NoMatches").style.display='none';
531 this.lastMatchCount = matches;
535 // return the first item with index index or higher that is visible
536 this.NavNext = function(index)
541 var focusName = 'Item'+index;
542 focusItem = document.getElementById(focusName);
543 if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
547 else if (!focusItem) // last element
557 this.NavPrev = function(index)
562 var focusName = 'Item'+index;
563 focusItem = document.getElementById(focusName);
564 if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
568 else if (!focusItem) // last element
578 this.ProcessKeys = function(e)
580 if (e.type == "keydown")
582 this.repeatOn = false;
583 this.lastKey = e.keyCode;
585 else if (e.type == "keypress")
589 if (this.lastKey) this.repeatOn = true;
590 return false; // ignore first keypress after keydown
593 else if (e.type == "keyup")
596 this.repeatOn = false;
598 return this.lastKey!=0;
601 this.Nav = function(evt,itemIndex)
603 var e = (evt) ? evt : window.event; // for IE
604 if (e.keyCode==13) return true;
605 if (!this.ProcessKeys(e)) return false;
607 if (this.lastKey==38) // Up
609 var newIndex = itemIndex-1;
610 var focusItem = this.NavPrev(newIndex);
613 var child = this.FindChildElement(focusItem.parentNode.parentNode.id);
614 if (child && child.style.display == 'block') // children visible
618 while (1) // search for last child
620 tmpElem = document.getElementById('Item'+newIndex+'_c'+n);
637 else // return focus to search field
639 document.getElementById("MSearchField").focus();
642 else if (this.lastKey==40) // Down
644 var newIndex = itemIndex+1;
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
650 focusItem = document.getElementById('Item'+itemIndex+'_c0');
652 if (!focusItem) focusItem = this.NavNext(newIndex);
653 if (focusItem) focusItem.focus();
655 else if (this.lastKey==39) // Right
657 var item = document.getElementById('Item'+itemIndex);
658 var elem = this.FindChildElement(item.parentNode.parentNode.id);
659 if (elem) elem.style.display = 'block';
661 else if (this.lastKey==37) // Left
663 var item = document.getElementById('Item'+itemIndex);
664 var elem = this.FindChildElement(item.parentNode.parentNode.id);
665 if (elem) elem.style.display = 'none';
667 else if (this.lastKey==27) // Escape
669 searchBox.CloseResultsWindow();
670 document.getElementById("MSearchField").focus();
672 else if (this.lastKey==13) // Enter
679 this.NavChild = function(evt,itemIndex,childIndex)
681 var e = (evt) ? evt : window.event; // for IE
682 if (e.keyCode==13) return true;
683 if (!this.ProcessKeys(e)) return false;
685 if (this.lastKey==38) // Up
689 var newIndex = childIndex-1;
690 document.getElementById('Item'+itemIndex+'_c'+newIndex).focus();
692 else // already at first child, jump to parent
694 document.getElementById('Item'+itemIndex).focus();
697 else if (this.lastKey==40) // Down
699 var newIndex = childIndex+1;
700 var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex);
701 if (!elem) // last child, jump to parent next parent
703 elem = this.NavNext(itemIndex+1);
710 else if (this.lastKey==27) // Escape
712 searchBox.CloseResultsWindow();
713 document.getElementById("MSearchField").focus();
715 else if (this.lastKey==13) // Enter
723 function setKeyActions(elem,action)
725 elem.setAttribute('onkeydown',action);
726 elem.setAttribute('onkeypress',action);
727 elem.setAttribute('onkeyup',action);
730 function setClassAttr(elem,attr)
732 elem.setAttribute('class',attr);
733 elem.setAttribute('className',attr);
736 function createResults(resultsPath)
738 var results = document.getElementById("SRResults");
739 results.innerHTML = '';
740 for (var e=0; e<searchData.length; e++)
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
756 srLink.setAttribute('href',resultsPath+searchData[e][1][1][0]);
757 srLink.setAttribute('onclick','searchBox.CloseResultsWindow()');
758 if (searchData[e][1][1][1])
760 srLink.setAttribute('target','_parent');
764 srLink.setAttribute('target','_blank');
766 var srScope = document.createElement('span');
767 setClassAttr(srScope,'SRScope');
768 srScope.innerHTML = searchData[e][1][1][2];
769 srEntry.appendChild(srScope);
771 else // multiple results
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++)
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])
786 srChild.setAttribute('target','_parent');
790 srChild.setAttribute('target','_blank');
792 srChild.innerHTML = searchData[e][1][c+1][2];
793 srChildren.appendChild(srChild);
795 srEntry.appendChild(srChildren);
797 srResult.appendChild(srEntry);
798 results.appendChild(srResult);
802 function init_search()
804 var results = document.getElementById("MSearchSelectWindow");
805 for (var key in indexSectionLabels)
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"> </span>'+indexSectionLabels[key];
812 results.appendChild(link);
814 searchBox.OnSelectItem(0);