Annotation of embedaddon/confuse/doc/html/search/search.js, revision 1.1.1.1
1.1 misho 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='•';
267: }
268: else
269: {
270: node.innerHTML=' ';
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"> </span>'+indexSectionLabels[key];
810: results.appendChild(link);
811: }
812: searchBox.OnSelectItem(0);
813: }
814: /* @license-end */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>