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