Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
michael@0 | 1 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 4 | |
michael@0 | 5 | # Global Variables |
michael@0 | 6 | var helpBrowser; |
michael@0 | 7 | var helpSearchPanel; |
michael@0 | 8 | var emptySearch; |
michael@0 | 9 | var emptySearchText; |
michael@0 | 10 | var emptySearchLink = "about:blank"; |
michael@0 | 11 | var helpTocPanel; |
michael@0 | 12 | var helpIndexPanel; |
michael@0 | 13 | var helpGlossaryPanel; |
michael@0 | 14 | var strBundle; |
michael@0 | 15 | var gTocDSList = ""; |
michael@0 | 16 | |
michael@0 | 17 | # Namespaces |
michael@0 | 18 | const NC = "http://home.netscape.com/NC-rdf#"; |
michael@0 | 19 | const MAX_LEVEL = 40; // maximum depth of recursion in search datasources. |
michael@0 | 20 | const MAX_HISTORY_MENU_ITEMS = 6; |
michael@0 | 21 | |
michael@0 | 22 | # ifdef logic ripped from toolkit/components/help/content/platformClasses.css |
michael@0 | 23 | #ifdef XP_WIN |
michael@0 | 24 | const platform = "win"; |
michael@0 | 25 | #else |
michael@0 | 26 | #ifdef XP_MACOSX |
michael@0 | 27 | const platform = "mac"; |
michael@0 | 28 | #else |
michael@0 | 29 | const platform = "unix"; |
michael@0 | 30 | #endif |
michael@0 | 31 | #endif |
michael@0 | 32 | |
michael@0 | 33 | # Resources |
michael@0 | 34 | const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"] |
michael@0 | 35 | .getService(Components.interfaces.nsIRDFService); |
michael@0 | 36 | const RDF_ROOT = RDF.GetResource("urn:root"); |
michael@0 | 37 | const NC_PANELLIST = RDF.GetResource(NC + "panellist"); |
michael@0 | 38 | const NC_PANELID = RDF.GetResource(NC + "panelid"); |
michael@0 | 39 | const NC_EMPTY_SEARCH_TEXT = RDF.GetResource(NC + "emptysearchtext"); |
michael@0 | 40 | const NC_EMPTY_SEARCH_LINK = RDF.GetResource(NC + "emptysearchlink"); |
michael@0 | 41 | const NC_DATASOURCES = RDF.GetResource(NC + "datasources"); |
michael@0 | 42 | const NC_PLATFORM = RDF.GetResource(NC + "platform"); |
michael@0 | 43 | const NC_SUBHEADINGS = RDF.GetResource(NC + "subheadings"); |
michael@0 | 44 | const NC_NAME = RDF.GetResource(NC + "name"); |
michael@0 | 45 | const NC_CHILD = RDF.GetResource(NC + "child"); |
michael@0 | 46 | const NC_LINK = RDF.GetResource(NC + "link"); |
michael@0 | 47 | const NC_TITLE = RDF.GetResource(NC + "title"); |
michael@0 | 48 | const NC_BASE = RDF.GetResource(NC + "base"); |
michael@0 | 49 | const NC_DEFAULTTOPIC = RDF.GetResource(NC + "defaulttopic"); |
michael@0 | 50 | |
michael@0 | 51 | var RDFContainer = |
michael@0 | 52 | Components.classes["@mozilla.org/rdf/container;1"] |
michael@0 | 53 | .createInstance(Components.interfaces.nsIRDFContainer); |
michael@0 | 54 | const CONSOLE_SERVICE = Components.classes['@mozilla.org/consoleservice;1'] |
michael@0 | 55 | .getService(Components.interfaces.nsIConsoleService); |
michael@0 | 56 | |
michael@0 | 57 | var RE; |
michael@0 | 58 | |
michael@0 | 59 | var helpFileURI; |
michael@0 | 60 | var helpFileDS; |
michael@0 | 61 | # Set from nc:base attribute on help rdf file. It may be used for prefix |
michael@0 | 62 | # reduction on all links within the current help set. |
michael@0 | 63 | var helpBaseURI; |
michael@0 | 64 | |
michael@0 | 65 | /* defaultTopic is either set |
michael@0 | 66 | 1. in the openHelp() call, passed as an argument to the Help window and |
michael@0 | 67 | evaluated in init(), or |
michael@0 | 68 | 2. in nc:defaulttopic in the content pack (e.g. firebirdhelp.rdf), |
michael@0 | 69 | evaluated in loadHelpRDF(), or |
michael@0 | 70 | 3. "welcome" as a fallback, specified in loadHelpRDF() as well; |
michael@0 | 71 | displayTopic() then uses defaultTopic because topic is null. */ |
michael@0 | 72 | var defaultTopic; |
michael@0 | 73 | |
michael@0 | 74 | const NSRESULT_RDF_SYNTAX_ERROR = 0x804e03f7; |
michael@0 | 75 | |
michael@0 | 76 | # This function is called by dialogs/windows that want to display |
michael@0 | 77 | # context-sensitive help |
michael@0 | 78 | # These dialogs/windows should include the script |
michael@0 | 79 | # chrome://help/content/contextHelp.js |
michael@0 | 80 | function displayTopic(topic) { |
michael@0 | 81 | // Get the page to open. |
michael@0 | 82 | var uri = getLink(topic); |
michael@0 | 83 | // Use default topic if specified topic is not found. |
michael@0 | 84 | if (!uri) { |
michael@0 | 85 | uri = getLink(defaultTopic); |
michael@0 | 86 | } |
michael@0 | 87 | // Load the page. |
michael@0 | 88 | if (uri) |
michael@0 | 89 | loadURI(uri); |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | # Initialize the Help window |
michael@0 | 93 | function init() { |
michael@0 | 94 | // Cache panel references. |
michael@0 | 95 | helpSearchPanel = document.getElementById("help-search-panel"); |
michael@0 | 96 | helpTocPanel = document.getElementById("help-toc-panel"); |
michael@0 | 97 | helpIndexPanel = document.getElementById("help-index-panel"); |
michael@0 | 98 | helpGlossaryPanel = document.getElementById("help-glossary-panel"); |
michael@0 | 99 | helpBrowser = document.getElementById("help-content"); |
michael@0 | 100 | |
michael@0 | 101 | // Turn off unnecessary features for security |
michael@0 | 102 | helpBrowser.docShell.allowJavascript = false; |
michael@0 | 103 | helpBrowser.docShell.allowPlugins = false; |
michael@0 | 104 | helpBrowser.docShell.allowSubframes = false; |
michael@0 | 105 | helpBrowser.docShell.allowMetaRedirects = false; |
michael@0 | 106 | |
michael@0 | 107 | strBundle = document.getElementById("bundle_help"); |
michael@0 | 108 | emptySearchText = strBundle.getString("emptySearchText"); |
michael@0 | 109 | |
michael@0 | 110 | // Get the content pack, base URL, and help topic |
michael@0 | 111 | var helpTopic = defaultTopic; |
michael@0 | 112 | if ("arguments" in window && |
michael@0 | 113 | window.arguments[0] instanceof Components.interfaces.nsIDialogParamBlock) { |
michael@0 | 114 | helpFileURI = window.arguments[0].GetString(0); |
michael@0 | 115 | // trailing "/" included. |
michael@0 | 116 | helpBaseURI = helpFileURI.substring(0, helpFileURI.lastIndexOf("/")+1); |
michael@0 | 117 | helpTopic = window.arguments[0].GetString(1); |
michael@0 | 118 | } |
michael@0 | 119 | |
michael@0 | 120 | loadHelpRDF(); |
michael@0 | 121 | displayTopic(helpTopic); |
michael@0 | 122 | |
michael@0 | 123 | // Move to Center of Screen |
michael@0 | 124 | const width = document.documentElement.getAttribute("width"); |
michael@0 | 125 | const height = document.documentElement.getAttribute("height"); |
michael@0 | 126 | window.moveTo((screen.availWidth - width) / 2, (screen.availHeight - height) / 2); |
michael@0 | 127 | |
michael@0 | 128 | // Initialize history. |
michael@0 | 129 | getWebNavigation().sessionHistory = |
michael@0 | 130 | Components.classes["@mozilla.org/browser/shistory;1"] |
michael@0 | 131 | .createInstance(Components.interfaces.nsISHistory); |
michael@0 | 132 | window.XULBrowserWindow = new nsHelpStatusHandler(); |
michael@0 | 133 | |
michael@0 | 134 | //Start the status handler. |
michael@0 | 135 | window.XULBrowserWindow.init(); |
michael@0 | 136 | |
michael@0 | 137 | // Hook up UI through Progress Listener |
michael@0 | 138 | const interfaceRequestor = helpBrowser.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor); |
michael@0 | 139 | const webProgress = interfaceRequestor.getInterface(Components.interfaces.nsIWebProgress); |
michael@0 | 140 | |
michael@0 | 141 | webProgress.addProgressListener(window.XULBrowserWindow, Components.interfaces.nsIWebProgress.NOTIFY_ALL); |
michael@0 | 142 | |
michael@0 | 143 | var searchBox = document.getElementById("findText"); |
michael@0 | 144 | searchBox.clickSelectsAll = getBoolPref("browser.urlbar.clickSelectsAll", true); |
michael@0 | 145 | |
michael@0 | 146 | setTimeout(focusSearch, 0); |
michael@0 | 147 | } |
michael@0 | 148 | |
michael@0 | 149 | function showSearchSidebar() { |
michael@0 | 150 | // if you tab too quickly, you end up with stuck focus, revert focus to the searchbar |
michael@0 | 151 | var searchTree = document.getElementById("help-toc-panel"); |
michael@0 | 152 | if (searchTree.treeBoxObject.focused) { |
michael@0 | 153 | focusSearch(); |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | var tableOfContents = document.getElementById("help-toc-sidebar"); |
michael@0 | 157 | tableOfContents.setAttribute("hidden", "true"); |
michael@0 | 158 | |
michael@0 | 159 | var sidebar = document.getElementById("help-search-sidebar"); |
michael@0 | 160 | sidebar.removeAttribute("hidden"); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | function hideSearchSidebar(aEvent) { |
michael@0 | 164 | // if we're focused in the search results, focus content |
michael@0 | 165 | var searchTree = document.getElementById("help-search-tree"); |
michael@0 | 166 | if (searchTree.treeBoxObject.focused) { |
michael@0 | 167 | content.focus(); |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | var sidebar = document.getElementById("help-search-sidebar"); |
michael@0 | 171 | sidebar.setAttribute("hidden", "true"); |
michael@0 | 172 | |
michael@0 | 173 | var tableOfContents = document.getElementById("help-toc-sidebar"); |
michael@0 | 174 | tableOfContents.removeAttribute("hidden"); |
michael@0 | 175 | } |
michael@0 | 176 | |
michael@0 | 177 | # loadHelpRDF |
michael@0 | 178 | # Parse the provided help content pack RDF file, and use it to |
michael@0 | 179 | # populate the datasources attached to the trees in the viewer. |
michael@0 | 180 | # Filter out any information not applicable to the user's platform. |
michael@0 | 181 | function loadHelpRDF() { |
michael@0 | 182 | if (!helpFileDS) { |
michael@0 | 183 | try { |
michael@0 | 184 | helpFileDS = RDF.GetDataSourceBlocking(helpFileURI); |
michael@0 | 185 | } catch (e if (e.result == NSRESULT_RDF_SYNTAX_ERROR)) { |
michael@0 | 186 | log("Help file: " + helpFileURI + " contains a syntax error."); |
michael@0 | 187 | } catch (e) { |
michael@0 | 188 | log("Help file: " + helpFileURI + " was not found."); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | try { |
michael@0 | 192 | document.title = getAttribute(helpFileDS, RDF_ROOT, NC_TITLE, ""); |
michael@0 | 193 | helpBaseURI = getAttribute(helpFileDS, RDF_ROOT, NC_BASE, helpBaseURI); |
michael@0 | 194 | // if there's no nc:defaulttopic in the content pack, set "welcome" |
michael@0 | 195 | // as the default topic |
michael@0 | 196 | defaultTopic = getAttribute(helpFileDS, |
michael@0 | 197 | RDF_ROOT, NC_DEFAULTTOPIC, "welcome"); |
michael@0 | 198 | |
michael@0 | 199 | var panelDefs = helpFileDS.GetTarget(RDF_ROOT, NC_PANELLIST, true); |
michael@0 | 200 | RDFContainer.Init(helpFileDS, panelDefs); |
michael@0 | 201 | var iterator = RDFContainer.GetElements(); |
michael@0 | 202 | while (iterator.hasMoreElements()) { |
michael@0 | 203 | var panelDef = iterator.getNext(); |
michael@0 | 204 | |
michael@0 | 205 | var panelID = getAttribute(helpFileDS, panelDef, NC_PANELID, null); |
michael@0 | 206 | var datasources = getAttribute(helpFileDS, panelDef, NC_DATASOURCES, ""); |
michael@0 | 207 | var panelPlatforms = getAttribute(helpFileDS, panelDef, NC_PLATFORM, null); |
michael@0 | 208 | |
michael@0 | 209 | if (panelPlatforms && panelPlatforms.split(/\s+/).indexOf(platform) == -1) |
michael@0 | 210 | continue; // ignore datasources for other platforms |
michael@0 | 211 | |
michael@0 | 212 | // empty datasources are valid on search panel definitions |
michael@0 | 213 | // convert them to "rdf:null" which can be filtered and ignored |
michael@0 | 214 | if (!datasources) |
michael@0 | 215 | datasources = "rdf:null"; |
michael@0 | 216 | |
michael@0 | 217 | datasources = normalizeLinks(helpBaseURI, datasources); |
michael@0 | 218 | |
michael@0 | 219 | var datasourceArray = datasources.split(/\s+/) |
michael@0 | 220 | .filter(function(x) { return x != "rdf:null"; }) |
michael@0 | 221 | .map(RDF.GetDataSourceBlocking); |
michael@0 | 222 | |
michael@0 | 223 | // Cache Additional Datasources to Augment Search Datasources. |
michael@0 | 224 | if (panelID == "search") { |
michael@0 | 225 | emptySearchText = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_TEXT, emptySearchText); |
michael@0 | 226 | emptySearchLink = getAttribute(helpFileDS, panelDef, NC_EMPTY_SEARCH_LINK, emptySearchLink); |
michael@0 | 227 | |
michael@0 | 228 | datasourceArray.forEach(helpSearchPanel.database.AddDataSource, |
michael@0 | 229 | helpSearchPanel.database); |
michael@0 | 230 | if (!panelPlatforms) |
michael@0 | 231 | filterDatasourceByPlatform(helpSearchPanel.database); |
michael@0 | 232 | |
michael@0 | 233 | continue; // to next panel definition |
michael@0 | 234 | } |
michael@0 | 235 | |
michael@0 | 236 | // cache toc datasources list for use in getLink() |
michael@0 | 237 | if (panelID == "toc") |
michael@0 | 238 | gTocDSList += " " + datasources; |
michael@0 | 239 | |
michael@0 | 240 | var tree = document.getElementById("help-" + panelID + "-panel"); |
michael@0 | 241 | |
michael@0 | 242 | // add each datasource to the current tree |
michael@0 | 243 | datasourceArray.forEach(tree.database.AddDataSource, |
michael@0 | 244 | tree.database); |
michael@0 | 245 | |
michael@0 | 246 | // filter and display the current tree |
michael@0 | 247 | if (!panelPlatforms) |
michael@0 | 248 | filterDatasourceByPlatform(tree.database); |
michael@0 | 249 | tree.builder.rebuild(); |
michael@0 | 250 | } |
michael@0 | 251 | } catch (e) { |
michael@0 | 252 | log(e + ""); |
michael@0 | 253 | } |
michael@0 | 254 | } |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | # filterDatasourceByPlatform |
michael@0 | 258 | # Remove statements for other platforms from a datasource. |
michael@0 | 259 | function filterDatasourceByPlatform(aDatasource) { |
michael@0 | 260 | filterNodeByPlatform(aDatasource, RDF_ROOT, 0); |
michael@0 | 261 | } |
michael@0 | 262 | |
michael@0 | 263 | # filterNodeByPlatform |
michael@0 | 264 | # Remove statements for other platforms from the provided datasource. |
michael@0 | 265 | function filterNodeByPlatform(aDatasource, aCurrentResource, aCurrentLevel) { |
michael@0 | 266 | if (aCurrentLevel > MAX_LEVEL) { |
michael@0 | 267 | log("Datasources over " + MAX_LEVEL + " levels deep are unsupported."); |
michael@0 | 268 | return; |
michael@0 | 269 | } |
michael@0 | 270 | |
michael@0 | 271 | // get the subheadings under aCurrentResource and filter them |
michael@0 | 272 | var nodes = aDatasource.GetTargets(aCurrentResource, NC_SUBHEADINGS, true); |
michael@0 | 273 | while (nodes.hasMoreElements()) { |
michael@0 | 274 | var node = nodes.getNext(); |
michael@0 | 275 | node = node.QueryInterface(Components.interfaces.nsIRDFResource); |
michael@0 | 276 | // should we test for rdf:Seq here? see also doFindOnDatasource |
michael@0 | 277 | filterSeqByPlatform(aDatasource, node, aCurrentLevel+1); |
michael@0 | 278 | } |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | # filterSeqByPlatform |
michael@0 | 282 | # Go through the children of aNode, if any, removing statements applicable |
michael@0 | 283 | # only on other platforms. |
michael@0 | 284 | function filterSeqByPlatform(aDatasource, aNode, aCurrentLevel) { |
michael@0 | 285 | // get nc:subheading children into an enumerator |
michael@0 | 286 | var RDFC = Components.classes["@mozilla.org/rdf/container;1"] |
michael@0 | 287 | .createInstance(Components.interfaces.nsIRDFContainer); |
michael@0 | 288 | RDFC.Init(aDatasource, aNode); |
michael@0 | 289 | var targets = RDFC.GetElements(); |
michael@0 | 290 | |
michael@0 | 291 | // process items in the rdf:Seq |
michael@0 | 292 | while (targets.hasMoreElements()) { |
michael@0 | 293 | var currentTarget = targets.getNext(); |
michael@0 | 294 | |
michael@0 | 295 | // find out on which platforms this node is meaningful |
michael@0 | 296 | var nodePlatforms = getAttribute(aDatasource, |
michael@0 | 297 | currentTarget.QueryInterface(Components.interfaces.nsIRDFResource), |
michael@0 | 298 | NC_PLATFORM, |
michael@0 | 299 | platform); |
michael@0 | 300 | |
michael@0 | 301 | if (nodePlatforms.split(/\s+/).indexOf(platform) == -1) { // node is for another platform |
michael@0 | 302 | var currentNode = currentTarget.QueryInterface(Components.interfaces.nsIRDFNode); |
michael@0 | 303 | // "false" because we don't want to renumber elements in the container |
michael@0 | 304 | RDFC.RemoveElement(currentNode, false); |
michael@0 | 305 | |
michael@0 | 306 | // move to next node - ignore the children, because 1) they might be |
michael@0 | 307 | // needed elsewhere and 2) nodes not connected to RDF_ROOT are ignored |
michael@0 | 308 | continue; |
michael@0 | 309 | } |
michael@0 | 310 | |
michael@0 | 311 | // filter any children |
michael@0 | 312 | filterNodeByPlatform(aDatasource, currentTarget, aCurrentLevel+1); |
michael@0 | 313 | } |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | # Prepend helpBaseURI to list of space separated links if they don't start with |
michael@0 | 317 | # "chrome:" |
michael@0 | 318 | function normalizeLinks(helpBaseURI, links) { |
michael@0 | 319 | if (!helpBaseURI) { |
michael@0 | 320 | return links; |
michael@0 | 321 | } |
michael@0 | 322 | var ls = links.split(/\s+/); |
michael@0 | 323 | if (ls.length == 0) { |
michael@0 | 324 | return links; |
michael@0 | 325 | } |
michael@0 | 326 | for (var i=0; i < ls.length; ++i) { |
michael@0 | 327 | if (ls[i] == "") |
michael@0 | 328 | continue; |
michael@0 | 329 | |
michael@0 | 330 | if (ls[i].substr(0,7) != "chrome:" && ls[i].substr(0,4) != "rdf:") |
michael@0 | 331 | ls[i] = helpBaseURI + ls[i]; |
michael@0 | 332 | } |
michael@0 | 333 | return ls.join(" "); |
michael@0 | 334 | } |
michael@0 | 335 | |
michael@0 | 336 | function getLink(ID) { |
michael@0 | 337 | if (!ID) |
michael@0 | 338 | return null; |
michael@0 | 339 | |
michael@0 | 340 | var tocDS = document.getElementById("help-toc-panel").database; |
michael@0 | 341 | if (!tocDS) |
michael@0 | 342 | return null; |
michael@0 | 343 | |
michael@0 | 344 | // URIs include both the ID part and the base file name, |
michael@0 | 345 | // so we need to check for a matching ID in each datasource |
michael@0 | 346 | var tocDSArray = gTocDSList.split(/\s+/) |
michael@0 | 347 | .filter(function(x) { return x != "rdf:null"; }); |
michael@0 | 348 | |
michael@0 | 349 | for (var i = 0; i < tocDSArray.length; i++) { |
michael@0 | 350 | var resource = RDF.GetResource(tocDSArray[i] + "#" + ID); |
michael@0 | 351 | var link = tocDS.GetTarget(resource, NC_LINK, true); |
michael@0 | 352 | if (!link) // no such rdf:ID found |
michael@0 | 353 | continue; |
michael@0 | 354 | return link.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; |
michael@0 | 355 | } |
michael@0 | 356 | return null; |
michael@0 | 357 | } |
michael@0 | 358 | |
michael@0 | 359 | # Called by contextHelp.js to determine if this window is displaying the |
michael@0 | 360 | # requested help file. |
michael@0 | 361 | function getHelpFileURI() { |
michael@0 | 362 | return helpFileURI; |
michael@0 | 363 | } |
michael@0 | 364 | |
michael@0 | 365 | function getBrowser() { |
michael@0 | 366 | return helpBrowser; |
michael@0 | 367 | } |
michael@0 | 368 | |
michael@0 | 369 | function getWebNavigation() { |
michael@0 | 370 | try { |
michael@0 | 371 | return helpBrowser.webNavigation; |
michael@0 | 372 | } catch (e) |
michael@0 | 373 | { |
michael@0 | 374 | return null; |
michael@0 | 375 | } |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | function loadURI(uri) { |
michael@0 | 379 | if (uri.substr(0,7) != "chrome:") { |
michael@0 | 380 | uri = helpBaseURI + uri; |
michael@0 | 381 | } |
michael@0 | 382 | getWebNavigation().loadURI(uri, Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE, |
michael@0 | 383 | null, null, null); |
michael@0 | 384 | } |
michael@0 | 385 | |
michael@0 | 386 | function goBack() { |
michael@0 | 387 | try |
michael@0 | 388 | { |
michael@0 | 389 | getWebNavigation().goBack(); |
michael@0 | 390 | } catch (e) |
michael@0 | 391 | { |
michael@0 | 392 | } |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | function goForward() { |
michael@0 | 396 | try |
michael@0 | 397 | { |
michael@0 | 398 | getWebNavigation().goForward(); |
michael@0 | 399 | } catch(e) |
michael@0 | 400 | { |
michael@0 | 401 | } |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | function goHome() { |
michael@0 | 405 | // Load "Welcome" page |
michael@0 | 406 | displayTopic(defaultTopic); |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | function print() { |
michael@0 | 410 | try { |
michael@0 | 411 | _content.print(); |
michael@0 | 412 | } catch (e) { |
michael@0 | 413 | } |
michael@0 | 414 | } |
michael@0 | 415 | |
michael@0 | 416 | function FillHistoryMenu(aParent, aMenu) |
michael@0 | 417 | { |
michael@0 | 418 | // Remove old entries if any |
michael@0 | 419 | deleteHistoryItems(aParent); |
michael@0 | 420 | |
michael@0 | 421 | var sessionHistory = getWebNavigation().sessionHistory; |
michael@0 | 422 | |
michael@0 | 423 | var count = sessionHistory.count; |
michael@0 | 424 | var index = sessionHistory.index; |
michael@0 | 425 | var end; |
michael@0 | 426 | var j; |
michael@0 | 427 | var entry; |
michael@0 | 428 | |
michael@0 | 429 | switch (aMenu) |
michael@0 | 430 | { |
michael@0 | 431 | case "back": |
michael@0 | 432 | end = (index > MAX_HISTORY_MENU_ITEMS) ? index - MAX_HISTORY_MENU_ITEMS : 0; |
michael@0 | 433 | if ((index - 1) < end) return false; |
michael@0 | 434 | for (j = index - 1; j >= end; j--) |
michael@0 | 435 | { |
michael@0 | 436 | entry = sessionHistory.getEntryAtIndex(j, false); |
michael@0 | 437 | if (entry) |
michael@0 | 438 | createMenuItem(aParent, j, entry.title); |
michael@0 | 439 | } |
michael@0 | 440 | break; |
michael@0 | 441 | case "forward": |
michael@0 | 442 | end = ((count-index) > MAX_HISTORY_MENU_ITEMS) ? index + MAX_HISTORY_MENU_ITEMS : count - 1; |
michael@0 | 443 | if ((index + 1) > end) return false; |
michael@0 | 444 | for (j = index + 1; j <= end; j++) |
michael@0 | 445 | { |
michael@0 | 446 | entry = sessionHistory.getEntryAtIndex(j, false); |
michael@0 | 447 | if (entry) |
michael@0 | 448 | createMenuItem(aParent, j, entry.title); |
michael@0 | 449 | } |
michael@0 | 450 | break; |
michael@0 | 451 | } |
michael@0 | 452 | return true; |
michael@0 | 453 | } |
michael@0 | 454 | |
michael@0 | 455 | function createMenuItem( aParent, aIndex, aLabel) |
michael@0 | 456 | { |
michael@0 | 457 | var menuitem = document.createElement( "menuitem" ); |
michael@0 | 458 | menuitem.setAttribute( "label", aLabel ); |
michael@0 | 459 | menuitem.setAttribute( "index", aIndex ); |
michael@0 | 460 | aParent.appendChild( menuitem ); |
michael@0 | 461 | } |
michael@0 | 462 | |
michael@0 | 463 | function deleteHistoryItems(aParent) |
michael@0 | 464 | { |
michael@0 | 465 | var children = aParent.childNodes; |
michael@0 | 466 | for (var i = children.length - 1; i >= 0; --i) |
michael@0 | 467 | { |
michael@0 | 468 | var index = children[i].getAttribute("index"); |
michael@0 | 469 | if (index) |
michael@0 | 470 | aParent.removeChild(children[i]); |
michael@0 | 471 | } |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | function createBackMenu(event) { |
michael@0 | 475 | return FillHistoryMenu(event.target, "back"); |
michael@0 | 476 | } |
michael@0 | 477 | |
michael@0 | 478 | function createForwardMenu(event) { |
michael@0 | 479 | return FillHistoryMenu(event.target, "forward"); |
michael@0 | 480 | } |
michael@0 | 481 | |
michael@0 | 482 | function gotoHistoryIndex(aEvent) { |
michael@0 | 483 | var index = aEvent.target.getAttribute("index"); |
michael@0 | 484 | if (!index) { |
michael@0 | 485 | return false; |
michael@0 | 486 | } |
michael@0 | 487 | try { |
michael@0 | 488 | getWebNavigation().gotoIndex(index); |
michael@0 | 489 | } catch(ex) { |
michael@0 | 490 | return false; |
michael@0 | 491 | } |
michael@0 | 492 | return true; |
michael@0 | 493 | } |
michael@0 | 494 | |
michael@0 | 495 | function nsHelpStatusHandler() { |
michael@0 | 496 | this.init(); |
michael@0 | 497 | } |
michael@0 | 498 | |
michael@0 | 499 | nsHelpStatusHandler.prototype = { |
michael@0 | 500 | |
michael@0 | 501 | onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {}, |
michael@0 | 502 | onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress, |
michael@0 | 503 | aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {}, |
michael@0 | 504 | onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {}, |
michael@0 | 505 | onSecurityChange : function(aWebProgress, aRequest, state) {}, |
michael@0 | 506 | onLocationChange : function(aWebProgress, aRequest, aLocation, aFlags) { |
michael@0 | 507 | UpdateBackForwardButtons(); |
michael@0 | 508 | }, |
michael@0 | 509 | QueryInterface : function(aIID) { |
michael@0 | 510 | if (aIID.equals(Components.interfaces.nsIWebProgressListener) || |
michael@0 | 511 | aIID.equals(Components.interfaces.nsISupportsWeakReference) || |
michael@0 | 512 | aIID.equals(Components.interfaces.nsIXULBrowserWindow) || |
michael@0 | 513 | aIID.equals(Components.interfaces.nsISupports)) { |
michael@0 | 514 | return this; |
michael@0 | 515 | } |
michael@0 | 516 | throw Components.results.NS_NOINTERFACE; |
michael@0 | 517 | }, |
michael@0 | 518 | |
michael@0 | 519 | init : function() {}, |
michael@0 | 520 | |
michael@0 | 521 | destroy : function() {}, |
michael@0 | 522 | |
michael@0 | 523 | setJSStatus : function(status) {}, |
michael@0 | 524 | setOverLink : function(link, context) {}, |
michael@0 | 525 | onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {} |
michael@0 | 526 | } |
michael@0 | 527 | |
michael@0 | 528 | function UpdateBackForwardButtons() { |
michael@0 | 529 | var backBroadcaster = document.getElementById("canGoBack"); |
michael@0 | 530 | var forwardBroadcaster = document.getElementById("canGoForward"); |
michael@0 | 531 | var webNavigation = getWebNavigation(); |
michael@0 | 532 | |
michael@0 | 533 | // Avoid setting attributes on broadcasters if the value hasn't changed! |
michael@0 | 534 | // Remember, guys, setting attributes on elements is expensive! They |
michael@0 | 535 | // get inherited into anonymous content, broadcast to other widgets, etc.! |
michael@0 | 536 | // Don't do it if the value hasn't changed! - dwh |
michael@0 | 537 | |
michael@0 | 538 | var backDisabled = (backBroadcaster.getAttribute("disabled") == "true"); |
michael@0 | 539 | var forwardDisabled = (forwardBroadcaster.getAttribute("disabled") == "true"); |
michael@0 | 540 | |
michael@0 | 541 | if (backDisabled == webNavigation.canGoBack) { |
michael@0 | 542 | if (backDisabled) |
michael@0 | 543 | backBroadcaster.removeAttribute("disabled"); |
michael@0 | 544 | else |
michael@0 | 545 | backBroadcaster.setAttribute("disabled", true); |
michael@0 | 546 | } |
michael@0 | 547 | |
michael@0 | 548 | if (forwardDisabled == webNavigation.canGoForward) { |
michael@0 | 549 | if (forwardDisabled) |
michael@0 | 550 | forwardBroadcaster.removeAttribute("disabled"); |
michael@0 | 551 | else |
michael@0 | 552 | forwardBroadcaster.setAttribute("disabled", true); |
michael@0 | 553 | } |
michael@0 | 554 | } |
michael@0 | 555 | |
michael@0 | 556 | function onselect_loadURI(tree) { |
michael@0 | 557 | try { |
michael@0 | 558 | var resource = tree.view.getResourceAtIndex(tree.currentIndex); |
michael@0 | 559 | var link = tree.database.GetTarget(resource, NC_LINK, true); |
michael@0 | 560 | if (link) { |
michael@0 | 561 | link = link.QueryInterface(Components.interfaces.nsIRDFLiteral); |
michael@0 | 562 | loadURI(link.Value); |
michael@0 | 563 | } |
michael@0 | 564 | } catch (e) { |
michael@0 | 565 | }// when switching between tabs a spurious row number is returned. |
michael@0 | 566 | } |
michael@0 | 567 | |
michael@0 | 568 | function focusSearch() { |
michael@0 | 569 | var searchBox = document.getElementById("findText"); |
michael@0 | 570 | searchBox.focus(); |
michael@0 | 571 | } |
michael@0 | 572 | |
michael@0 | 573 | # doFind - Searches the help files for what is located in findText and outputs into |
michael@0 | 574 | # the find search tree. |
michael@0 | 575 | function doFind() { |
michael@0 | 576 | if (document.getElementById("help-search-sidebar").hidden) |
michael@0 | 577 | showSearchSidebar(); |
michael@0 | 578 | |
michael@0 | 579 | var searchTree = document.getElementById("help-search-tree"); |
michael@0 | 580 | var findText = document.getElementById("findText"); |
michael@0 | 581 | |
michael@0 | 582 | // clear any previous results. |
michael@0 | 583 | clearDatabases(searchTree.database); |
michael@0 | 584 | |
michael@0 | 585 | // if the search string is empty or contains only whitespace, purge the results tree and return |
michael@0 | 586 | RE = findText.value.match(/\S+/g); |
michael@0 | 587 | if (!RE) { |
michael@0 | 588 | searchTree.builder.rebuild(); |
michael@0 | 589 | hideSearchSidebar(); |
michael@0 | 590 | return; |
michael@0 | 591 | } |
michael@0 | 592 | |
michael@0 | 593 | // compile the search string, which has already been split up above, into regexps |
michael@0 | 594 | for (var i=0; i < RE.length; ++i) { |
michael@0 | 595 | RE[i] = new RegExp(RE[i], "i"); |
michael@0 | 596 | } |
michael@0 | 597 | emptySearch = true; |
michael@0 | 598 | |
michael@0 | 599 | // search TOC |
michael@0 | 600 | var resultsDS = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] |
michael@0 | 601 | .createInstance(Components.interfaces.nsIRDFDataSource); |
michael@0 | 602 | var sourceDS = helpTocPanel.database; |
michael@0 | 603 | doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0); |
michael@0 | 604 | |
michael@0 | 605 | // search glossary. |
michael@0 | 606 | sourceDS = helpGlossaryPanel.database; |
michael@0 | 607 | doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0); |
michael@0 | 608 | |
michael@0 | 609 | // search index |
michael@0 | 610 | sourceDS = helpIndexPanel.database; |
michael@0 | 611 | doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0); |
michael@0 | 612 | |
michael@0 | 613 | // search additional search datasources |
michael@0 | 614 | sourceDS = helpSearchPanel.database; |
michael@0 | 615 | doFindOnDatasource(resultsDS, sourceDS, RDF_ROOT, 0); |
michael@0 | 616 | |
michael@0 | 617 | if (emptySearch) |
michael@0 | 618 | assertSearchEmpty(resultsDS); |
michael@0 | 619 | // Add the datasource to the search tree |
michael@0 | 620 | searchTree.database.AddDataSource(resultsDS); |
michael@0 | 621 | searchTree.builder.rebuild(); |
michael@0 | 622 | } |
michael@0 | 623 | |
michael@0 | 624 | function clearDatabases(compositeDataSource) { |
michael@0 | 625 | var enumDS = compositeDataSource.GetDataSources() |
michael@0 | 626 | while (enumDS.hasMoreElements()) { |
michael@0 | 627 | var ds = enumDS.getNext(); |
michael@0 | 628 | compositeDataSource.RemoveDataSource(ds); |
michael@0 | 629 | } |
michael@0 | 630 | } |
michael@0 | 631 | |
michael@0 | 632 | function doFindOnDatasource(resultsDS, sourceDS, resource, level) { |
michael@0 | 633 | if (level > MAX_LEVEL) { |
michael@0 | 634 | try { |
michael@0 | 635 | log("Recursive reference to resource: " + resource.Value + "."); |
michael@0 | 636 | } catch (e) { |
michael@0 | 637 | log("Recursive reference to unknown resource."); |
michael@0 | 638 | } |
michael@0 | 639 | return; |
michael@0 | 640 | } |
michael@0 | 641 | // find all SUBHEADING children of current resource. |
michael@0 | 642 | var targets = sourceDS.GetTargets(resource, NC_SUBHEADINGS, true); |
michael@0 | 643 | while (targets.hasMoreElements()) { |
michael@0 | 644 | var target = targets.getNext(); |
michael@0 | 645 | target = target.QueryInterface(Components.interfaces.nsIRDFResource); |
michael@0 | 646 | // The first child of a rdf:subheading should (must) be a rdf:seq. |
michael@0 | 647 | // Should we test for a SEQ here? |
michael@0 | 648 | doFindOnSeq(resultsDS, sourceDS, target, level+1); |
michael@0 | 649 | } |
michael@0 | 650 | } |
michael@0 | 651 | |
michael@0 | 652 | function doFindOnSeq(resultsDS, sourceDS, resource, level) { |
michael@0 | 653 | // load up an RDFContainer so we can access the contents of the current |
michael@0 | 654 | // rdf:seq. |
michael@0 | 655 | RDFContainer.Init(sourceDS, resource); |
michael@0 | 656 | var targets = RDFContainer.GetElements(); |
michael@0 | 657 | while (targets.hasMoreElements()) { |
michael@0 | 658 | var target = targets.getNext(); |
michael@0 | 659 | var link = sourceDS.GetTarget(target, NC_LINK, true); |
michael@0 | 660 | var name = sourceDS.GetTarget(target, NC_NAME, true); |
michael@0 | 661 | |
michael@0 | 662 | if (link && |
michael@0 | 663 | name instanceof Components.interfaces.nsIRDFLiteral && |
michael@0 | 664 | isMatch(name.Value)) { |
michael@0 | 665 | // we have found a search entry - add it to the results datasource. |
michael@0 | 666 | var urn = RDF.GetAnonymousResource(); |
michael@0 | 667 | resultsDS.Assert(urn, NC_NAME, name, true); |
michael@0 | 668 | resultsDS.Assert(urn, NC_LINK, link, true); |
michael@0 | 669 | resultsDS.Assert(RDF_ROOT, NC_CHILD, urn, true); |
michael@0 | 670 | |
michael@0 | 671 | emptySearch = false; |
michael@0 | 672 | } |
michael@0 | 673 | // process any nested rdf:seq elements. |
michael@0 | 674 | doFindOnDatasource(resultsDS, sourceDS, target, level+1); |
michael@0 | 675 | } |
michael@0 | 676 | } |
michael@0 | 677 | |
michael@0 | 678 | function assertSearchEmpty(resultsDS) { |
michael@0 | 679 | var resSearchEmpty = RDF.GetResource("urn:emptySearch"); |
michael@0 | 680 | resultsDS.Assert(RDF_ROOT, |
michael@0 | 681 | NC_CHILD, |
michael@0 | 682 | resSearchEmpty, |
michael@0 | 683 | true); |
michael@0 | 684 | resultsDS.Assert(resSearchEmpty, |
michael@0 | 685 | NC_NAME, |
michael@0 | 686 | RDF.GetLiteral(emptySearchText), |
michael@0 | 687 | true); |
michael@0 | 688 | resultsDS.Assert(resSearchEmpty, |
michael@0 | 689 | NC_LINK, |
michael@0 | 690 | RDF.GetLiteral(emptySearchLink), |
michael@0 | 691 | true); |
michael@0 | 692 | } |
michael@0 | 693 | |
michael@0 | 694 | function isMatch(text) { |
michael@0 | 695 | for (var i=0; i < RE.length; ++i ) { |
michael@0 | 696 | if (!RE[i].test(text)) { |
michael@0 | 697 | return false; |
michael@0 | 698 | } |
michael@0 | 699 | } |
michael@0 | 700 | return true; |
michael@0 | 701 | } |
michael@0 | 702 | |
michael@0 | 703 | function getAttribute(datasource, resource, attributeResourceName, |
michael@0 | 704 | defaultValue) { |
michael@0 | 705 | var literal = datasource.GetTarget(resource, attributeResourceName, true); |
michael@0 | 706 | if (!literal) { |
michael@0 | 707 | return defaultValue; |
michael@0 | 708 | } |
michael@0 | 709 | return getLiteralValue(literal, defaultValue); |
michael@0 | 710 | } |
michael@0 | 711 | |
michael@0 | 712 | function getLiteralValue(literal, defaultValue) { |
michael@0 | 713 | if (literal) { |
michael@0 | 714 | literal = literal.QueryInterface(Components.interfaces.nsIRDFLiteral); |
michael@0 | 715 | if (literal) { |
michael@0 | 716 | return literal.Value; |
michael@0 | 717 | } |
michael@0 | 718 | } |
michael@0 | 719 | if (defaultValue) { |
michael@0 | 720 | return defaultValue; |
michael@0 | 721 | } |
michael@0 | 722 | return null; |
michael@0 | 723 | } |
michael@0 | 724 | |
michael@0 | 725 | # Write debug string to error console. |
michael@0 | 726 | function log(aText) { |
michael@0 | 727 | CONSOLE_SERVICE.logStringMessage(aText); |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | function getBoolPref (aPrefname, aDefault) |
michael@0 | 731 | { |
michael@0 | 732 | try { |
michael@0 | 733 | var pref = Components.classes["@mozilla.org/preferences-service;1"] |
michael@0 | 734 | .getService(Components.interfaces.nsIPrefBranch); |
michael@0 | 735 | return pref.getBoolPref(aPrefname); |
michael@0 | 736 | } |
michael@0 | 737 | catch(e) { |
michael@0 | 738 | return aDefault; |
michael@0 | 739 | } |
michael@0 | 740 | } |
michael@0 | 741 | |
michael@0 | 742 | # getXulWin - Returns the current Help window as a nsIXULWindow. |
michael@0 | 743 | function getXulWin() |
michael@0 | 744 | { |
michael@0 | 745 | window.QueryInterface(Components.interfaces.nsIInterfaceRequestor); |
michael@0 | 746 | var webnav = window.getInterface(Components.interfaces.nsIWebNavigation); |
michael@0 | 747 | var dsti = webnav.QueryInterface(Components.interfaces.nsIDocShellTreeItem); |
michael@0 | 748 | var treeowner = dsti.treeOwner; |
michael@0 | 749 | var ifreq = treeowner.QueryInterface(Components.interfaces.nsIInterfaceRequestor); |
michael@0 | 750 | |
michael@0 | 751 | return ifreq.getInterface(Components.interfaces.nsIXULWindow); |
michael@0 | 752 | } |
michael@0 | 753 | |
michael@0 | 754 | # toggleZLevel - Toggles whether or not the window will always appear on top. Because |
michael@0 | 755 | # alwaysRaised is not supported on an OS other than Windows, this code will not |
michael@0 | 756 | # appear in those builds. |
michael@0 | 757 | # |
michael@0 | 758 | # element - The DOM node that persists the checked state. |
michael@0 | 759 | #ifdef XP_WIN |
michael@0 | 760 | #define HELP_ALWAYS_RAISED_TOGGLE |
michael@0 | 761 | #endif |
michael@0 | 762 | #ifdef HELP_ALWAYS_RAISED_TOGGLE |
michael@0 | 763 | function toggleZLevel(element) |
michael@0 | 764 | { |
michael@0 | 765 | var xulwin = getXulWin(); |
michael@0 | 766 | |
michael@0 | 767 | // Now we can flip the zLevel, and set the attribute so that it persists correctly |
michael@0 | 768 | if (xulwin.zLevel > xulwin.normalZ) { |
michael@0 | 769 | xulwin.zLevel = xulwin.normalZ; |
michael@0 | 770 | element.setAttribute("checked", "false"); |
michael@0 | 771 | } else { |
michael@0 | 772 | xulwin.zLevel = xulwin.raisedZ; |
michael@0 | 773 | element.setAttribute("checked", "true"); |
michael@0 | 774 | } |
michael@0 | 775 | } |
michael@0 | 776 | #endif |