home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2005 August / PCADVD_121.iso / internet / nsb-setup.exe / chrome / browser.jar / content / browser / sidebar / sidebarOverlay.js < prev    next >
Encoding:
JavaScript  |  2005-03-30  |  55.9 KB  |  1,753 lines

  1. /* -*- Mode: Java; tab-width: 4; insert-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Mozilla Public License
  4.  * Version 1.1 (the "License"); you may not use this file except in
  5.  * compliance with the License. You may obtain a copy of the License at
  6.  * http://www.mozilla.org/MPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS IS"
  9.  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  10.  * the License for the specific language governing rights and
  11.  * limitations under the License.
  12.  *
  13.  * The Original Code is Mozilla Communicator client code, released
  14.  * March 31, 1998.
  15.  *
  16.  * The Initial Developer of the Original Code is Netscape
  17.  * Communications Corporation. Portions created by Netscape are
  18.  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
  19.  * Rights Reserved.
  20.  */
  21.  
  22. // xul/id summary:
  23. //
  24. // <box> sidebar-box
  25. //    <splitter> sidebar-panels-splitter
  26. //       <box> sidebar-panels-splitter-box*
  27. //    <sidebarheader> sidebar-title-box
  28. //       <menubutton> sidebar-panel-picker*
  29. //          <menupopup> sidebar-panel-picker-popup
  30. //    <box> sidebar-panels
  31. //       <template> sidebar-template*
  32. //          <box> sidebar-iframe-no-panels
  33. // <splitter> sidebar-splitter
  34. // <menupopup> menu_View_Popup*
  35. //    <menuitem> sidebar-menu
  36.  
  37. //////////////////////////////////////////////////////////////
  38. // Global variables
  39. //////////////////////////////////////////////////////////////
  40.  
  41. var gCurFrame;
  42. var gTimeoutID = null;
  43. var gMustInit = true;
  44. var gAboutToUncollapse = false;
  45. var gCheckMissingPanels = true;
  46. var gSearchService = null;
  47. var numTries = 0;
  48.  
  49. function setBlank()
  50. {
  51.     gTimeoutID = null;
  52.     gCurFrame.setAttribute('src', 'chrome://browser/content/sidebar/PageNotFound.xul');
  53. }
  54.  
  55.  
  56. // Uncomment for debug output
  57. const SB_DEBUG = false;
  58.  
  59. // pref for limiting number of tabs in view
  60. // initialized in sidebar_overlay_init()
  61. var gNumTabsInViewPref;
  62.  
  63. // The rdf service
  64. const RDF_URI = '@mozilla.org/rdf/rdf-service;1';
  65. var RDF = Components.classes[RDF_URI].getService();
  66. RDF = RDF.QueryInterface(Components.interfaces.nsIRDFService);
  67.  
  68. const NC = "http://home.netscape.com/NC-rdf#";
  69.  
  70. // The directory services property to find panels.rdf
  71. const PANELS_RDF_FILE = "UPnls";
  72. const SIDEBAR_VERSION = "0.1";
  73.  
  74. // The default sidebar:
  75. var sidebarObj = new Object;
  76. sidebarObj.never_built = true;
  77.  
  78. //////////////////////////////////////////////////////////////////////
  79. // sbPanelList Class
  80. //
  81. // Wrapper around DOM representation of the sidebar. This UI event
  82. // handlers and the sidebar datasource observers call into this.  This
  83. // class is responsible for keeping the DOM state of the sidebar up to
  84. // date.
  85. // This class does not make any changes to the sidebar rdf datasource.
  86. //////////////////////////////////////////////////////////////////////
  87.  
  88. function sbPanelList(container_id)
  89. {
  90.   debug("sbPanelList("+container_id+")");
  91.   this.node = document.getElementById(container_id);
  92.   this.childNodes = this.node.childNodes;
  93.   this.initialized = false; // set after first display of tabs
  94. }
  95.  
  96. sbPanelList.prototype.get_panel_from_id =
  97. function (id)
  98. {
  99.   debug("get_panel_from_id(" + id + ")... "+this.node.childNodes.length+" nodes");
  100.   var index = 0;
  101.   var header = null;
  102.   if (id && id != '') {
  103.     for (var ii=2; ii < this.node.childNodes.length; ii += 2) {
  104.       header = this.node.childNodes.item(ii);
  105.       if (header.getAttribute('id') == id) {
  106.         debug("get_panel_from_id: Found at index, " + ii);
  107.         index = ii;
  108.         break;
  109.       }
  110.     }
  111.   }
  112.   if (index > 0) {
  113.     return new sbPanel(id, header, index);
  114.   } else {
  115.     return null;
  116.   }
  117. }
  118.  
  119. sbPanelList.prototype.get_panel_from_header_node =
  120. function (node)
  121. {
  122.   return this.get_panel_from_id(node.getAttribute('id'));
  123. }
  124.  
  125. sbPanelList.prototype.get_panel_from_header_index =
  126. function (index)
  127. {
  128.   return this.get_panel_from_header_node(this.node.childNodes.item(index));
  129. }
  130.  
  131. sbPanelList.prototype.find_first =
  132. function (panels)
  133. {
  134.   debug("pick_default_panel: length=" + this.node.childNodes.length);
  135.   for (var ii = 2; ii < this.node.childNodes.length; ii += 2) {
  136.     var panel = this.get_panel_from_header_index(ii);
  137.     if (!panel.is_excluded() && panel.is_in_view()) {
  138.       return panel;
  139.     }
  140.   }
  141.   return null;
  142. }
  143.  
  144. sbPanelList.prototype.find_last =
  145. function (panels)
  146. {
  147.   debug("pick_default_panel: length=" + this.node.childNodes.length);
  148.   for (var ii=(this.node.childNodes.length - 1); ii >= 2; ii -= 2) {
  149.     var panel = this.get_panel_from_header_index(ii);
  150.     if (!panel.is_excluded() && panel.is_in_view()) {
  151.       return panel;
  152.     }
  153.   }
  154.   return null;
  155. }
  156.  
  157. sbPanelList.prototype.visible_panels_exist =
  158. function ()
  159. {
  160.   var i;
  161.   var panels = this.node.childNodes;
  162.   for (i = 2; i < panels.length; i += 2)
  163.   {
  164.     if (!panels.item(i).hidden)
  165.       return true;
  166.   }
  167.   return false;
  168. }
  169.  
  170. sbPanelList.prototype.num_panels_included =
  171. function ()
  172. {
  173.   var count = 0;
  174.   var panels = this.node.childNodes;
  175.   for (var i = 2; i < panels.length; i += 2)
  176.   {
  177.     var curr = this.get_panel_from_header_index(i);
  178.     if (!curr.is_excluded())
  179.       count++;
  180.   }
  181.   return count;
  182. }
  183.  
  184. sbPanelList.prototype.num_panels_in_view =
  185. function ()
  186. {
  187.   var count = 0;
  188.   var panels = this.node.childNodes;
  189.   for (var i = 2; i < panels.length; i += 2)
  190.   {
  191.     var curr = this.get_panel_from_header_index(i);
  192.     if (curr.is_in_view())
  193.       count++;
  194.   }
  195.   return count;
  196. }
  197.  
  198. sbPanelList.prototype.select =
  199. function (panel, force_reload)
  200. {
  201.   if (!force_reload && panel.is_selected()) {
  202.     return;
  203.   }
  204.   // select(): Open this panel and possibly reload it.
  205.   if (this.node.getAttribute('last-selected-panel') != panel.id) {
  206.     // "last-selected-panel" is used as a global variable.
  207.     // this.update() will reference "last-selected-panel".
  208.     // This way the value can be persisted in localstore.rdf.
  209.     this.node.setAttribute('last-selected-panel', panel.id);
  210.   }
  211.   this.update(force_reload);
  212. }
  213.  
  214. sbPanelList.prototype.exclude =
  215. function (panel)
  216. {
  217.   if (this.node.getAttribute('last-selected-panel') == panel.id) {
  218.     this.select_default_panel();
  219.   } else {
  220.     this.update(false);
  221.   }
  222. }
  223.  
  224.  
  225. sbPanelList.prototype.select_default_panel =
  226. function ()
  227. {
  228.   var default_panel = null
  229.  
  230.   // First, check the XUL for the "defaultpanel" attribute of "sidebar-box".
  231.   var sidebar_container = document.getElementById('sidebar-box');
  232.   var content_default_id = sidebar_container.getAttribute('defaultpanel');
  233.   if (content_default_id != '') {
  234.     var content = sidebarObj.panels.get_panel_from_id(content_default_id);
  235.     if (content && !content.is_excluded() && content.is_in_view()) {
  236.       default_panel = content;
  237.     }
  238.   }
  239.  
  240.   // Second, try to use the panel persisted in 'last-selected-panel'.
  241.   if (!default_panel) {
  242.     var last_selected_id = this.node.getAttribute('last-selected-panel');
  243.     if (last_selected_id != '') {
  244.       var last = sidebarObj.panels.get_panel_from_id(last_selected_id);
  245.       if (last && !last.is_excluded() && last.is_in_view()) {
  246.         default_panel = last;
  247.       }
  248.     }
  249.   }
  250.  
  251.   // Finally, just use the last one in the list.
  252.   if (!default_panel) {
  253.     default_panel = this.find_last();
  254.   }
  255.  
  256.   if (default_panel) {
  257.     this.node.setAttribute('last-selected-panel', default_panel.id);
  258.   }
  259.   this.update(false);
  260. }
  261.  
  262. sbPanelList.prototype.refresh =
  263. function ()
  264. {
  265.   var last_selected_id = this.node.getAttribute('last-selected-panel');
  266.   var last_selected = sidebarObj.panels.get_panel_from_id(last_selected_id);
  267.   if (last_selected && last_selected.is_selected()) {
  268.     // The desired panel is already selected
  269.     this.update(false);
  270.   } else {
  271.     this.select_default_panel();
  272.   }
  273. }
  274.  
  275. // panel_loader(): called from a timer that is set in sbPanelList.update()
  276. // Removes the "Loading..." screen when the panel has finished loading.
  277. function panel_loader() {
  278.   debug("panel_loader()");
  279.  
  280.   if (gTimeoutID != null) {
  281.       clearTimeout(gTimeoutID);
  282.       gTimeoutID = null; 
  283.   }
  284.  
  285.   this.removeEventListener("load", panel_loader, true);
  286.   this.removeAttribute('collapsed');
  287.   this.setAttribute('loadstate','loaded');
  288.   this.parentNode.firstChild.setAttribute('hidden','true');
  289.  
  290.   if (this.hasAttribute('focusOnLoad')) {
  291.     var elementToFocus = this.contentDocument.documentElement.getAttribute('elementtofocus');
  292.     if (elementToFocus) {
  293.       var element = this.contentDocument.getElementById(elementToFocus);
  294.       if (element)
  295.         element.focus();
  296.       else
  297.         dump(elementToFocus + ' element was not found to focus!\n');
  298.     } else {
  299.       this.contentWindow.focus();
  300.     }
  301.     this.removeAttribute('focusOnLoad');
  302.   }
  303. }
  304. sbPanelList.prototype.update =
  305. function (force_reload)
  306. {
  307.   // This function requires that the attribute 'last-selected-panel'
  308.   // holds the id of a non-excluded panel. If it doesn't, no panel will
  309.   // be selected. The attribute is used instead of a funciton
  310.   // parameter to allow the value to be persisted in localstore.rdf.
  311.   var selected_id = this.node.getAttribute('last-selected-panel');
  312.  
  313.   if (sidebar_is_collapsed()) {
  314.     sidebarObj.collapsed = true;
  315.   } else {
  316.     sidebarObj.collapsed = false;
  317.   }
  318.  
  319.   var num_included = sidebarObj.panels.num_panels_included();
  320.   if (num_included > gNumTabsInViewPref)
  321.     document.getElementById("nav-buttons-box").hidden = false;
  322.   else
  323.     document.getElementById("nav-buttons-box").hidden = true;
  324.  
  325.   var have_set_top = 0;
  326.   var have_set_after_selected = 0;
  327.   var is_after_selected = 0;
  328.   var last_header = 0;
  329.   var num_in_view = 0;
  330.   debug("this.initialized: " + this.initialized);
  331.   for (var ii=2; ii < this.node.childNodes.length; ii += 2) {
  332.     var header = this.node.childNodes.item(ii);
  333.     var content = this.node.childNodes.item(ii+1);
  334.     var id = header.getAttribute('id');
  335.     var panel = new sbPanel(id, header, ii);
  336.     var excluded = panel.is_excluded();
  337.     var in_view = false;
  338.     if (!this.initialized) 
  339.     {
  340.       if (num_in_view < gNumTabsInViewPref)
  341.         in_view = true;
  342.     }
  343.     else
  344.     {
  345.       if (header.getAttribute("in-view") == "true")
  346.         in_view = true;
  347.     }
  348.     if (excluded || !in_view)
  349.     {
  350.       debug("item("+ii/2+") excluded: " + excluded + 
  351.                           " in view: " + in_view);
  352.       header.setAttribute('hidden','true');
  353.       content.setAttribute('hidden','true');
  354.       if (!in_view)
  355.       {
  356.         header.setAttribute("in-view", false);
  357.         header.removeAttribute("top-panel");
  358.         header.removeAttribute("last-panel");
  359.       }
  360.     } else {
  361.       // only set if in view
  362.       if (!this.initialized || (num_in_view < gNumTabsInViewPref))
  363.         last_header = header;
  364.       header.removeAttribute('last-panel');
  365.       // only set if in view
  366.       if (!have_set_top && 
  367.           (!this.initialized || (header.getAttribute("in-view") == "true")))
  368.       {
  369.         header.setAttribute('top-panel','true');
  370.         have_set_top = 1;
  371.       } else {
  372.         header.removeAttribute('top-panel');
  373.       }
  374.       if (!have_set_after_selected && is_after_selected) {
  375.         header.setAttribute('first-panel-after-selected','true');
  376.         have_set_after_selected = 1
  377.       } else {
  378.         header.removeAttribute('first-panel-after-selected');
  379.       }
  380.       header.removeAttribute('hidden');
  381.       header.setAttribute("in-view", true);
  382.       num_in_view++;
  383.       
  384.       // (a) when we have hit the maximum number of tabs that can be in view and no tab 
  385.       //     has been selected yet
  386.       //     -or-
  387.       // (b) when we have reached the last tab we are about to display
  388.       if ( ((num_in_view == num_included) ||
  389.             (num_in_view == gNumTabsInViewPref)) &&
  390.           !is_after_selected )
  391.       {
  392.         selected_id = id;
  393.         this.node.setAttribute('last-selected-panel', id);
  394.       }
  395.  
  396.       // Pick sandboxed, or unsandboxed iframe
  397.       var iframe = panel.get_iframe();
  398.       var load_state;
  399.  
  400.       if (selected_id == id) {
  401.         is_after_selected = 1
  402.         debug("item("+ii/2+") selected");
  403.         header.setAttribute('selected', 'true');
  404.         content.removeAttribute('hidden');
  405.         content.removeAttribute('collapsed');
  406.  
  407.         if (sidebarObj.collapsed && panel.is_sandboxed()) {
  408.           if (!panel.is_persistent()) {
  409.             debug("    set src=about:blank");
  410.             iframe.setAttribute('src', 'about:blank');
  411.           }
  412.         } else {
  413.           var saved_src = iframe.getAttribute('content');
  414.           var src = iframe.getAttribute('src');
  415.           // either we have been requested to force_reload or the
  416.           // panel src has changed so we must restore the original src
  417.           if (force_reload || (saved_src != src)) {
  418.             debug("    set src="+saved_src);
  419.             iframe.setAttribute('src', saved_src);
  420.  
  421.             if (gTimeoutID != null)
  422.               clearTimeout(gTimeoutID);
  423.  
  424.             gCurFrame = iframe;
  425.             gTimeoutID = setTimeout (setBlank, 20000);
  426.           }
  427.         }
  428.  
  429.         load_state = content.getAttribute('loadstate');
  430.         if (load_state == 'stopped') {
  431.           load_state = 'never loaded';
  432.           toggleLoadarea(content);
  433.         }
  434.         if (load_state == 'never loaded') {
  435.           iframe.removeAttribute('hidden');
  436.           iframe.setAttribute('loadstate', 'loading');
  437.           iframe.addEventListener('load', panel_loader, true);
  438.         }
  439.       } else {
  440.         debug("item("+ii/2+")");
  441.         header.removeAttribute('selected');
  442.         content.setAttribute('collapsed','true');
  443.  
  444.         if (!panel.is_persistent()) {
  445.           iframe.setAttribute('src', 'about:blank');
  446.           load_state = content.getAttribute('loadstate');
  447.           if (load_state == 'loading') {
  448.             iframe.removeEventListener("load", panel_loader, true);
  449.             content.setAttribute('hidden','true');
  450.             iframe.setAttribute('loadstate', 'never loaded');
  451.           }
  452.         }
  453.         if (panel.is_sandboxed()) {
  454.           if (!panel.is_persistent())
  455.             iframe.setAttribute('src', 'about:blank');
  456.         }
  457.       }
  458.     }
  459.   }
  460.   if (last_header) {
  461.     last_header.setAttribute('last-panel','true');
  462.   }
  463.  
  464.   var no_panels_iframe = document.getElementById('sidebar-iframe-no-panels');
  465.   if (have_set_top) {
  466.       no_panels_iframe.setAttribute('hidden','true');
  467.       // The hide and show of 'sidebar-panels' should not be needed,
  468.       // but some old profiles may have this persisted as hidden (50973).
  469.       this.node.removeAttribute('hidden');
  470.   } else {
  471.       no_panels_iframe.removeAttribute('hidden');
  472.       this.node.setAttribute('hidden','true');
  473.   }
  474.  
  475.   this.initialized = true;
  476. }
  477.  
  478.  
  479. //////////////////////////////////////////////////////////////////////
  480. // sbPanel Class
  481. //
  482. // Like sbPanelList, this class is a wrapper around DOM representation
  483. // of individual panels in the sidebar. This UI event handlers and the
  484. // sidebar datasource observers call into this.  This class is
  485. // responsible for keeping the DOM state of the sidebar up to date.
  486. // This class does not make any changes to the sidebar rdf datasource.
  487. //////////////////////////////////////////////////////////////////////
  488.  
  489. function sbPanel(id, header, index)
  490. {
  491.   // This constructor should only be called by sbPanelList class.
  492.   // To create a panel instance, use the helper functions in sbPanelList:
  493.   //   sb_panel.get_panel_from_id(id)
  494.   //   sb_panel.get_panel_from_header_node(dom_node)
  495.   //   sb_panel.get_panel_from_header_index(index)
  496.   this.id = id;
  497.   this.header = header;
  498.   this.index = index;
  499.   this.parent = sidebarObj.panels;
  500. }
  501.  
  502. sbPanel.prototype.get_header =
  503. function ()
  504. {
  505.   return this.header;
  506. }
  507.  
  508. sbPanel.prototype.get_content =
  509. function ()
  510. {
  511.   return this.get_header().nextSibling;
  512. }
  513.  
  514. sbPanel.prototype.is_sandboxed =
  515. function ()
  516. {
  517.   if (typeof this.sandboxed == "undefined") {
  518.     var content = this.get_content();
  519.     var unsandboxed_iframe = content.childNodes.item(1);
  520.     this.sandboxed = !unsandboxed_iframe.getAttribute('content').match(/^chrome:/);
  521.   }
  522.   return this.sandboxed;
  523. }
  524.  
  525. sbPanel.prototype.get_iframe =
  526. function ()
  527. {
  528.   if (typeof this.iframe == "undefined") {
  529.     var content = this.get_content();
  530.     if (this.is_sandboxed()) {
  531.       var unsandboxed_iframe = content.childNodes.item(2);
  532.       this.iframe = unsandboxed_iframe;
  533.     } else {
  534.       var sandboxed_iframe = content.childNodes.item(1);
  535.       this.iframe = sandboxed_iframe;
  536.     }
  537.   }
  538.   return this.iframe;
  539. }
  540.  
  541. // This exclude function is used on panels and on the panel picker menu.
  542. // That is why it is hanging out in the global name space instead of
  543. // minding its own business in the class.
  544. function sb_panel_is_excluded(node)
  545. {
  546.   var exclude = node.getAttribute('exclude');
  547.   return ( exclude && exclude != '' &&
  548.            exclude.indexOf(sidebarObj.component) != -1 );
  549. }
  550. sbPanel.prototype.is_excluded =
  551. function ()
  552. {
  553.   return sb_panel_is_excluded(this.get_header());
  554. }
  555.  
  556. sbPanel.prototype.is_in_view =
  557. function()
  558. {
  559.   return (this.header.getAttribute("in-view") == "true");
  560. }
  561.  
  562. sbPanel.prototype.is_selected =
  563. function (panel_id)
  564. {
  565.   return 'true' == this.get_header().getAttribute('selected');
  566. }
  567.  
  568. sbPanel.prototype.is_persistent =
  569. function ()
  570. {
  571.     var rv = false;
  572.     var datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
  573.     var persistNode = datasource.GetTarget(RDF.GetResource(this.id),
  574.                                            RDF.GetResource(NC + "persist"),
  575.                                            true);
  576.     if (persistNode)
  577.     {
  578.         persistNode = 
  579.           persistNode.QueryInterface(Components.interfaces.nsIRDFLiteral);
  580.         rv = persistNode.Value == 'true';
  581.     }
  582.  
  583.     return rv;
  584. }
  585.  
  586. sbPanel.prototype.select =
  587. function (force_reload)
  588. {
  589.   this.parent.select(this, force_reload);
  590. }
  591.  
  592. sbPanel.prototype.stop_load =
  593. function ()
  594. {
  595.   var iframe = this.get_iframe();
  596.   var content = this.get_content();
  597.   var load_state = iframe.getAttribute('loadstate');
  598.   if (load_state == "loading") {
  599.     debug("Stop the presses");
  600.     iframe.removeEventListener("load", panel_loader, true);
  601.     content.setAttribute("loadstate", "stopped");
  602.     iframe.setAttribute('src', 'about:blank');
  603.     toggleLoadarea(content);
  604.   }
  605. }
  606.  
  607. function toggleLoadarea(content)
  608. {
  609.   // toggle between "loading" and "load stopped" in the UI
  610.   var widgetBox = content.firstChild.firstChild;
  611.   var widgetBoxKids = widgetBox.childNodes;
  612.   var stopButton = widgetBoxKids.item(3);
  613.   var reloadButton = widgetBoxKids.item(4);
  614.   var loadingImage = widgetBox.firstChild;
  615.   var loadingText = loadingImage.nextSibling;
  616.   var loadStoppedText = loadingText.nextSibling;
  617.  
  618.   // sanity check
  619.   if (stopButton.getAttribute("type") != "stop")
  620.   {
  621.     debug("Error: Expected button of type=\"stop\" but didn't get one!");
  622.     return;
  623.   }
  624.  
  625.   if (!stopButton.hidden)
  626.   {
  627.     // change button from "stop" to "reload"
  628.     stopButton.hidden = "true";
  629.     reloadButton.removeAttribute("hidden");
  630.  
  631.     // hide the loading image and set text to "load stopped"
  632.     loadingImage.hidden = "true";
  633.     loadingText.hidden = "true";
  634.     loadStoppedText.removeAttribute("hidden");
  635.   }
  636.   else
  637.   {
  638.     // change button from "reload" to "stop"
  639.     stopButton.removeAttribute("hidden");
  640.     reloadButton.hidden = "true";
  641.  
  642.     // show the loading image and set text to "loading"
  643.     loadingImage.removeAttribute("hidden");
  644.     loadingText.removeAttribute("hidden");
  645.     loadStoppedText.hidden = "true";
  646.   }
  647. }
  648.  
  649. sbPanel.prototype.exclude =
  650. function ()
  651. {
  652.   // Exclusion is handled by the datasource,
  653.   // but we need to make sure this panel is no longer selected.
  654.   this.get_header().removeAttribute('selected');
  655.   this.parent.exclude(this);
  656. }
  657.  
  658. sbPanel.prototype.reload =
  659. function ()
  660. {
  661.   if (!this.is_excluded()) {
  662.     this.select(true);
  663.   }
  664. }
  665.  
  666. //////////////////////////////////////////////////////////////////
  667. // Panels' RDF Datasource Observer
  668. //
  669. // This observer will ensure that the Sidebar UI stays current
  670. // when the datasource changes.
  671. // - When "refresh" is asserted, the sidebar refreshed.
  672. //   Currently this happens when a panel is included/excluded or
  673. //   added/removed (the later comes from the customize dialog).
  674. // - When "refresh_panel" is asserted, the targeted panel is reloaded.
  675. //   Currently this happens when the customize panel dialog is closed.
  676. //////////////////////////////////////////////////////////////////
  677. var panel_observer = {
  678.   onAssert : function(ds,src,prop,target) {
  679.     //debug ("observer: assert");
  680.     // "refresh" is asserted by select menu and by customize.js.
  681.     if (prop == RDF.GetResource(NC + "refresh")) {
  682.       sidebarObj.panels.initialized = false; // reset so panels are put in view
  683.       sidebarObj.panels.refresh();
  684.     } else if (prop == RDF.GetResource(NC + "refresh_panel")) {
  685.       var panel_id = target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
  686.       var panel = sidebarObj.panels.get_panel_from_id(panel_id);
  687.       panel.reload();
  688.     }
  689.   },
  690.   onUnassert : function(ds,src,prop,target) {
  691.     //debug ("observer: unassert");
  692.   },
  693.   onChange : function(ds,src,prop,old_target,new_target) {
  694.     //debug ("observer: change");
  695.   },
  696.   onMove : function(ds,old_src,new_src,prop,target) {
  697.     //debug ("observer: move");
  698.   },
  699.   onBeginUpdateBatch : function(ds) {
  700.     //debug ("observer: onBeginUpdateBatch");
  701.   },
  702.   onEndUpdateBatch : function(ds) {
  703.     //debug ("observer: onEndUpdateBatch");
  704.   }
  705. };
  706.  
  707. // Use an assertion to pass a "refresh" event to all the sidebars.
  708. // They use observers to watch for this assertion (see above).
  709. function refresh_all_sidebars() {
  710.   dump("refresh_all_sidebars()\n");
  711.   sidebarObj.datasource.Assert(RDF.GetResource(sidebarObj.resource),
  712.                                RDF.GetResource(NC + "refresh"),
  713.                                RDF.GetLiteral("true"),
  714.                                true);
  715.   sidebarObj.datasource.Unassert(RDF.GetResource(sidebarObj.resource),
  716.                                  RDF.GetResource(NC + "refresh"),
  717.                                  RDF.GetLiteral("true"));
  718. }
  719.  
  720. //////////////////////////////////////////////////////////////
  721. // Sidebar Init
  722. //////////////////////////////////////////////////////////////
  723. function sidebar_overlay_init() {
  724.   dump("sidebar_overlay_init()\n");
  725.   if (sidebar_is_collapsed() && !gAboutToUncollapse)
  726.     return;
  727.   gMustInit = false;
  728.   sidebarObj.panels = new sbPanelList('sidebar-panels');
  729.   sidebarObj.datasource_uri = get_sidebar_datasource_uri();
  730.   sidebarObj.resource = 'urn:sidebar:current-panel-list';
  731.  
  732.   sidebarObj.master_datasources = "";
  733.   sidebarObj.master_datasources = get_remote_datasource_url();
  734.   sidebarObj.master_datasources += " chrome://browser/content/sidebar/local-panels.rdf";
  735.   sidebarObj.master_resource = 'urn:sidebar:master-panel-list';
  736.   sidebarObj.component = document.firstChild.getAttribute('windowtype');
  737.   debug("sidebarObj.component is " + sidebarObj.component);
  738.  
  739.   // Initialize the display
  740.   var sidebar_element = document.getElementById('sidebar-box');
  741.   var sidebar_menuitem = document.getElementById('sidebar-menu');
  742.   if (sidebar_is_hidden()) {
  743.     if (sidebar_menuitem) {
  744.       sidebar_menuitem.setAttribute('checked', 'false');
  745.     }
  746.   } else {
  747.     if (sidebar_menuitem) {
  748.       sidebar_menuitem.setAttribute('checked', 'true');
  749.     }
  750.  
  751.     // for old profiles that don't persist the hidden attribute when splitter is not hidden.
  752.     var sidebar_splitter = document.getElementById('sidebar-splitter')
  753.     if (sidebar_splitter)
  754.       sidebar_splitter.setAttribute('hidden', 'false');
  755.     
  756.     if (sidebarObj.never_built) {
  757.       sidebarObj.never_built = false;
  758.       
  759.       debug("sidebar = " + sidebarObj);
  760.       debug("sidebarObj.resource = " + sidebarObj.resource);
  761.       debug("sidebarObj.datasource_uri = " + sidebarObj.datasource_uri);
  762.  
  763.       // Obtain the pref for limiting the number of tabs in view
  764.       try
  765.       {
  766.         var prefs = Components.classes["@mozilla.org/preferences-service;1"].
  767.                       getService(Components.interfaces.nsIPrefBranch);
  768.         gNumTabsInViewPref = prefs.getIntPref("sidebar.num_tabs_in_view");
  769.       }
  770.       catch (ex)
  771.       {
  772.         gNumTabsInViewPref = 8; // failover default
  773.       }
  774.  
  775.       // Show the header for the panels area. Use a splitter if there
  776.       // is stuff over the panels area.
  777.       var sidebar_panels_splitter = document.getElementById('sidebar-panels-splitter');
  778.       if (sidebar_element.firstChild != sidebar_panels_splitter) {
  779.         debug("Showing the panels splitter");
  780.         sidebar_panels_splitter.removeAttribute('hidden');
  781.       }
  782.     }
  783.     if (sidebar_is_collapsed()) {
  784.       sidebarObj.collapsed = true;
  785.     } else {
  786.       sidebarObj.collapsed = false;
  787.     }
  788.  
  789.     sidebar_open_default_panel(100, 0);
  790.   }
  791. }
  792.  
  793. function sidebar_overlay_destruct() {
  794.     var panels = document.getElementById('sidebar-panels');
  795.     debug("Removing observer from database.");
  796.     panels.database.RemoveObserver(panel_observer);
  797. }
  798.  
  799. var gBusyOpeningDefault = false;
  800.  
  801. function sidebar_open_default_panel(wait, tries) {
  802.   // check for making function reentrant
  803.   if (gBusyOpeningDefault)
  804.     return;
  805.   gBusyOpeningDefault = true;
  806.  
  807.   var ds = RDF.GetDataSource(sidebarObj.datasource_uri);
  808.   var currentListRes = RDF.GetResource("urn:sidebar:current-panel-list");
  809.   var panelListRes = RDF.GetResource("http://home.netscape.com/NC-rdf#panel-list");
  810.   var container = ds.GetTarget(currentListRes, panelListRes, true);
  811.   if (container) {
  812.     // Add the user's current panel choices to the template builder,
  813.     // which will aggregate it with the other datasources that describe
  814.     // the individual panel's title, customize URL, and content URL.
  815.     var panels = document.getElementById('sidebar-panels');
  816.     panels.database.AddDataSource(ds);
  817.     
  818.     debug("Adding observer to database.");
  819.     panels.database.AddObserver(panel_observer);
  820.  
  821.     // XXX This is a hack to force re-display
  822.     panels.builder.rebuild();
  823.   } else {
  824.     if (tries < 3) {
  825.       // No children yet, try again later
  826.       setTimeout('sidebar_open_default_panel(' + (wait*2) + ',' + (tries+1) + ')',wait);
  827.       gBusyOpeningDefault = false;
  828.       return;
  829.     } else {
  830.       sidebar_fixup_datasource();
  831.     }
  832.   }
  833.  
  834.   sidebarObj.panels.refresh();
  835.   gBusyOpeningDefault = false;
  836.   if (gCheckMissingPanels)
  837.     check_for_missing_panels();
  838. }
  839.  
  840. function SidebarRebuild() {
  841.   dump("SidebarRebuild()\n");
  842.   sidebarObj.panels.initialized = false; // reset so panels are brought in view
  843.   var panels = document.getElementById("sidebar-panels");
  844.   panels.builder.rebuild();
  845.   sidebar_open_default_panel(100, 0);
  846. }
  847.  
  848. const NS_ERROR_FILE_NOT_FOUND = 0x80520012;
  849.  
  850. function check_for_missing_panels() {
  851.   dump("check_for_missing_panels()\n");
  852.   var tabs = sidebarObj.panels.node.childNodes;
  853.   var currHeader;
  854.   var currTab;
  855.   for (var i = 2; i < tabs.length; i += 2) {
  856.     currHeader = tabs[i];
  857.     currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i);
  858.     if (!currTab.is_excluded()) {
  859.       if (currHeader.hasAttribute("prereq") && currHeader.getAttribute("prereq") != "") {
  860.         var prereq_file = currHeader.getAttribute("prereq");
  861.         var ioService = Components.classes["@mozilla.org/network/io-service;1"]
  862.                                   .getService(Components.interfaces.nsIIOService);      
  863.         var uri = ioService.newURI(prereq_file, null, null);
  864.         var channel = ioService.newChannelFromURI(uri);
  865.         try {
  866.           channel.open();
  867.         }
  868.         catch(ex if (ex.result == NS_ERROR_FILE_NOT_FOUND)) {
  869.           sidebarObj.datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
  870.           sidebarObj.datasource.Assert(RDF.GetResource(currHeader.getAttribute("id")),
  871.                                        RDF.GetResource(NC + "exclude"),
  872.                                        RDF.GetLiteral(sidebarObj.component),
  873.                                        true);
  874.           currTab.exclude();
  875.         }        
  876.       }
  877.     }
  878.   }
  879.   gCheckMissingPanels = false;
  880. }
  881.  
  882. //////////////////////////////////////////////////////////////
  883. // Sidebar File and Datasource functions
  884. //////////////////////////////////////////////////////////////
  885.  
  886. function sidebar_get_panels_file() {
  887.   dump("sidebar_get_panels_file()\n");
  888.   try {
  889.     var locator_service = Components.classes["@mozilla.org/file/directory_service;1"].getService();
  890.     if (locator_service)
  891.       locator_service = locator_service.QueryInterface(Components.interfaces.nsIProperties);
  892.     // Use the fileLocator to look in the profile directory to find
  893.     // 'panels.rdf', which is the database of the user's currently
  894.     // selected panels.
  895.     // If <profile>/panels.rdf doesn't exist, GetFileLocation() will copy
  896.     // bin/defaults/profile/panels.rdf to <profile>/panels.rdf
  897.     var sidebar_file = locator_service.get(PANELS_RDF_FILE, Components.interfaces.nsIFile);
  898.     if (!sidebar_file.exists()) {
  899.       // This should not happen, as GetFileLocation() should copy
  900.       // defaults/panels.rdf to the users profile directory
  901.       debug("Sidebar panels file does not exist");
  902.       throw("Panels file does not exist");
  903.     }
  904.     return sidebar_file;
  905.   } catch (ex) {
  906.     // This should not happen
  907.     debug("Error: Unable to grab panels file.\n");
  908.     throw(ex);
  909.   }
  910.   return null;
  911. }
  912.  
  913. function sidebar_revert_to_default_panels() {
  914.   dump("sidebar_revert_to_default_panels()\n");
  915.   try {
  916.     var sidebar_file = sidebar_get_panels_file();
  917.  
  918.     sidebar_file.remove(false);
  919.  
  920.     // Since we just removed the panels file,
  921.     // this should copy the defaults over.
  922.     sidebar_file = sidebar_get_panels_file();
  923.  
  924.     debug("sidebar defaults reloaded");
  925.     var datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
  926.     datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Refresh(true);
  927.   } catch (ex) {
  928.     debug("Error: Unable to reload panel defaults file.\n");
  929.   }
  930.   return null;
  931. }
  932.  
  933. function get_sidebar_datasource_uri() {
  934.   dump("get_sidebar_datasource_uri()\n");
  935.   try {
  936.     var sidebar_file = sidebar_get_panels_file();
  937.     
  938.     var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  939.     var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
  940.     
  941.     return fileHandler.getURLSpecFromFile(sidebar_file);
  942.   } catch (ex) {
  943.     // This should not happen
  944.     debug("Error: Unable to load panels file.\n");
  945.   }
  946.   return null;
  947. }
  948.  
  949. // Get the template for the available panels url from preferences.
  950. // Replace variables in the url:
  951. //     %LOCALE%  -->  Application locale (e.g. en-us).
  952. //     %VERSION% --> Sidebar file format version (e.g. 0.0).
  953. function get_remote_datasource_url() {
  954.   dump("get_remote_datasource_url()\n");
  955.   var url = '';
  956.   var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  957.                         .getService(Components.interfaces.nsIPrefBranch);
  958.   var locale;
  959.   try {
  960.     if (prefs.getPrefType("sidebar.customize.all_panels.url") == prefs.PREF_STRING) {
  961.         url = prefs.getCharPref("sidebar.customize.all_panels.url");
  962.         url = url.replace(/%SIDEBAR_VERSION%/g, SIDEBAR_VERSION);
  963.     }
  964.   } catch(ex) {
  965.     debug("Unable to get remote url pref. What now? "+ex);
  966.   }
  967.   try {
  968.     locale = prefs.getComplexValue("intl.content.langcode",
  969.                                    Components.interfaces.nsIPrefLocalizedString);
  970.   } catch(ex) {
  971.     try {
  972.       debug("No lang code pref, intl.content.langcode.");
  973.       debug("Use locale from user agent string instead");
  974.  
  975.       locale = prefs.getComplexValue("general.useragent.locale",
  976.                                      Components.interfaces.nsIPrefLocalizedString);
  977.   } catch(ex) {
  978.       debug("Unable to get system locale. What now? "+ex);
  979.     }
  980.   }
  981.   locale = locale.data.toLowerCase();
  982.   url = url.replace(/%LOCALE%/g, locale);
  983.  
  984.   debug("Remote url is " + url);
  985.   return url;
  986. }
  987.  
  988. function sidebar_fixup_datasource() {
  989.   dump("sidebar_fixup_datasource()\n");
  990.   var datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
  991.   var resource = RDF.GetResource(sidebarObj.resource);
  992.  
  993.   var panel_list = datasource.GetTarget(resource,
  994.                                         RDF.GetResource(NC+"panel-list"),
  995.                                         true);
  996.   if (!panel_list) {
  997.     debug("Sidebar datasource is an old format or busted\n");
  998.     sidebar_revert_to_default_panels();
  999.   } else {
  1000.     // The datasource is ok, but it just has no panels.
  1001.     // sidebar_refresh() will display some helper content.
  1002.     // Do nothing here.
  1003.   }
  1004. }
  1005.  
  1006. //////////////////////////////////////////////////////////////
  1007. // Sidebar Interface for XUL
  1008. //////////////////////////////////////////////////////////////
  1009.  
  1010. // Change the sidebar content to the selected panel.
  1011. // Called when a panel title is clicked.
  1012. function SidebarSelectPanel(header, should_popopen, should_unhide) {
  1013.   debug("SidebarSelectPanel("+header+","+should_popopen+","+should_unhide+")");
  1014.   var panel = sidebarObj.panels.get_panel_from_header_node(header);
  1015.  
  1016.   if (!panel) {
  1017.     return false;
  1018.   }
  1019.  
  1020.   var popopen = false;
  1021.   var unhide = false;
  1022.  
  1023.   if (panel.is_excluded()) {
  1024.     return false;
  1025.   }
  1026.   if (sidebar_is_hidden()) {
  1027.     if (should_unhide) {
  1028.       unhide = true;
  1029.     } else {
  1030.       return false;
  1031.     }
  1032.   }
  1033.   if (sidebar_is_collapsed()) {
  1034.     if (should_popopen) {
  1035.       popopen = true;
  1036.     } else {
  1037.       return false;
  1038.     }
  1039.   }
  1040.   if (unhide)  SidebarShowHide();
  1041.   if (popopen) SidebarExpandCollapse();
  1042.  
  1043.   try { 
  1044.     panel.get_iframe().setAttribute('focusOnLoad', true); 
  1045.   } catch (ex) { 
  1046.     // ignore exception for cases where content isn't built yet
  1047.     // e.g., auto opening search tab: we don't want to focus search field
  1048.   }
  1049.   if (!panel.is_selected()) panel.select(false);
  1050.  
  1051.   return true;
  1052. }
  1053.  
  1054. function SidebarGetLastSelectedPanel()
  1055. {
  1056.   return (sidebarObj.panels && 
  1057.           sidebarObj.panels.node.getAttribute('last-selected-panel'));
  1058. }
  1059.  
  1060. function SidebarGetRelativePanel(direction)
  1061. {
  1062.   // direction == 1 to view next panel, -1 to view prev panel
  1063.  
  1064.   if (sidebar_is_hidden())
  1065.     SidebarShowHide();
  1066.   if (sidebar_is_collapsed())
  1067.     SidebarExpandCollapse();
  1068.  
  1069.   var currentPanel = sidebarObj.panels.get_panel_from_id(SidebarGetLastSelectedPanel());
  1070.   if (!currentPanel) {
  1071.     sidebarObj.panels.select_default_panel();
  1072.     return;
  1073.   }
  1074.  
  1075.   var newPanel = currentPanel;
  1076.  
  1077.   do {
  1078.     var newPanelIndex = newPanel.index + (direction * 2);
  1079.     if (newPanelIndex < 2 || newPanelIndex >= sidebarObj.panels.node.childNodes.length)
  1080.       newPanel = (direction == 1)? sidebarObj.panels.find_first(): sidebarObj.panels.find_last();
  1081.     else 
  1082.       newPanel = sidebarObj.panels.get_panel_from_header_index(newPanelIndex);
  1083.  
  1084.     if (!newPanel)
  1085.       break;
  1086.  
  1087.     if (!newPanel.is_excluded()) {
  1088.       SidebarSelectPanel(newPanel.header, true, true);  // found a panel that's not excluded to select -- do it
  1089.       break;
  1090.     }
  1091.   } while (newPanel != currentPanel);  // keep looking for a panel, but don't loop infinitely
  1092. }
  1093.  
  1094. function SidebarStopPanelLoad(header) {
  1095.   var panel = sidebarObj.panels.get_panel_from_header_node(header);
  1096.   panel.stop_load();
  1097. }
  1098.  
  1099. function SidebarReloadPanel(header) {
  1100.   var panel = sidebarObj.panels.get_panel_from_header_node(header);
  1101.   panel.reload();
  1102. }
  1103.  
  1104. // No one is calling this right now.
  1105. function SidebarReload() {
  1106.   sidebarObj.panels.refresh();
  1107. }
  1108.  
  1109. // Set up a lame hack to avoid opening two customize
  1110. // windows on a double click.
  1111. var gDisableCustomize = false;
  1112. function enable_customize() {
  1113.   gDisableCustomize = false;
  1114. }
  1115.  
  1116. // Bring up the Sidebar customize dialog.
  1117. function SidebarCustomize() {
  1118.   // Use a single sidebar customize dialog
  1119.   var cwindowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
  1120.   var iwindowManager = Components.interfaces.nsIWindowMediator;
  1121.   var windowManager  = cwindowManager.QueryInterface(iwindowManager);
  1122.  
  1123.   var customizeWindow = windowManager.getMostRecentWindow('sidebar:customize');
  1124.  
  1125.   if (customizeWindow) {
  1126.     debug("Reuse existing customize dialog");
  1127.     customizeWindow.focus();
  1128.   } else {
  1129.     debug("Open a new customize dialog");
  1130.  
  1131.     if (false == gDisableCustomize) {
  1132.       debug("First time creating customize dialog");
  1133.       gDisableCustomize = true;
  1134.  
  1135.       var panels = document.getElementById('sidebar-panels');
  1136.  
  1137.       customizeWindow = window.openDialog(
  1138.                          'chrome://browser/content/sidebar/customize.xul',
  1139.                          '_blank','centerscreen,chrome,resizable,dialog=no,dependent',
  1140.                          sidebarObj.master_datasources,
  1141.                          sidebarObj.master_resource,
  1142.                          sidebarObj.datasource_uri,
  1143.                          sidebarObj.resource);
  1144.       setTimeout(enable_customize, 2000);
  1145.     }
  1146.   }
  1147. }
  1148.  
  1149. function BrowseMorePanels()
  1150. {
  1151.   var url = '';
  1152.   var browser_url = "chrome://browser/content/browser.xul";
  1153.   var locale;
  1154.   try {
  1155.     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  1156.                           .getService(Components.interfaces.nsIPrefBranch);
  1157.     url = prefs.getCharPref("sidebar.customize.directory.url");
  1158.     var temp = prefs.getCharPref("browser.chromeURL");
  1159.     if (temp) browser_url = temp;
  1160.   } catch(ex) {
  1161.     debug("Unable to get prefs: "+ex);
  1162.   }
  1163.   window.openDialog(browser_url, "_blank", "chrome,all,dialog=no", url);
  1164. }
  1165.  
  1166.  
  1167.  
  1168. function sidebar_is_collapsed() {
  1169.   var sidebar_splitter = document.getElementById('sidebar-splitter');
  1170.   return (sidebar_splitter &&
  1171.           sidebar_splitter.getAttribute('state') == 'collapsed');
  1172. }
  1173.  
  1174. function SidebarExpandCollapse() {
  1175.   var sidebar_splitter = document.getElementById('sidebar-splitter');
  1176.   var sidebar_box = document.getElementById('sidebar-box');
  1177.   if (sidebar_splitter.getAttribute('state') == 'collapsed') {
  1178.     if (gMustInit)
  1179.       sidebar_overlay_init();
  1180.     debug("Expanding the sidebar");
  1181.     sidebar_splitter.removeAttribute('state');
  1182.     sidebar_box.removeAttribute('collapsed');
  1183.     SidebarSetButtonOpen(true);
  1184.   } else {
  1185.     debug("Collapsing the sidebar");
  1186.     sidebar_splitter.setAttribute('state', 'collapsed');
  1187.     sidebar_box.setAttribute('collapsed', 'true');
  1188.     SidebarSetButtonOpen(false);
  1189.   }
  1190. }
  1191.  
  1192. // sidebar_is_hidden() - Helper function for SidebarShowHide().
  1193. function sidebar_is_hidden() {
  1194.   var sidebar_title = document.getElementById('sidebar-title-box');
  1195.   var sidebar_box = document.getElementById('sidebar-box');
  1196.   return sidebar_box.getAttribute('hidden') == 'true'
  1197.          || sidebar_title.getAttribute('hidden') == 'true';
  1198. }
  1199.  
  1200. // Show/Hide the entire sidebar.
  1201. // Invoked by the "View / Sidebar" menu option.
  1202. function SidebarShowHide() {
  1203.   var sidebar_box = document.getElementById('sidebar-box');
  1204.   var title_box = document.getElementById('sidebar-title-box');
  1205.   var sidebar_panels_splitter = document.getElementById('sidebar-panels-splitter');
  1206.   var sidebar_panels_splitter_box = document.getElementById('sidebar-panels-splitter-box');
  1207.   var sidebar_splitter = document.getElementById('sidebar-splitter');
  1208.   var sidebar_menu_item = document.getElementById('sidebar-menu');
  1209.   var tabs_menu = document.getElementById('sidebar-panel-picker');
  1210.  
  1211.   if (sidebar_is_hidden()) {
  1212.     debug("Showing the sidebar");
  1213.  
  1214.     // for older profiles: 
  1215.     sidebar_box.setAttribute('hidden', 'false'); 
  1216.     sidebar_panels_splitter_box.setAttribute('hidden', 'false'); 
  1217.  
  1218.     sidebar_box.removeAttribute('collapsed');
  1219.     if (sidebar_splitter.getAttribute('state') == 'collapsed')
  1220.       sidebar_splitter.removeAttribute('state');
  1221.     title_box.removeAttribute('hidden');
  1222.     sidebar_panels_splitter_box.removeAttribute('collapsed');
  1223.     sidebar_splitter.setAttribute('hidden', 'false');
  1224.     if (sidebar_box.firstChild != sidebar_panels_splitter) {
  1225.       debug("Showing the panels splitter");
  1226.       sidebar_panels_splitter.removeAttribute('hidden');
  1227.       if (sidebar_panels_splitter.getAttribute('state') == 'collapsed')
  1228.         sidebar_panels_splitter.removeAttribute('state');
  1229.     }
  1230.     sidebar_overlay_init();
  1231.     sidebar_menu_item.setAttribute('checked', 'true');
  1232.     tabs_menu.removeAttribute('hidden');
  1233.     SidebarSetButtonOpen(true);
  1234.   } else {
  1235.     debug("Hiding the sidebar");
  1236.     var hide_everything = sidebar_panels_splitter.getAttribute('hidden') == 'true';
  1237.     if (hide_everything) {
  1238.       debug("Hide everything");
  1239.       sidebar_box.setAttribute('collapsed', 'true');
  1240.       sidebar_splitter.setAttribute('hidden', 'true');
  1241.     } else {
  1242.       sidebar_panels_splitter.setAttribute('hidden', 'true');
  1243.     }
  1244.     title_box.setAttribute('hidden', 'true');
  1245.     sidebar_panels_splitter_box.setAttribute('collapsed', 'true');
  1246.     sidebar_menu_item.setAttribute('checked', 'false');
  1247.     tabs_menu.setAttribute('hidden', 'true');
  1248.     SidebarSetButtonOpen(false);
  1249.   }
  1250.   // Immediately save persistent values
  1251.   document.persist('sidebar-title-box', 'hidden');
  1252.   persist_width();
  1253.   window._content.focus();
  1254. }
  1255.  
  1256. function SidebarBuildPickerPopup() {
  1257.   var menu = document.getElementById('sidebar-panel-picker-popup');
  1258.   menu.database.AddDataSource(RDF.GetDataSource(sidebarObj.datasource_uri));
  1259.   menu.builder.rebuild();
  1260.  
  1261.   for (var ii=3; ii < menu.childNodes.length; ii++) {
  1262.     var panel_menuitem = menu.childNodes.item(ii);
  1263.     if (sb_panel_is_excluded(panel_menuitem)) {
  1264.       debug(ii+": "+panel_menuitem.getAttribute('label')+ ": excluded; uncheck.");
  1265.       panel_menuitem.removeAttribute('checked');
  1266.     } else {
  1267.       debug(ii+": "+panel_menuitem.getAttribute('label')+ ": included; check.");
  1268.       panel_menuitem.setAttribute('checked', 'true');
  1269.     }
  1270.   }
  1271. }
  1272.  
  1273. function SidebarTogglePanel(panel_menuitem) {
  1274.   // Create a "container" wrapper around the current panels to
  1275.   // manipulate the RDF:Seq more easily.
  1276.   dump("SidebarTogglePanel - panel_menuitem= " + panel_menuitem + "\n");
  1277.   sidebarObj.datasource = RDF.GetDataSource(sidebarObj.datasource_uri);
  1278.  
  1279.   var did_exclude = false;
  1280.   var panel_id = panel_menuitem.getAttribute('id');
  1281.   var panel = sidebarObj.panels.get_panel_from_id(panel_id);
  1282.   var panel_exclude = panel_menuitem.getAttribute('exclude')
  1283.   if (panel_exclude == '') {
  1284.     // Nothing excluded for this panel yet, so add this component to the list.
  1285.     debug("Excluding " + panel_id + " from " + sidebarObj.component);
  1286.     sidebarObj.datasource.Assert(RDF.GetResource(panel_id),
  1287.                                 RDF.GetResource(NC + "exclude"),
  1288.                                 RDF.GetLiteral(sidebarObj.component),
  1289.                                 true);
  1290.     panel.exclude();
  1291.     did_exclude = true;
  1292.   } else {
  1293.     // Panel has an exclude string, but it may or may not have the
  1294.     // current component listed in the string.
  1295.     debug("Current exclude string: " + panel_exclude);
  1296.     var new_exclude = panel_exclude;
  1297.     if (sb_panel_is_excluded(panel_menuitem)) {
  1298.       debug("Plucking this component out of the exclude list");
  1299.       replace_pat = new RegExp(sidebarObj.component + "\s*");
  1300.       new_exclude = new_exclude.replace(replace_pat,'');
  1301.       new_exclude = new_exclude.replace(/^\s+/,'');
  1302.       // did_exclude remains false
  1303.     } else {
  1304.       debug("Adding this component to the exclude list");
  1305.       new_exclude = new_exclude + " " + sidebarObj.component;
  1306.       panel.exclude();
  1307.       did_exclude = true;
  1308.     }
  1309.     if (new_exclude == '') {
  1310.       debug("Removing exclude list");
  1311.       sidebarObj.datasource.Unassert(RDF.GetResource(panel_id),
  1312.                                      RDF.GetResource(NC + "exclude"),
  1313.                                      RDF.GetLiteral(sidebarObj.component));
  1314.     } else {
  1315.       debug("New exclude string: " + new_exclude);
  1316.       exclude_target =
  1317.         sidebarObj.datasource.GetTarget(RDF.GetResource(panel_id),
  1318.                                         RDF.GetResource(NC + "exclude"),
  1319.                                         true);
  1320.       sidebarObj.datasource.Change(RDF.GetResource(panel_id),
  1321.                                    RDF.GetResource(NC + "exclude"),
  1322.                                    exclude_target,
  1323.                                    RDF.GetLiteral(new_exclude));
  1324.     }
  1325.   }
  1326.  
  1327.   if (did_exclude)
  1328.   {
  1329.     // if we excluded a tab in view then add another one
  1330.     var tabs = sidebarObj.panels.node.childNodes;
  1331.     if (panel.is_in_view())
  1332.     {
  1333.       // we excluded one so let's try to bring a non-excluded one into view
  1334.       var newFirst = null;
  1335.       var added = false;
  1336.       for (var i = 2; i < tabs.length ; i += 2)
  1337.       {
  1338.         var currTab = sidebarObj.panels.get_panel_from_header_index(i);
  1339.         var hasPotential = !currTab.is_excluded() && !currTab.is_in_view();
  1340.  
  1341.         // set potential new first tab in case we can't find one after the
  1342.         // tab that was just excluded
  1343.         if (!newFirst && hasPotential)
  1344.           newFirst = currTab;
  1345.  
  1346.         if (i > panel.index && hasPotential)
  1347.         {
  1348.           currTab.header.setAttribute("in-view", true);
  1349.           added = true;
  1350.           break;
  1351.         }
  1352.       }
  1353.       if (!added && newFirst)
  1354.         newFirst.header.setAttribute("in-view", true);
  1355.  
  1356.       // lose it from current view
  1357.       panel.header.setAttribute("in-view", false);
  1358.     }
  1359.   }
  1360.   else
  1361.   {
  1362.     panel.header.setAttribute("in-view", true);
  1363.  
  1364.     // if we have one too many tabs we better get rid of an old one
  1365.     if (sidebarObj.panels.num_panels_in_view() > gNumTabsInViewPref)
  1366.     {
  1367.       // we included a new tab so let's take the last one out of view
  1368.       for (i = 2; i < tabs.length; i += 2)
  1369.       {
  1370.         var currHeader = tabs[i];
  1371.         if (currHeader.hasAttribute("last-panel"))
  1372.           currHeader.setAttribute("in-view", false);
  1373.       }
  1374.     }
  1375.  
  1376.     panel.select(false);
  1377.   }
  1378.  
  1379.   if (did_exclude && !sidebarObj.panels.visible_panels_exist())
  1380.     // surrender focus to main content area
  1381.     window._content.focus();
  1382.   else
  1383.     // force all the sidebars to update
  1384.     refresh_all_sidebars();
  1385.  
  1386.   // Write the modified panels out.
  1387.   sidebarObj.datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush();
  1388. }
  1389.  
  1390. function SidebarNavigate(aDirection)
  1391. {
  1392.   debug("SidebarNavigate " + aDirection);
  1393.  
  1394.   var tabs = sidebarObj.panels.node.childNodes;
  1395.   var i;
  1396.   var currHeader;
  1397.   var currTab;
  1398.   // move forward a tab (down in the template)
  1399.   if (aDirection > 0)
  1400.   {
  1401.     // ensure we have a tab below the last one
  1402.     var foundLast = false; 
  1403.     var oldFirst = null;
  1404.     for (i = 2; i < tabs.length; i += 2) 
  1405.     {
  1406.       currHeader = tabs[i];
  1407.       currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i);
  1408.   
  1409.       if (!currTab.is_excluded())
  1410.       {     
  1411.         if (foundLast)
  1412.         {
  1413.           debug("toggling old first and new last");
  1414.           debug("new last:  " + currHeader.getAttribute("id"));
  1415.           debug("old first: " + oldFirst.getAttribute("id"));
  1416.           currHeader.setAttribute("in-view", true);
  1417.           oldFirst.setAttribute("in-view", false);
  1418.           
  1419.           // if old first was selected select new first instead
  1420.           if (oldFirst.getAttribute("id") == 
  1421.               sidebarObj.panels.node.getAttribute("last-selected-panel"))
  1422.           {
  1423.             sidebarObj.panels.node.setAttribute('last-selected-panel', 
  1424.               currTab.id);
  1425.           }
  1426.           
  1427.           break;
  1428.         }
  1429.  
  1430.         if (!foundLast && currHeader.hasAttribute("last-panel"))
  1431.         {
  1432.           debug("found last");
  1433.           foundLast = true;
  1434.         }
  1435.  
  1436.         // set the old first in case we find a new last below
  1437.         // the old last and need to toggle the new first's ``in-view''
  1438.         if (!oldFirst && currTab.is_in_view())
  1439.           oldFirst = currHeader;
  1440.       }
  1441.     }
  1442.   }
  1443.   
  1444.   // move back a tab (up in the template)
  1445.   else if (aDirection < 0)
  1446.   {
  1447.     var newFirst = null, newLast = null;
  1448.     var foundFirst = false;
  1449.     for (i = 2; i < tabs.length; i += 2)
  1450.     {
  1451.       currHeader = tabs[i];
  1452.       currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i);
  1453.  
  1454.       if (!currTab.is_excluded())
  1455.       {
  1456.         if (!foundFirst && currHeader.hasAttribute("top-panel"))
  1457.         {
  1458.           debug("found first");
  1459.           foundFirst = true;
  1460.         }
  1461.         if (!foundFirst)
  1462.         {
  1463.           debug("setting newFirst");
  1464.           newFirst = currHeader;
  1465.         }
  1466.  
  1467.         if (currHeader.hasAttribute("last-panel"))
  1468.         {
  1469.           debug("found last");
  1470.  
  1471.           // ensure we have a tab above the first one
  1472.           if (newFirst)
  1473.           {
  1474.             debug("toggling new first and old last");
  1475.             debug("new first: " + newFirst.getAttribute("id"));
  1476.             debug("old last:  " + currHeader.getAttribute("id"));
  1477.  
  1478.             newFirst.setAttribute("in-view", true);
  1479.             currHeader.setAttribute("in-view", false); // hide old last
  1480.           
  1481.             // if old last was selected, now select one above it
  1482.             if (sidebarObj.panels.node.getAttribute("last-selected-panel") ==
  1483.                 currTab.id)
  1484.             {
  1485.               sidebarObj.panels.node.setAttribute("last-selected-panel", 
  1486.                 newLast.getAttribute("id"));
  1487.             }
  1488.  
  1489.             break;
  1490.           }
  1491.         }
  1492.         if (currTab.is_in_view())
  1493.           newLast = currHeader;
  1494.       }
  1495.     }
  1496.   }
  1497.  
  1498.   if (aDirection)
  1499.     sidebarObj.panels.update(false);
  1500. }
  1501.  
  1502. //////////////////////////////// SEARCH ////////////////////////////////////
  1503.  
  1504. function UpdateInternetSearchResults(event)
  1505. {
  1506.   dump('UpdateInternetSearchResults()\n');
  1507.   var url = getWebNavigation().currentURI.spec;
  1508.   if (url) {
  1509.    // try {
  1510.       var autoOpenSearchPanel = 
  1511.         gPrefService.getBoolPref("browser.search.opensidebarsearchpanel");
  1512.  
  1513.       if (autoOpenSearchPanel || isSearchPanelOpen())
  1514.       {
  1515.         if (!gSearchService)
  1516.           gSearchService = Components.classes["@mozilla.org/rdf/datasource;1?name=internetsearch"]
  1517.                                          .getService(Components.interfaces.nsIInternetSearchService);
  1518.  
  1519.         var searchInProgressFlag = gSearchService.FindInternetSearchResults(url);
  1520.  
  1521.         if (searchInProgressFlag) {
  1522.           if (autoOpenSearchPanel)
  1523.             RevealSearchPanel();
  1524.         }
  1525.       }
  1526. /*
  1527.     } catch (ex) {
  1528.       dump('Exception in UpdateInternetSearchResults()\n');
  1529.     }
  1530. */
  1531.   }
  1532. }
  1533.  
  1534. function RevealSearchPanel()
  1535. {
  1536.   dump('RevealSearchPanel()\n');
  1537.   var searchPanel = document.getElementById("urn:sidebar:panel:search");
  1538.   if (searchPanel)
  1539.     SidebarSelectPanel(searchPanel, true, true); // lives in sidebarOverlay.js
  1540. }
  1541.  
  1542. function isSearchPanelOpen()
  1543. {
  1544.   dump('isSearchPanelOpen()\n');
  1545.   return ( !sidebar_is_hidden()    && 
  1546.            !sidebar_is_collapsed() && 
  1547.            SidebarGetLastSelectedPanel() == "urn:sidebar:panel:search"
  1548.          );
  1549. }
  1550.  
  1551. //////////////////////////////////////////////////////////////
  1552. // Sidebar Hacks and Work-arounds
  1553. //////////////////////////////////////////////////////////////
  1554.  
  1555. // SidebarCleanUpExpandCollapse() - Respond to grippy click.
  1556. function SidebarCleanUpExpandCollapse() {
  1557.   // XXX Mini hack. Persist isn't working too well. Force the persist,
  1558.   // but wait until the change has commited.
  1559.   if (gMustInit) {
  1560.     gAboutToUncollapse = true;
  1561.     sidebar_overlay_init();
  1562.   }
  1563.  
  1564.   setTimeout("document.persist('sidebar-box', 'collapsed');",100);
  1565.   setTimeout("sidebarObj.panels.refresh();",100);
  1566. }
  1567.  
  1568. function PersistHeight() {
  1569.   // XXX Mini hack. Persist isn't working too well. Force the persist,
  1570.   // but wait until the last drag has been committed.
  1571.   // May want to do something smarter here like only force it if the
  1572.   // height has really changed.
  1573.   setTimeout("document.persist('sidebar-panels-splitter-box','height');",100);
  1574. }
  1575.  
  1576. function persist_width() {
  1577.   // XXX Mini hack. Persist isn't working too well. Force the persist,
  1578.   // but wait until the width change has commited.
  1579.   setTimeout("document.persist('sidebar-box', 'width');",100);
  1580.  
  1581.   var is_collapsed = document.getElementById('sidebar-box').
  1582.                        getAttribute('collapsed') == 'true';
  1583.   SidebarSetButtonOpen(!is_collapsed);
  1584. }
  1585.  
  1586. function SidebarFinishClick() {
  1587.  
  1588.   // XXX Semi-hack for bug #16516.
  1589.   // If we had the proper drag event listener, we would not need this
  1590.   // timeout. The timeout makes sure the width is written to disk after
  1591.   // the sidebar-box gets the newly dragged width.
  1592.   setTimeout("persist_width()",100);
  1593.  
  1594.   var is_collapsed = document.getElementById('sidebar-box').getAttribute('collapsed') == 'true';
  1595.   debug("collapsed: " + is_collapsed);
  1596.   if (is_collapsed != sidebarObj.collapsed) {
  1597.     if (gMustInit)
  1598.       sidebar_overlay_init();
  1599.   }
  1600. }
  1601.  
  1602. function SidebarSetButtonOpen(aSidebarNowOpen)
  1603. {
  1604.   dump("SidebarSetButtonOpen("+aSidebarNowOpen+");\n");
  1605.   // change state so toolbar icon can be updated
  1606.   var tb = document.getElementById("content");
  1607.   // Don't know why, but this intermediary step seems to be required... !
  1608.   var tabs = document.getAnonymousElementByAttribute(tb, 'class', 'tabbrowser-tabs');
  1609.   var sb = document.getAnonymousElementByAttribute(tabs,'anonid','sidebar-button');
  1610.   if (sb) {
  1611.     sb.setAttribute("sidebaropen", aSidebarNowOpen);
  1612.  
  1613.     // set tooltip for toolbar icon
  1614.     var header = document.getElementById("sidebar-title-box");
  1615.     var tooltip = header.getAttribute(aSidebarNowOpen ? 
  1616.                   "tooltipclose" : "tooltipopen");
  1617.     sb.setAttribute("tooltiptext", tooltip);
  1618.   }
  1619. }
  1620.  
  1621. function SidebarInitContextMenu(aMenu, aPopupNode)
  1622. {
  1623.   var panel = sidebarObj.panels.get_panel_from_header_node(aPopupNode);
  1624.   var switchItem = document.getElementById("switch-ctx-item");
  1625.   var reloadItem = document.getElementById("reload-ctx-item");
  1626.   var stopItem = document.getElementById("stop-ctx-item");
  1627.  
  1628.   // the current panel can be reloaded, but other panels are not showing 
  1629.   // any content, so we only allow you to switch to other panels
  1630.   if (panel.is_selected()) 
  1631.   {
  1632.     switchItem.setAttribute("collapsed", "true"); 
  1633.     reloadItem.removeAttribute("disabled");
  1634.   }
  1635.   else 
  1636.   {
  1637.     switchItem.removeAttribute("collapsed");
  1638.     reloadItem.setAttribute("disabled", "true");
  1639.   }
  1640.  
  1641.   // only if a panel is currently loading enable the ``Stop'' item
  1642.   if (panel.get_iframe().getAttribute("loadstate") == "loading")
  1643.     stopItem.removeAttribute("disabled");
  1644.   else
  1645.     stopItem.setAttribute("disabled", "true");
  1646. }
  1647.  
  1648. ///////////////////////////////////////////////////////////////
  1649. // Handy Debug Tools
  1650. //////////////////////////////////////////////////////////////
  1651. var debug = null;
  1652. var dump_attributes = null;
  1653. var dump_tree = null;
  1654. if (!SB_DEBUG) {
  1655.   debug = function (s) {};
  1656.   dump_attributes = function (node, depth) {};
  1657.   dump_tree = function (node) {};
  1658.   var _dump_tree_recur = function (node, depth, index) {};
  1659. } else {
  1660.   debug = function (s) { dump("-*- sbOverlay: " + s + "\n"); };
  1661.  
  1662.   dump_attributes = function (node, depth) {
  1663.     var attributes = node.attributes;
  1664.     var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | . ";
  1665.  
  1666.     if (!attributes || attributes.length == 0) {
  1667.       debug(indent.substr(indent.length - depth*2) + "no attributes");
  1668.     }
  1669.     for (var ii=0; ii < attributes.length; ii++) {
  1670.       var attr = attributes.item(ii);
  1671.       debug(indent.substr(indent.length - depth*2) + attr.name +
  1672.             "=" + attr.value);
  1673.     }
  1674.   }
  1675.   dump_tree = function (node) {
  1676.     _dump_tree_recur(node, 0, 0);
  1677.   }
  1678.   _dump_tree_recur = function (node, depth, index) {
  1679.     if (!node) {
  1680.       debug("dump_tree: node is null");
  1681.     }
  1682.     var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | + ";
  1683.     debug(indent.substr(indent.length - depth*2) + index +
  1684.           " " + node.nodeName);
  1685.     if (node.nodeName != "#text") {
  1686.       dump_attributes(node, depth);
  1687.     }
  1688.     var kids = node.childNodes;
  1689.     for (var ii=0; ii < kids.length; ii++) {
  1690.       _dump_tree_recur(kids[ii], depth + 1, ii);
  1691.     }
  1692.   }
  1693. }
  1694.  
  1695. //////////////////////////////////////////////////////////////
  1696. // Install the load/unload handlers
  1697. //////////////////////////////////////////////////////////////
  1698. addEventListener("load", sidebar_overlay_init, false);
  1699. addEventListener("unload", sidebar_overlay_destruct, false);
  1700.  
  1701.  
  1702. //****************************************************************
  1703. // This function will activate the sidebar panel specified by
  1704. // 'panel_id'. If the panel is not already visible in the
  1705. // sidebar, it is added.  Sidebar is displayed if hidden.
  1706. // |wait| and |tries| are for re-entrant calls
  1707. //****************************************************************
  1708. function RevealPanel(panel_id, wait, tries) {
  1709.   debug('sidebarOverlay.js: RevealPanel(panel_id:'+panel_id
  1710.         +',wait:'+wait+',tries:'+tries+')');
  1711.  
  1712.   // First make sure the sidebar is open
  1713.   if (sidebar_is_hidden()) {
  1714.     SidebarShowHide();
  1715.   }
  1716.  
  1717.   // MERC: SL - give sidebar panels a bit of time to load up first
  1718.   //    time you launch browser
  1719.   // If the panel is not in the list, don't bother opening it.
  1720.   var panel = document.getElementById(panel_id);
  1721.   if (!panel) {
  1722.    dump("unable to open panel in RevealPanel " + panel_id + "\n");
  1723.    if (numTries > 2) return;
  1724.    numTries++;
  1725.   }
  1726.  
  1727.   var panelObj = sidebarObj.panels.get_panel_from_id(panel_id);
  1728.   
  1729.   if (!panelObj) {
  1730.     // The panel does not exist yet.  This probably means we need to give
  1731.     // the sidebar a bit more time to load.
  1732.     if (!wait) wait = 100;
  1733.     if (!tries) tries = 3;
  1734.     if (tries) {
  1735.       setTimeout("RevealPanel('"+panel_id+"',"+wait+","+(tries-1)+");", wait);
  1736.       return;
  1737.     }
  1738.   }
  1739.  
  1740.   var panel = document.getElementById(panel_id);
  1741.  
  1742.   // if the panel is not in the sidebar panel list, add it
  1743.   if (!panelObj.is_in_view()) {
  1744.     SidebarTogglePanel(panel);
  1745.   }
  1746.  
  1747.   // activate panel and make sidebar visible
  1748.   SidebarSelectPanel(panel, true, true);
  1749.  
  1750.   // return the panel object
  1751.   return panelObj;
  1752. }
  1753.