My Project - RET HER  versionsnr
search.js
Go to the documentation of this file.
1 /*
2  @licstart The following is the entire license notice for the
3  JavaScript code in this file.
4 
5  Copyright (C) 1997-2017 by Dimitri van Heesch
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License along
18  with this program; if not, write to the Free Software Foundation, Inc.,
19  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 
21  @licend The above is the entire license notice
22  for the JavaScript code in this file
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\u0080-\uFFFF]/))
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='&#8226;';
267  }
268  else
269  {
270  node.innerHTML='&#160;';
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 idxChar = searchValue.substr(0, 1).toLowerCase();
337  if ( 0xD800 <= code && code <= 0xDBFF && searchValue > 1) // surrogate pair
338  {
339  idxChar = searchValue.substr(0, 2);
340  }
341 
342  var resultsPage;
343  var resultsPageWithSearch;
344  var hasResultsPage;
345 
346  var idx = indexSectionsWithContent[this.searchIndex].indexOf(idxChar);
347  if (idx!=-1)
348  {
349  var hexCode=idx.toString(16);
350  resultsPage = this.resultsPath + '/' + indexSectionNames[this.searchIndex] + '_' + hexCode + '.html';
351  resultsPageWithSearch = resultsPage+'?'+escape(searchValue);
352  hasResultsPage = true;
353  }
354  else // nothing available for this search term
355  {
356  resultsPage = this.resultsPath + '/nomatches.html';
357  resultsPageWithSearch = resultsPage;
358  hasResultsPage = false;
359  }
360 
361  window.frames.MSearchResults.location = resultsPageWithSearch;
362  var domPopupSearchResultsWindow = this.DOMPopupSearchResultsWindow();
363 
364  if (domPopupSearchResultsWindow.style.display!='block')
365  {
366  var domSearchBox = this.DOMSearchBox();
367  this.DOMSearchClose().style.display = 'inline';
368  if (this.insideFrame)
369  {
370  var domPopupSearchResults = this.DOMPopupSearchResults();
371  domPopupSearchResultsWindow.style.position = 'relative';
372  domPopupSearchResultsWindow.style.display = 'block';
373  var width = document.body.clientWidth - 8; // the -8 is for IE :-(
374  domPopupSearchResultsWindow.style.width = width + 'px';
375  domPopupSearchResults.style.width = width + 'px';
376  }
377  else
378  {
379  var domPopupSearchResults = this.DOMPopupSearchResults();
380  var left = getXPos(domSearchBox) + 150; // domSearchBox.offsetWidth;
381  var top = getYPos(domSearchBox) + 20; // domSearchBox.offsetHeight + 1;
382  domPopupSearchResultsWindow.style.display = 'block';
383  left -= domPopupSearchResults.offsetWidth;
384  domPopupSearchResultsWindow.style.top = top + 'px';
385  domPopupSearchResultsWindow.style.left = left + 'px';
386  }
387  }
388 
389  this.lastSearchValue = searchValue;
390  this.lastResultsPage = resultsPage;
391  }
392 
393  // -------- Activation Functions
394 
395  // Activates or deactivates the search panel, resetting things to
396  // their default values if necessary.
397  this.Activate = function(isActive)
398  {
399  if (isActive || // open it
400  this.DOMPopupSearchResultsWindow().style.display == 'block'
401  )
402  {
403  this.DOMSearchBox().className = 'MSearchBoxActive';
404 
405  var searchField = this.DOMSearchField();
406 
407  if (searchField.value == this.searchLabel) // clear "Search" term upon entry
408  {
409  searchField.value = '';
410  this.searchActive = true;
411  }
412  }
413  else if (!isActive) // directly remove the panel
414  {
415  this.DOMSearchBox().className = 'MSearchBoxInactive';
416  this.DOMSearchField().value = this.searchLabel;
417  this.searchActive = false;
418  this.lastSearchValue = ''
419  this.lastResultsPage = '';
420  }
421  }
422 }
423 
424 // -----------------------------------------------------------------------
425 
426 // The class that handles everything on the search results page.
427 function SearchResults(name)
428 {
429  // The number of matches from the last run of <Search()>.
430  this.lastMatchCount = 0;
431  this.lastKey = 0;
432  this.repeatOn = false;
433 
434  // Toggles the visibility of the passed element ID.
435  this.FindChildElement = function(id)
436  {
437  var parentElement = document.getElementById(id);
438  var element = parentElement.firstChild;
439 
440  while (element && element!=parentElement)
441  {
442  if (element.nodeName == 'DIV' && element.className == 'SRChildren')
443  {
444  return element;
445  }
446 
447  if (element.nodeName == 'DIV' && element.hasChildNodes())
448  {
449  element = element.firstChild;
450  }
451  else if (element.nextSibling)
452  {
453  element = element.nextSibling;
454  }
455  else
456  {
457  do
458  {
459  element = element.parentNode;
460  }
461  while (element && element!=parentElement && !element.nextSibling);
462 
463  if (element && element!=parentElement)
464  {
465  element = element.nextSibling;
466  }
467  }
468  }
469  }
470 
471  this.Toggle = function(id)
472  {
473  var element = this.FindChildElement(id);
474  if (element)
475  {
476  if (element.style.display == 'block')
477  {
478  element.style.display = 'none';
479  }
480  else
481  {
482  element.style.display = 'block';
483  }
484  }
485  }
486 
487  // Searches for the passed string. If there is no parameter,
488  // it takes it from the URL query.
489  //
490  // Always returns true, since other documents may try to call it
491  // and that may or may not be possible.
492  this.Search = function(search)
493  {
494  if (!search) // get search word from URL
495  {
496  search = window.location.search;
497  search = search.substring(1); // Remove the leading '?'
498  search = unescape(search);
499  }
500 
501  search = search.replace(/^ +/, ""); // strip leading spaces
502  search = search.replace(/ +$/, ""); // strip trailing spaces
503  search = search.toLowerCase();
504  search = convertToId(search);
505 
506  var resultRows = document.getElementsByTagName("div");
507  var matches = 0;
508 
509  var i = 0;
510  while (i < resultRows.length)
511  {
512  var row = resultRows.item(i);
513  if (row.className == "SRResult")
514  {
515  var rowMatchName = row.id.toLowerCase();
516  rowMatchName = rowMatchName.replace(/^sr\d*_/, ''); // strip 'sr123_'
517 
518  if (search.length<=rowMatchName.length &&
519  rowMatchName.substr(0, search.length)==search)
520  {
521  row.style.display = 'block';
522  matches++;
523  }
524  else
525  {
526  row.style.display = 'none';
527  }
528  }
529  i++;
530  }
531  document.getElementById("Searching").style.display='none';
532  if (matches == 0) // no results
533  {
534  document.getElementById("NoMatches").style.display='block';
535  }
536  else // at least one result
537  {
538  document.getElementById("NoMatches").style.display='none';
539  }
540  this.lastMatchCount = matches;
541  return true;
542  }
543 
544  // return the first item with index index or higher that is visible
545  this.NavNext = function(index)
546  {
547  var focusItem;
548  while (1)
549  {
550  var focusName = 'Item'+index;
551  focusItem = document.getElementById(focusName);
552  if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
553  {
554  break;
555  }
556  else if (!focusItem) // last element
557  {
558  break;
559  }
560  focusItem=null;
561  index++;
562  }
563  return focusItem;
564  }
565 
566  this.NavPrev = function(index)
567  {
568  var focusItem;
569  while (1)
570  {
571  var focusName = 'Item'+index;
572  focusItem = document.getElementById(focusName);
573  if (focusItem && focusItem.parentNode.parentNode.style.display=='block')
574  {
575  break;
576  }
577  else if (!focusItem) // last element
578  {
579  break;
580  }
581  focusItem=null;
582  index--;
583  }
584  return focusItem;
585  }
586 
587  this.ProcessKeys = function(e)
588  {
589  if (e.type == "keydown")
590  {
591  this.repeatOn = false;
592  this.lastKey = e.keyCode;
593  }
594  else if (e.type == "keypress")
595  {
596  if (!this.repeatOn)
597  {
598  if (this.lastKey) this.repeatOn = true;
599  return false; // ignore first keypress after keydown
600  }
601  }
602  else if (e.type == "keyup")
603  {
604  this.lastKey = 0;
605  this.repeatOn = false;
606  }
607  return this.lastKey!=0;
608  }
609 
610  this.Nav = function(evt,itemIndex)
611  {
612  var e = (evt) ? evt : window.event; // for IE
613  if (e.keyCode==13) return true;
614  if (!this.ProcessKeys(e)) return false;
615 
616  if (this.lastKey==38) // Up
617  {
618  var newIndex = itemIndex-1;
619  var focusItem = this.NavPrev(newIndex);
620  if (focusItem)
621  {
622  var child = this.FindChildElement(focusItem.parentNode.parentNode.id);
623  if (child && child.style.display == 'block') // children visible
624  {
625  var n=0;
626  var tmpElem;
627  while (1) // search for last child
628  {
629  tmpElem = document.getElementById('Item'+newIndex+'_c'+n);
630  if (tmpElem)
631  {
632  focusItem = tmpElem;
633  }
634  else // found it!
635  {
636  break;
637  }
638  n++;
639  }
640  }
641  }
642  if (focusItem)
643  {
644  focusItem.focus();
645  }
646  else // return focus to search field
647  {
648  parent.document.getElementById("MSearchField").focus();
649  }
650  }
651  else if (this.lastKey==40) // Down
652  {
653  var newIndex = itemIndex+1;
654  var focusItem;
655  var item = document.getElementById('Item'+itemIndex);
656  var elem = this.FindChildElement(item.parentNode.parentNode.id);
657  if (elem && elem.style.display == 'block') // children visible
658  {
659  focusItem = document.getElementById('Item'+itemIndex+'_c0');
660  }
661  if (!focusItem) focusItem = this.NavNext(newIndex);
662  if (focusItem) focusItem.focus();
663  }
664  else if (this.lastKey==39) // Right
665  {
666  var item = document.getElementById('Item'+itemIndex);
667  var elem = this.FindChildElement(item.parentNode.parentNode.id);
668  if (elem) elem.style.display = 'block';
669  }
670  else if (this.lastKey==37) // Left
671  {
672  var item = document.getElementById('Item'+itemIndex);
673  var elem = this.FindChildElement(item.parentNode.parentNode.id);
674  if (elem) elem.style.display = 'none';
675  }
676  else if (this.lastKey==27) // Escape
677  {
678  parent.searchBox.CloseResultsWindow();
679  parent.document.getElementById("MSearchField").focus();
680  }
681  else if (this.lastKey==13) // Enter
682  {
683  return true;
684  }
685  return false;
686  }
687 
688  this.NavChild = function(evt,itemIndex,childIndex)
689  {
690  var e = (evt) ? evt : window.event; // for IE
691  if (e.keyCode==13) return true;
692  if (!this.ProcessKeys(e)) return false;
693 
694  if (this.lastKey==38) // Up
695  {
696  if (childIndex>0)
697  {
698  var newIndex = childIndex-1;
699  document.getElementById('Item'+itemIndex+'_c'+newIndex).focus();
700  }
701  else // already at first child, jump to parent
702  {
703  document.getElementById('Item'+itemIndex).focus();
704  }
705  }
706  else if (this.lastKey==40) // Down
707  {
708  var newIndex = childIndex+1;
709  var elem = document.getElementById('Item'+itemIndex+'_c'+newIndex);
710  if (!elem) // last child, jump to parent next parent
711  {
712  elem = this.NavNext(itemIndex+1);
713  }
714  if (elem)
715  {
716  elem.focus();
717  }
718  }
719  else if (this.lastKey==27) // Escape
720  {
721  parent.searchBox.CloseResultsWindow();
722  parent.document.getElementById("MSearchField").focus();
723  }
724  else if (this.lastKey==13) // Enter
725  {
726  return true;
727  }
728  return false;
729  }
730 }
731 
732 function setKeyActions(elem,action)
733 {
734  elem.setAttribute('onkeydown',action);
735  elem.setAttribute('onkeypress',action);
736  elem.setAttribute('onkeyup',action);
737 }
738 
739 function setClassAttr(elem,attr)
740 {
741  elem.setAttribute('class',attr);
742  elem.setAttribute('className',attr);
743 }
744 
745 function createResults()
746 {
747  var results = document.getElementById("SRResults");
748  for (var e=0; e<searchData.length; e++)
749  {
750  var id = searchData[e][0];
751  var srResult = document.createElement('div');
752  srResult.setAttribute('id','SR_'+id);
753  setClassAttr(srResult,'SRResult');
754  var srEntry = document.createElement('div');
755  setClassAttr(srEntry,'SREntry');
756  var srLink = document.createElement('a');
757  srLink.setAttribute('id','Item'+e);
758  setKeyActions(srLink,'return searchResults.Nav(event,'+e+')');
759  setClassAttr(srLink,'SRSymbol');
760  srLink.innerHTML = searchData[e][1][0];
761  srEntry.appendChild(srLink);
762  if (searchData[e][1].length==2) // single result
763  {
764  srLink.setAttribute('href',searchData[e][1][1][0]);
765  if (searchData[e][1][1][1])
766  {
767  srLink.setAttribute('target','_parent');
768  }
769  var srScope = document.createElement('span');
770  setClassAttr(srScope,'SRScope');
771  srScope.innerHTML = searchData[e][1][1][2];
772  srEntry.appendChild(srScope);
773  }
774  else // multiple results
775  {
776  srLink.setAttribute('href','javascript:searchResults.Toggle("SR_'+id+'")');
777  var srChildren = document.createElement('div');
778  setClassAttr(srChildren,'SRChildren');
779  for (var c=0; c<searchData[e][1].length-1; c++)
780  {
781  var srChild = document.createElement('a');
782  srChild.setAttribute('id','Item'+e+'_c'+c);
783  setKeyActions(srChild,'return searchResults.NavChild(event,'+e+','+c+')');
784  setClassAttr(srChild,'SRScope');
785  srChild.setAttribute('href',searchData[e][1][c+1][0]);
786  if (searchData[e][1][c+1][1])
787  {
788  srChild.setAttribute('target','_parent');
789  }
790  srChild.innerHTML = searchData[e][1][c+1][2];
791  srChildren.appendChild(srChild);
792  }
793  srEntry.appendChild(srChildren);
794  }
795  srResult.appendChild(srEntry);
796  results.appendChild(srResult);
797  }
798 }
799 
800 function init_search()
801 {
802  var results = document.getElementById("MSearchSelectWindow");
803  for (var key in indexSectionLabels)
804  {
805  var link = document.createElement('a');
806  link.setAttribute('class','SelectItem');
807  link.setAttribute('onclick','searchBox.OnSelectItem('+key+')');
808  link.href='javascript:void(0)';
809  link.innerHTML='<span class="SelectionMark">&#160;</span>'+indexSectionLabels[key];
810  results.appendChild(link);
811  }
812  searchBox.OnSelectItem(0);
813 }
814 /* @license-end */