home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2005 October / Gamestar_77_2005-10_dvd.iso / Programy / nsb-install-8-0.exe / chrome / help.jar / content / browser / help / help.js < prev    next >
Encoding:
Text File  |  2005-07-29  |  25.2 KB  |  785 lines

  1. /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  * The contents of this file are subject to the Netscape Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/NPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is Mozilla Communicator client code, released
  13.  * March 31, 1998.
  14.  * 
  15.  * The Initial Developer of the Original Code is Netscape
  16.  * Communications Corporation. Portions created by Netscape are
  17.  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
  18.  * Rights Reserved.
  19.  * Contributor(s):
  20.  *    Ian Oeschger <oeschger@brownhen.com> (Original Author)
  21.  *    Peter Wilson (added sidebar tabs)
  22.  *    R.J. Keller <rlk@trfenv.com>
  23.  */
  24.  
  25. dump('loading file: help.js\n');
  26.  
  27. //-------- global variables
  28. var helpBrowser;
  29. var helpWindow;
  30. var helpSearchPanel;
  31. var emptySearch;
  32. var emptySearchText
  33. var emptySearchLink
  34. var helpTocPanel;
  35. var helpIndexPanel;
  36. var helpGlossaryPanel;
  37.  
  38. // Namespaces
  39. const NC = "http://home.netscape.com/NC-rdf#";
  40. const SN = "rdf:http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  41. const XML = "http://www.w3.org/XML/1998/namespace#"
  42. const MAX_LEVEL = 40; // maximum depth of recursion in search datasources.
  43.  
  44. // Resources
  45. const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService);
  46. const RDF_ROOT = RDF.GetResource("urn:root");
  47. const NC_PANELLIST = RDF.GetResource(NC + "panellist");
  48. const NC_PANELID = RDF.GetResource(NC + "panelid");
  49. const NC_EMPTY_SEARCH_TEXT = RDF.GetResource(NC + "emptysearchtext");
  50. const NC_EMPTY_SEARCH_LINK = RDF.GetResource(NC + "emptysearchlink");
  51. const NC_DATASOURCES = RDF.GetResource(NC + "datasources");
  52. const NC_SUBHEADINGS = RDF.GetResource(NC + "subheadings");
  53. const NC_NAME = RDF.GetResource(NC + "name");
  54. const NC_CHILD = RDF.GetResource(NC + "child");
  55. const NC_LINK = RDF.GetResource(NC + "link");
  56. const NC_TITLE = RDF.GetResource(NC + "title");
  57. const NC_BASE = RDF.GetResource(NC + "base"); 
  58. const NC_DEFAULTTOPIC = RDF.GetResource(NC + "defaulttopic"); 
  59.  
  60. const RDFCUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].getService(Components.interfaces.nsIRDFContainerUtils);
  61. const RDFContainer = Components.classes["@mozilla.org/rdf/container;1"].getService(Components.interfaces.nsIRDFContainer);
  62. const CONSOLE_SERVICE = Components.classes['@mozilla.org/consoleservice;1'].getService(Components.interfaces.nsIConsoleService);
  63.             
  64. const MAX_HISTORY_MENU_ITEMS = 15;
  65.                      
  66. var urnID = 0;
  67. var RE;
  68.  
  69. var helpFileURI;
  70. var helpFileDS;
  71. // Set from nc:base attribute on help rdf file. It may be used for prefix reduction on all links within
  72. // the current help set.
  73. var helpBaseURI;
  74.  
  75. const defaultHelpFile = "chrome://help/locale/mozillahelp.rdf";
  76. // Set from nc:defaulttopic. It is used when the requested uri has no topic specified. 
  77. var defaultTopic = "welcome"; 
  78. var searchDatasources = "rdf:null";
  79. var searchDS = null;
  80.  
  81. const NSRESULT_RDF_SYNTAX_ERROR = 0x804e03f7; 
  82.  
  83. // This function is called by dialogs/windows that want to display context-sensitive help
  84. // These dialogs/windows should include the script chrome://help/content/contextHelp.js
  85. function displayTopic(topic) {
  86.   // Use default topic if topic is not specified.
  87.   if (!topic)
  88.     topic = defaultTopic;
  89.  
  90.   // Get the help page to open.
  91.   var uri = getLink(topic);
  92.  
  93.   // Use default topic if specified topic is not found.
  94.   if (!uri) // Topic not found - revert to default.
  95.     uri = getLink(defaultTopic);
  96.   loadURI(uri);
  97. }
  98.  
  99. // Initialize the Help window
  100. function init() {
  101.   debug('init()');
  102.  
  103.   //cache panel references.
  104.   helpWindow = document.getElementById("help");
  105.   helpSearchPanel = document.getElementById("help-search-panel");
  106.   helpTocPanel = document.getElementById("help-toc-panel");
  107.   helpIndexPanel = document.getElementById("help-index-panel");
  108.   helpGlossaryPanel = document.getElementById("help-glossary-panel");
  109.   helpBrowser = document.getElementById("help-content");
  110.  
  111.   // Get the help content pack, base URL, and help topic
  112.   var helpTopic = defaultTopic;
  113.   if ("arguments" in window && window.arguments[0] instanceof Components.interfaces.nsIDialogParamBlock) {
  114.     helpFileURI = window.arguments[0].GetString(0);
  115.     helpBaseURI = helpFileURI.substring(0, helpFileURI.lastIndexOf("/")+1); // trailing "/" included.
  116.     helpTopic = window.arguments[0].GetString(1);
  117.   }
  118.  
  119.   loadHelpRDF();
  120.  
  121.   displayTopic(helpTopic);
  122.  
  123.   // Initalize History.
  124.   var sessionHistory =  Components.classes["@mozilla.org/browser/shistory;1"]
  125.                                   .createInstance(Components.interfaces.nsISHistory);
  126.  
  127.   window.XULBrowserWindow = new nsHelpStatusHandler();
  128.  
  129.   //Start the status handler.
  130.   window.XULBrowserWindow.init();
  131.  
  132.   // Hook up UI through Progress Listener
  133.   const interfaceRequestor = helpBrowser.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
  134.   const webProgress = interfaceRequestor.getInterface(Components.interfaces.nsIWebProgress);
  135.   webProgress.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL);
  136.  
  137.   //Always show the Table of Contents sidebar at startup.
  138.   showPanel('help-toc');
  139.  
  140.   debug('END init()');
  141. }
  142.  
  143. function loadHelpRDF() {
  144.   debug('loadHelpRDF()');
  145.  
  146.   if (!helpFileDS) {
  147.     try {
  148.       helpFileDS = RDF.GetDataSourceBlocking(helpFileURI);
  149.     }
  150.     catch (e if (e.result == NSRESULT_RDF_SYNTAX_ERROR)) {
  151.       log("Help file: " + helpFileURI + " contains a syntax error.");
  152.     }
  153.     catch (e) {
  154.       log("Help file: " + helpFileURI + " was not found.");
  155.     }
  156.     try {
  157.       helpWindow.setAttribute("title", getAttribute(helpFileDS, RDF_ROOT, NC_TITLE, ""));
  158.       helpBaseURI = getAttribute(helpFileDS, RDF_ROOT, NC_BASE, helpBaseURI);
  159.       defaultTopic = getAttribute(helpFileDS, RDF_ROOT, NC_DEFAULTTOPIC, "welcome");
  160.  
  161.       var panelDefs = helpFileDS.GetTarget(RDF_ROOT, NC_PANELLIST, true);      
  162.       RDFContainer.Init(helpFileDS, panelDefs);
  163.       var iterator = RDFContainer.GetElements();
  164.         while (iterator.hasMoreElements()) {
  165.         var panelDef = iterator.getNext();
  166.         var panelID = getAttribute(helpFileDS, panelDef, NC_PANELID, null);        
  167.  
  168.         var datasources = getAttribute(helpFileDS, panelDef, NC_DATASOURCES, "rdf:none");
  169.         datasources = normalizeLinks(helpBaseURI, datasources);
  170.         // cache additional datsources to augment search datasources.
  171.         if (panelID == "search") {
  172.            emptySearchText = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_TEXT, null) || "No search items found." ;        
  173.            emptySearchLink = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_LINK, null) || "about:blank";        
  174.           searchDatasources = datasources;
  175.           datasources = "rdf:null"; // but don't try to display them yet!
  176.         }  
  177.  
  178.         // cache toc datasources for use by ID lookup.
  179.         var tree = document.getElementById("help-" + panelID + "-panel");
  180.         loadDatabasesBlocking(datasources);
  181.         tree.setAttribute("datasources", datasources);
  182.       }  
  183.     }
  184.     catch (e) {
  185.       log(e + "");      
  186.     }
  187.   }
  188. }
  189.  
  190. function loadDatabasesBlocking(datasources) {
  191.   var ds = datasources.split(/\s+/);
  192.   for (var i=0; i < ds.length; ++i) {
  193.     if (ds[i] == "rdf:null" || ds[i] == "")
  194.       continue;
  195.     try {  
  196.       // we need blocking here to ensure the database is loaded so getLink(topic) works.
  197.       var datasource = RDF.GetDataSourceBlocking(ds[i]);
  198.     }
  199.     catch (e) {
  200.       log("Datasource: " + ds[i] + " was not found.");
  201.     }
  202.   }
  203. }
  204.  
  205. // prepend helpBaseURI to list of space separated links if the don't start with "chrome:"
  206. function normalizeLinks(helpBaseURI, links) {
  207.   if (!helpBaseURI)
  208.     return links;
  209.   var ls = links.split(/\s+/);
  210.   if (ls.length == 0)
  211.     return links;
  212.   for (var i=0; i < ls.length; ++i) {
  213.     if (ls[i] == "")
  214.       continue;
  215.     if (ls[i].substr(0,7) != "chrome:" && ls[i].substr(0,4) != "rdf:") 
  216.       ls[i] = helpBaseURI + ls[i];
  217.   }
  218.   return ls.join(" ");  
  219. }
  220.  
  221. function getLink(ID) {
  222.   if (!ID)
  223.     return null;
  224.   // Note resources are stored in fileURL#ID format.
  225.   // We have one possible source for an ID for each datasource in the composite datasource.
  226.   // The first ID which matches is returned.
  227.   var tocTree = document.getElementById("help-toc-panel");
  228.   var tocDS = tocTree.database;
  229.     if (tocDS == null)
  230.       return null;
  231.     var tocDatasources = tocTree.getAttribute("datasources");
  232.   var ds = tocDatasources.split(/\s+/);
  233.   for (var i=0; i < ds.length; ++i) {
  234.     if (ds[i] == "rdf:null" || ds[i] == "")
  235.       continue;
  236.     try {
  237.       var rdfID = ds[i] + "#" + ID;
  238.       var resource = RDF.GetResource(rdfID);
  239.       if (resource) {
  240.         var link = tocDS.GetTarget(resource, NC_LINK, true);
  241.         if (link) {
  242.           link = link.QueryInterface(Components.interfaces.nsIRDFLiteral);
  243.           if (link) 
  244.             return link.Value;
  245.           else  
  246.             return null;
  247.         }  
  248.       }
  249.     }
  250.     catch (e) { log(rdfID + " " + e);}
  251.   }
  252.   return null;
  253. }
  254.  
  255. // Called by contextHelp.js to determine if this window is displaying the requested help file.
  256. function getHelpFileURI() {
  257.   return helpFileURI;
  258. }
  259.  
  260.  
  261. function getWebNavigation()
  262. {
  263.   return helpBrowser.webNavigation;
  264. }
  265.  
  266. function loadURI(uri)
  267. {
  268.    if (uri.substr(0,7) != "chrome:")
  269.       uri = helpBaseURI + uri;
  270.    const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
  271.    var webNav=getWebNavigation();
  272.    webNav.browserEngine="Gecko";
  273.    webNav.loadURI(uri, nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
  274. }
  275.  
  276. function goBack()
  277. {
  278.   var webNavigation = getWebNavigation();
  279.   if (webNavigation.canGoBack)
  280.     webNavigation.goBack();
  281. }
  282.  
  283. function goForward()
  284. {
  285.   var webNavigation = getWebNavigation();
  286.   if (webNavigation.canGoForward)
  287.     webNavigation.goForward();
  288. }
  289.  
  290. function goHome() {
  291.   // load "Welcome" page
  292.   displayTopic(defaultTopic);
  293. }
  294.  
  295. function print()
  296. {
  297.   try {
  298.     _content.print();
  299.   } catch (e) {
  300.   }
  301. }
  302.  
  303. function deleteHistoryItems(aParent)
  304. {
  305.   var children = aParent.childNodes;
  306.   for (var i = 0; i < children.length; i++)
  307.     {
  308.       var index = children[i].getAttribute("index");
  309.       if (index)
  310.         aParent.removeChild(children[i]);
  311.     }
  312. }
  313.  
  314. function createMenuItem( aParent, aIndex, aLabel)
  315.   {
  316.     var menuitem = document.createElement( "menuitem" );
  317.     menuitem.setAttribute( "label", aLabel );
  318.     menuitem.setAttribute( "index", aIndex );
  319.     aParent.appendChild( menuitem );
  320.   }
  321.  
  322. function createRadioMenuItem( aParent, aIndex, aLabel, aChecked)
  323.   {
  324.     var menuitem = document.createElement( "menuitem" );
  325.     menuitem.setAttribute( "type", "radio" );
  326.     menuitem.setAttribute( "label", aLabel );
  327.     menuitem.setAttribute( "index", aIndex );
  328.     if (aChecked==true)
  329.       menuitem.setAttribute( "checked", "true" );
  330.     aParent.appendChild( menuitem );
  331.   }
  332.  
  333. function FillHistoryMenu(aParent, aMenu)
  334.   {
  335.     // Remove old entries if any
  336.     deleteHistoryItems(aParent);
  337.  
  338.     var sessionHistory = getWebNavigation().sessionHistory;
  339.  
  340.     var count = sessionHistory.count;
  341.     var index = sessionHistory.index;
  342.     var end;
  343.     var j;
  344.     var entry;
  345.  
  346.     switch (aMenu)
  347.       {
  348.         case "back":
  349.           end = (index > MAX_HISTORY_MENU_ITEMS) ? index - MAX_HISTORY_MENU_ITEMS : 0;
  350.           if ((index - 1) < end) return false;
  351.           for (j = index - 1; j >= end; j--)
  352.             {
  353.               entry = sessionHistory.getEntryAtIndex(j, false);
  354.               if (entry)
  355.                 createMenuItem(aParent, j, entry.title);
  356.             }
  357.           break;
  358.         case "forward":
  359.           end  = ((count-index) > MAX_HISTORY_MENU_ITEMS) ? index + MAX_HISTORY_MENU_ITEMS : count;
  360.           if ((index + 1) >= end) return false;
  361.           for (j = index + 1; j < end; j++)
  362.             {
  363.               entry = sessionHistory.getEntryAtIndex(j, false);
  364.               if (entry)
  365.                 createMenuItem(aParent, j, entry.title);
  366.             }
  367.           break;
  368.         case "go":
  369.           aParent.lastChild.hidden = (count == 0);
  370.           end = count > MAX_HISTORY_MENU_ITEMS ? count - MAX_HISTORY_MENU_ITEMS : 0;
  371.           for (j = count - 1; j >= end; j--)
  372.             {
  373.               entry = sessionHistory.getEntryAtIndex(j, false);
  374.               if (entry)
  375.                 createRadioMenuItem(aParent, j, entry.title, j==index);
  376.             }
  377.           break;
  378.       }
  379.     return true;
  380. }
  381.  
  382. function createBackMenu(event)
  383. {
  384.   return FillHistoryMenu(event.target, "back");
  385. }
  386.  
  387. function createForwardMenu(event)
  388. {
  389.   return FillHistoryMenu(event.target, "forward");
  390. }
  391.  
  392. function gotoHistoryIndex(aEvent)
  393. {
  394.   var index = aEvent.target.getAttribute("index");
  395.   if (!index)
  396.     return false;
  397.   try {
  398.     getWebNavigation().gotoIndex(index);
  399.   }
  400.   catch(ex) {
  401.     return false;
  402.   }
  403.   return true;
  404. }
  405.  
  406. function nsHelpStatusHandler()
  407. {
  408. }
  409.  
  410. nsHelpStatusHandler.prototype =
  411. {
  412.   onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus)
  413.   {
  414.     const nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
  415.  
  416.     // Turn on the throbber.
  417.     if (aStateFlags & nsIWebProgressListener.STATE_START)
  418.       this.throbberElement.setAttribute("busy", "true");
  419.     else if (aStateFlags & nsIWebProgressListener.STATE_STOP)
  420.       this.throbberElement.removeAttribute("busy");
  421.   },
  422.   onStatusChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {},
  423.   onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress,
  424.                               aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {},
  425.   onSecurityChange : function(aWebProgress, aRequest, state) {},
  426.   onLocationChange : function(aWebProgress, aRequest, aLocation)
  427.   {
  428.     UpdateBackForwardButtons();
  429.   },
  430.   QueryInterface : function(aIID)
  431.   {
  432.     if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
  433.       aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
  434.       aIID.equals(Components.interfaces.nsIXULBrowserWindow) ||
  435.       aIID.equals(Components.interfaces.nsISupports))
  436.       return this;
  437.     throw Components.results.NS_NOINTERFACE;
  438.   },
  439.  
  440.   init : function()
  441.   {
  442.     this.throbberElement = document.getElementById("navigator-throbber");
  443.   },
  444.  
  445.   destroy : function()
  446.   {
  447.     //this is needed to avoid memory leaks, see bug 60729
  448.     this.throbberElement = null;
  449.   },
  450.  
  451.   setJSStatus : function(status) {},
  452.   setJSDefaultStatus : function(status) {},
  453.   setOverLink : function(link) {}
  454. }
  455.  
  456. function UpdateBackForwardButtons()
  457. {
  458.   var backBroadcaster = document.getElementById("canGoBack");
  459.   var forwardBroadcaster = document.getElementById("canGoForward");
  460.   var webNavigation = getWebNavigation();
  461.  
  462.   // Avoid setting attributes on broadcasters if the value hasn't changed!
  463.   // Remember, guys, setting attributes on elements is expensive!  They
  464.   // get inherited into anonymous content, broadcast to other widgets, etc.!
  465.   // Don't do it if the value hasn't changed! - dwh
  466.  
  467.   var backDisabled = (backBroadcaster.getAttribute("disabled") == "true");
  468.   var forwardDisabled = (forwardBroadcaster.getAttribute("disabled") == "true");
  469.  
  470.   if (backDisabled == webNavigation.canGoBack)
  471.     backBroadcaster.setAttribute("disabled", !backDisabled);
  472.   
  473.   if (forwardDisabled == webNavigation.canGoForward)
  474.     forwardBroadcaster.setAttribute("disabled", !forwardDisabled);
  475. }
  476.  
  477. var gFindInstData;
  478. function getFindInstData()
  479. {
  480.   if (!gFindInstData) {
  481.     gFindInstData = new nsFindInstData();
  482.     gFindInstData.browser = helpBrowser;
  483.     // defaults for rootSearchWindow and currentSearchWindow are fine here
  484.   }
  485.   return gFindInstData;
  486. }
  487.  
  488. function find(again, reverse)
  489. {
  490.   if (again)
  491.     findAgainInPage(getFindInstData(), reverse);
  492.   else
  493.     findInPage(getFindInstData())
  494. }
  495.  
  496. function getMarkupDocumentViewer()
  497. {
  498.   return helpBrowser.markupDocumentViewer;
  499. }
  500.  
  501. //Show the selected sidebar panel
  502. function showPanel(panelId) {
  503.   //hide other sidebar panels and show the panel name taken in from panelID.
  504.   helpSearchPanel.setAttribute("hidden", "true");
  505.   helpTocPanel.setAttribute("hidden", "true");
  506.   helpIndexPanel.setAttribute("hidden", "true");
  507.   helpGlossaryPanel.setAttribute("hidden", "true");
  508.   var thePanel = document.getElementById(panelId + "-panel");
  509.   thePanel.setAttribute("hidden","false");
  510.  
  511.   //remove the selected style from the previous panel selected.
  512.   document.getElementById("help-glossary-btn").removeAttribute("selected");
  513.   document.getElementById("help-index-btn").removeAttribute("selected");
  514.   document.getElementById("help-search-btn").removeAttribute("selected");
  515.   document.getElementById("help-toc-btn").removeAttribute("selected");
  516.  
  517.   //add the selected style to the correct panel.
  518.   var theButton = document.getElementById(panelId + "-btn");
  519.   theButton.setAttribute("selected", "true");
  520. }
  521.  
  522. function onselect_loadURI(tree, columnName) {
  523.   try {
  524.     var row = tree.treeBoxObject.view.selection.currentIndex;
  525.     var properties = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
  526.     tree.treeBoxObject.view.getCellProperties(row, columnName, properties);
  527.     if (!properties) return;
  528.     var uri = getPropertyValue(properties, "link-");
  529.     if (uri)
  530.       loadURI(uri);
  531.   }
  532.   catch (e) {}// when switching between tabs a spurious row number is returned.
  533. }
  534.  
  535. /** Search properties nsISupportsArray for an nsIAtom which starts with the given property name. **/
  536. function getPropertyValue(properties, propName) {
  537.   for (var i=0; i< properties.Count(); ++i) {
  538.     var atom = properties.GetElementAt(i).QueryInterface(Components.interfaces.nsIAtom);
  539.     var atomValue = atom.toString();
  540.     if (atomValue.substr(0, propName.length) == propName)
  541.       return atomValue.substr(propName.length);
  542.   }
  543.   return null;
  544. }
  545.  
  546. function doFind() {
  547.   var searchTree = document.getElementById("help-search-tree");
  548.   var findText = document.getElementById("findText");
  549.  
  550.   // clear any previous results.
  551.   clearDatabases(searchTree.database);
  552.  
  553.   // split search string into separate terms and compile into regexp's
  554.   // MERC - JCH: Fix for getting rid of leading spaces bug
  555.   RE = new Array();
  556.   var aTemp = findText.value.split(/\s+/);
  557.   for (var i=0; i < aTemp.length; ++i) {
  558.     if (aTemp[i] == "")
  559.       continue;
  560.  
  561.     var obj = new RegExp(aTemp[i], "i");
  562.     RE.push(obj);
  563.   }
  564.  
  565.   emptySearch = true;
  566.   // search TOC
  567.   var resultsDS =  Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].createInstance(Components.interfaces.nsIRDFDataSource);
  568.   var tree = document.getElementById("help-toc-panel");
  569.   var sourceDS = tree.database;
  570.   doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
  571.  
  572.   // search additional search datasources
  573.   if (searchDatasources != "rdf:null") {
  574.     if (!searchDS)
  575.       searchDS = loadCompositeDS(searchDatasources);
  576.     doFindOnDatasource(resultsDS, searchDS, RDF_ROOT, 0);
  577.   }
  578.  
  579.   // search index.
  580.   tree = document.getElementById("help-index-panel");
  581.   sourceDS = tree.database;
  582.   if (!sourceDS) // If the index has never been displayed this will be null.
  583.     sourceDS = loadCompositeDS(tree.datasources);
  584.   doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
  585.  
  586.   // search glossary.
  587.   tree = document.getElementById("help-glossary-panel");
  588.   sourceDS = tree.database;
  589.   if (!sourceDS) // If the glossary has never been displayed this will be null (sigh!).
  590.     sourceDS = loadCompositeDS(tree.datasources);
  591.   doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0);
  592.  
  593.   if (emptySearch)
  594.         assertSearchEmpty(resultsDS);
  595.   // Add the datasource to the search tree
  596.   searchTree.database.AddDataSource(resultsDS);
  597.   searchTree.builder.rebuild();
  598. }
  599.  
  600. function doEnabling() {
  601.   var findButton = document.getElementById("findButton");
  602.   var findTextbox = document.getElementById("findText");
  603.   findButton.disabled = !findTextbox.value;
  604. }
  605.  
  606. function clearDatabases(compositeDataSource) {
  607.   var enumDS = compositeDataSource.GetDataSources()
  608.   while (enumDS.hasMoreElements()) {
  609.     var ds = enumDS.getNext();
  610.         compositeDataSource.RemoveDataSource(ds);
  611.   }
  612. }
  613.  
  614. function doFindOnDatasource(resultsDS, sourceDS, resource, level) {
  615.   if (level > MAX_LEVEL) {
  616.     try {
  617.       log("Recursive reference to resource: " + resource.Value + ".");
  618.     }
  619.     catch (e) {
  620.       log("Recursive reference to unknown resource.");
  621.     }
  622.     return;
  623.   }
  624.   // find all SUBHEADING children of current resource.
  625.   var targets = sourceDS.GetTargets(resource, NC_SUBHEADINGS, true);
  626.   while (targets.hasMoreElements()) {
  627.       var target = targets.getNext();
  628.       target = target.QueryInterface(Components.interfaces.nsIRDFResource);
  629.         // The first child of a rdf:subheading should (must) be a rdf:seq.
  630.         // Should we test for a SEQ here?
  631.     doFindOnSeq(resultsDS, sourceDS, target, level+1);       
  632.   }  
  633. }
  634.  
  635. function doFindOnSeq(resultsDS, sourceDS, resource, level) {
  636.   // load up an RDFContainer so we can access the contents of the current rdf:seq.    
  637.     RDFContainer.Init(sourceDS, resource);
  638.     var targets = RDFContainer.GetElements();
  639.     while (targets.hasMoreElements()) {
  640.     var target = targets.getNext();
  641.         target = target.QueryInterface(Components.interfaces.nsIRDFResource);
  642.         var name = sourceDS.GetTarget(target, NC_NAME, true);
  643.         name = name.QueryInterface(Components.interfaces.nsIRDFLiteral);
  644.         
  645.         if (isMatch(name.Value)) {
  646.           // we have found a search entry - add it to the results datasource.
  647.           
  648.           // Get URL of html for this entry.
  649.       var link = sourceDS.GetTarget(target, NC_LINK, true);
  650.       link = link.QueryInterface(Components.interfaces.nsIRDFLiteral);        
  651.  
  652.       urnID++;
  653.       resultsDS.Assert(RDF_ROOT,
  654.              RDF.GetResource("http://home.netscape.com/NC-rdf#child"),
  655.              RDF.GetResource("urn:" + urnID),
  656.              true);
  657.       resultsDS.Assert(RDF.GetResource("urn:" + urnID),
  658.              RDF.GetResource("http://home.netscape.com/NC-rdf#name"),
  659.              name,
  660.              true);
  661.       resultsDS.Assert(RDF.GetResource("urn:" + urnID),
  662.              RDF.GetResource("http://home.netscape.com/NC-rdf#link"),
  663.              link,
  664.              true);
  665.           emptySearch = false;     
  666.              
  667.     }
  668.     // process any nested rdf:seq elements.
  669.     doFindOnDatasource(resultsDS, sourceDS, target, level+1);       
  670.     }  
  671. }
  672.  
  673. function assertSearchEmpty(resultsDS) {
  674.     var resSearchEmpty = RDF.GetResource("urn:emptySearch");
  675.     resultsDS.Assert(RDF_ROOT,
  676.              NC_CHILD,
  677.              resSearchEmpty,
  678.              true);
  679.     resultsDS.Assert(resSearchEmpty,
  680.              NC_NAME,
  681.              RDF.GetLiteral(emptySearchText),
  682.              true);
  683.     resultsDS.Assert(resSearchEmpty,
  684.              NC_LINK,
  685.              RDF.GetLiteral(emptySearchLink),
  686.              true);
  687. }
  688.  
  689. function isMatch(text) {
  690.   for (var i=0; i < RE.length; ++i ) {
  691.     if (!RE[i].test(text))
  692.       return false;
  693.   }
  694.   return true;
  695. }
  696.  
  697. function loadCompositeDS(datasources) {
  698.   // We can't search on each individual datasource's - only the aggregate (for each sidebar tab)
  699.   // has the appropriate structure.
  700.   var compositeDS =  Components.classes["@mozilla.org/rdf/datasource;1?name=composite-datasource"]
  701.       .createInstance(Components.interfaces.nsIRDFCompositeDataSource);
  702.   
  703.   var ds = datasources.split(/\s+/);
  704.   for (var i=0; i < ds.length; ++i) {
  705.     if (ds[i] == "rdf:null" || ds[i] == "")
  706.       continue;
  707.     try {  
  708.       // we need blocking here to ensure the database is loaded.
  709.       var sourceDS = RDF.GetDataSourceBlocking(ds[i]);
  710.       compositeDS.AddDataSource(sourceDS);
  711.     }
  712.     catch (e) {
  713.       log("Datasource: " + ds[i] + " was not found.");
  714.     }
  715.   }
  716.   return compositeDS;
  717. }
  718.  
  719. function getAttribute(datasource, resource, attributeResourceName, defaultValue) {
  720.   var literal = datasource.GetTarget(resource, attributeResourceName, true);
  721.   if (!literal)
  722.     return defaultValue;
  723.   return getLiteralValue(literal, defaultValue);  
  724. }
  725.  
  726. function getLiteralValue(literal, defaultValue) {
  727.   if (literal) {
  728.       literal = literal.QueryInterface(Components.interfaces.nsIRDFLiteral);
  729.       if (literal)
  730.         return literal.Value;
  731.   }
  732.   if (defaultValue)
  733.     return defaultValue;
  734.   return null;
  735. }
  736. // Write debug string to javascript console.
  737. function log(aText) {
  738.   CONSOLE_SERVICE.logStringMessage(aText);
  739. }
  740.  
  741.  
  742. //INDEX OPENING FUNCTION -- called in oncommand for index pane
  743. // iterate over all the items in the outliner;
  744. // open the ones at the top-level (i.e., expose the headings underneath
  745. // the letters in the list.
  746. function displayIndex() {
  747.   var treeview = helpIndexPanel.view;
  748.   var i = treeview.rowCount;
  749.   while (i--)
  750.     if (!treeview.getLevel(i) && !treeview.isContainerOpen(i))
  751.       treeview.toggleOpenState(i);
  752. }
  753.  
  754. // Shows the panel relative to the currently selected panel.
  755. // Takes a boolean parameter - if true it will show the next panel, 
  756. // otherwise it will show the previous panel.
  757. function showRelativePanel(goForward) {
  758.   var selectedIndex = -1;
  759.   var sidebarBox = document.getElementById("helpsidebar-box");
  760.   var sidebarButtons = new Array();
  761.   for (var i = 0; i < sidebarBox.childNodes.length; i++) {
  762.     var btn = sidebarBox.childNodes[i];
  763.     if (btn.nodeName == "button") {
  764.       if (btn.getAttribute("selected") == "true")
  765.         selectedIndex = sidebarButtons.length;
  766.       sidebarButtons.push(btn);
  767.     }
  768.   }
  769.   if (selectedIndex == -1)
  770.     return;
  771.   selectedIndex += goForward ? 1 : -1;
  772.   if (selectedIndex >= sidebarButtons.length)
  773.     selectedIndex = 0;
  774.   else if (selectedIndex < 0)
  775.     selectedIndex = sidebarButtons.length - 1;
  776.   sidebarButtons[selectedIndex].doCommand();
  777. }
  778.  
  779. /**
  780.  * Print debug output to the console window
  781.  **/
  782. function debug(msg) {
  783.     dump('help.js: '+msg+'\n');
  784. }
  785.