docs/api-2.1/search/search.js

Sat, 06 Feb 2021 19:11:44 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 06 Feb 2021 19:11:44 +0100
changeset 390
d345541018fa
permissions
-rw-r--r--

starts ucx 3.0 development

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

mercurial