diff options
Diffstat (limited to '')
-rw-r--r-- | comm/suite/components/sidebar/content/sidebarOverlay.js | 1704 |
1 files changed, 1704 insertions, 0 deletions
diff --git a/comm/suite/components/sidebar/content/sidebarOverlay.js b/comm/suite/components/sidebar/content/sidebarOverlay.js new file mode 100644 index 0000000000..95b0af029d --- /dev/null +++ b/comm/suite/components/sidebar/content/sidebarOverlay.js @@ -0,0 +1,1704 @@ +/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// xul/id summary: +// +// <box> sidebar-box +// <splitter> sidebar-panels-splitter +// <box> sidebar-panels-splitter-box* +// <sidebarheader> sidebar-title-box +// <menubutton> sidebar-panel-picker* +// <menupopup> sidebar-panel-picker-popup +// <box> sidebar-panels +// <template> sidebar-template* +// <box> sidebar-iframe-no-panels +// <splitter> sidebar-splitter +// <menupopup> menu_View_Popup* +// <menuitem> sidebar-menu + +////////////////////////////////////////////////////////////// +// Import modules +////////////////////////////////////////////////////////////// + +var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +////////////////////////////////////////////////////////////// +// Global variables +////////////////////////////////////////////////////////////// + + +var gCurFrame; +var gTimeoutID = null; +var gMustInit = true; +var gAboutToUncollapse = false; +var gCheckMissingPanels = true; + +function setBlank() +{ + gTimeoutID = null; + gCurFrame.setAttribute('src', 'chrome://communicator/content/sidebar/PageNotFound.xul'); +} + + +// Uncomment for debug output +const SB_DEBUG = false; + +// pref for limiting number of tabs in view +// initialized in sidebar_overlay_init() +var gNumTabsInViewPref; + +// The rdf service +var RDF = Cc["@mozilla.org/rdf/rdf-service;1"] + .getService(Ci.nsIRDFService); + +const NC = "http://home.netscape.com/NC-rdf#"; + +// The directory services property to find panels.rdf +const PANELS_RDF_FILE = "UPnls"; +const SIDEBAR_VERSION = "0.1"; + +// The default sidebar: +var sidebarObj = new Object; +sidebarObj.never_built = true; + +////////////////////////////////////////////////////////////////////// +// sbPanelList Class +// +// Wrapper around DOM representation of the sidebar. This UI event +// handlers and the sidebar datasource observers call into this. This +// class is responsible for keeping the DOM state of the sidebar up to +// date. +// This class does not make any changes to the sidebar rdf datasource. +////////////////////////////////////////////////////////////////////// + +function sbPanelList(container_id) +{ + debug("sbPanelList("+container_id+")"); + this.node = document.getElementById(container_id); + this.childNodes = this.node.childNodes; + this.initialized = false; // set after first display of tabs +} + +sbPanelList.prototype.get_panel_from_id = +function (id) +{ + debug("get_panel_from_id(" + id + ")"); + var index = 0; + var header = null; + if (id && id != '') { + for (var ii=2; ii < this.node.childNodes.length; ii += 2) { + header = this.node.childNodes.item(ii); + if (header.getAttribute('id') == id) { + debug("get_panel_from_id: Found at index, " + ii); + index = ii; + break; + } + } + } + if (index > 0) { + return new sbPanel(id, header, index); + } else { + return null; + } +} + +sbPanelList.prototype.get_panel_from_header_node = +function (node) +{ + return this.get_panel_from_id(node.getAttribute('id')); +} + +sbPanelList.prototype.get_panel_from_header_index = +function (index) +{ + return this.get_panel_from_header_node(this.node.childNodes.item(index)); +} + +sbPanelList.prototype.find_first = +function (panels) +{ + debug("pick_default_panel: length=" + this.node.childNodes.length); + for (var ii = 2; ii < this.node.childNodes.length; ii += 2) { + var panel = this.get_panel_from_header_index(ii); + if (!panel.is_excluded() && panel.is_in_view()) { + return panel; + } + } + return null; +} + +sbPanelList.prototype.find_last = +function (panels) +{ + debug("pick_default_panel: length=" + this.node.childNodes.length); + for (var ii=(this.node.childNodes.length - 1); ii >= 2; ii -= 2) { + var panel = this.get_panel_from_header_index(ii); + if (!panel.is_excluded() && panel.is_in_view()) { + return panel; + } + } + return null; +} + +sbPanelList.prototype.visible_panels_exist = +function () +{ + var i; + var panels = this.node.childNodes; + for (i = 2; i < panels.length; i += 2) + { + if (!panels.item(i).hidden) + return true; + } + return false; +} + +sbPanelList.prototype.num_panels_included = +function () +{ + var count = 0; + var panels = this.node.childNodes; + for (var i = 2; i < panels.length; i += 2) + { + var curr = this.get_panel_from_header_index(i); + if (!curr.is_excluded()) + count++; + } + return count; +} + +sbPanelList.prototype.num_panels_in_view = +function () +{ + var count = 0; + var panels = this.node.childNodes; + for (var i = 2; i < panels.length; i += 2) + { + var curr = this.get_panel_from_header_index(i); + if (curr.is_in_view()) + count++; + } + return count; +} + +sbPanelList.prototype.select = +function (panel, force_reload) +{ + if (!force_reload && panel.is_selected()) { + return; + } + // select(): Open this panel and possibly reload it. + if (this.node.getAttribute('last-selected-panel') != panel.id) { + // "last-selected-panel" is used as a global variable. + // this.update() will reference "last-selected-panel". + // This way the value can be persisted in xulstore.json. + this.node.setAttribute('last-selected-panel', panel.id); + } + this.update(force_reload); +} + +sbPanelList.prototype.exclude = +function (panel) +{ + if (this.node.getAttribute('last-selected-panel') == panel.id) { + this.select_default_panel(); + } else { + this.update(false); + } +} + + +sbPanelList.prototype.select_default_panel = +function () +{ + var default_panel = null + + // First, check the XUL for the "defaultpanel" attribute of "sidebar-box". + var sidebar_container = document.getElementById('sidebar-box'); + var content_default_id = sidebar_container.getAttribute('defaultpanel'); + if (content_default_id != '') { + var content = sidebarObj.panels.get_panel_from_id(content_default_id); + if (content && !content.is_excluded() && content.is_in_view()) { + default_panel = content; + } + } + + // Second, try to use the panel persisted in 'last-selected-panel'. + if (!default_panel) { + var last_selected_id = this.node.getAttribute('last-selected-panel'); + if (last_selected_id != '') { + var last = sidebarObj.panels.get_panel_from_id(last_selected_id); + if (last && !last.is_excluded() && last.is_in_view()) { + default_panel = last; + } + } + } + + // Finally, just use the last one in the list. + if (!default_panel) { + default_panel = this.find_last(); + } + + if (default_panel) { + this.node.setAttribute('last-selected-panel', default_panel.id); + } + this.update(false); +} + +sbPanelList.prototype.refresh = +function () +{ + var last_selected_id = this.node.getAttribute('last-selected-panel'); + var last_selected = sidebarObj.panels.get_panel_from_id(last_selected_id); + if (last_selected && last_selected.is_selected()) { + // The desired panel is already selected + this.update(false); + } else { + this.select_default_panel(); + } +} + +// panel_loader(): called from a timer that is set in sbPanelList.update() +// Removes the "Loading..." screen when the panel has finished loading. +function panel_loader() { + debug("panel_loader()"); + + if (gTimeoutID != null) { + clearTimeout(gTimeoutID); + gTimeoutID = null; + } + + this.removeEventListener("load", panel_loader, true); + this.removeAttribute('collapsed'); + // uncollapse the notificationbox element + this.parentNode.removeAttribute('collapsed'); + // register a progress listener for the notificationbox, + // now that this browser has a docShell + this.parentNode.addProgressListener(); + this.setAttribute('loadstate', 'loaded'); + // hide the load area + this.parentNode.parentNode.firstChild.setAttribute('hidden', 'true'); + + if (this.hasAttribute('focusOnLoad')) { + var elementToFocus = this.contentDocument.documentElement.getAttribute('elementtofocus'); + if (elementToFocus) { + var element = this.contentDocument.getElementById(elementToFocus); + if (element) + element.focus(); + else + dump(elementToFocus + ' element was not found to focus!\n'); + } else { + this.contentWindow.focus(); + } + this.removeAttribute('focusOnLoad'); + } +} +sbPanelList.prototype.update = +function (force_reload) +{ + // This function requires that the attribute 'last-selected-panel' + // holds the id of a non-excluded panel. If it doesn't, no panel will + // be selected. The attribute is used instead of a function + // parameter to allow the value to be persisted in xulstore.json. + var selected_id = this.node.getAttribute('last-selected-panel'); + + if (sidebar_is_collapsed()) { + sidebarObj.collapsed = true; + } else { + sidebarObj.collapsed = false; + } + + var num_included = sidebarObj.panels.num_panels_included(); + if (num_included > gNumTabsInViewPref) + document.getElementById("nav-buttons-box").hidden = false; + else + document.getElementById("nav-buttons-box").hidden = true; + + var have_set_top = 0; + var have_set_after_selected = 0; + var is_after_selected = 0; + var last_header = 0; + var num_in_view = 0; + debug("this.initialized: " + this.initialized); + for (var ii=2; ii < this.node.childNodes.length; ii += 2) { + var header = this.node.childNodes.item(ii); + var content = this.node.childNodes.item(ii+1); + var id = header.getAttribute('id'); + var panel = new sbPanel(id, header, ii); + var excluded = panel.is_excluded(); + var in_view = false; + if (!this.initialized) + { + if (num_in_view < gNumTabsInViewPref) + in_view = true; + } + else + { + if (header.getAttribute("in-view") == "true") + in_view = true; + } + if (excluded || !in_view) + { + debug("item("+ii/2+") excluded: " + excluded + + " in view: " + in_view); + header.setAttribute('hidden','true'); + content.setAttribute('hidden','true'); + if (!in_view) + { + header.setAttribute("in-view", false); + header.removeAttribute("top-panel"); + header.removeAttribute("last-panel"); + } + } else { + // only set if in view + if (!this.initialized || (num_in_view < gNumTabsInViewPref)) + last_header = header; + header.removeAttribute('last-panel'); + // only set if in view + if (!have_set_top && + (!this.initialized || (header.getAttribute("in-view") == "true"))) + { + header.setAttribute('top-panel','true'); + have_set_top = 1; + } else { + header.removeAttribute('top-panel'); + } + if (!have_set_after_selected && is_after_selected) { + header.setAttribute('first-panel-after-selected','true'); + have_set_after_selected = 1 + } else { + header.removeAttribute('first-panel-after-selected'); + } + header.removeAttribute('hidden'); + header.setAttribute("in-view", true); + num_in_view++; + + // (a) when we have hit the maximum number of tabs that can be in view and no tab + // has been selected yet + // -or- + // (b) when we have reached the last tab we are about to display + if ( ((num_in_view == num_included) || + (num_in_view == gNumTabsInViewPref)) && + !is_after_selected ) + { + selected_id = id; + this.node.setAttribute('last-selected-panel', id); + } + + // Pick sandboxed, or unsandboxed iframe + var iframe = panel.get_iframe(); + var notificationbox = iframe.parentNode; + var load_state; + + if (selected_id == id) { + is_after_selected = 1 + debug("item("+ii/2+") selected"); + header.setAttribute('selected', 'true'); + content.removeAttribute('hidden'); + content.removeAttribute('collapsed'); + + if (sidebarObj.collapsed && panel.is_sandboxed()) { + if (!panel.is_persistent()) { + debug(" set src=about:blank"); + iframe.setAttribute('src', 'about:blank'); + } + } else { + var saved_src = iframe.getAttribute('content'); + var src = iframe.getAttribute('src'); + // either we have been requested to force_reload or the + // panel src has changed so we must restore the original src + if (force_reload || (saved_src != src)) { + debug(" set src="+saved_src); + iframe.setAttribute('src', saved_src); + + if (gTimeoutID != null) + clearTimeout(gTimeoutID); + + gCurFrame = iframe; + gTimeoutID = setTimeout(setBlank, 20000); + } + } + + load_state = content.getAttribute('loadstate'); + if (load_state == 'stopped') { + load_state = 'never loaded'; + toggleLoadarea(content); + } + if (load_state == 'never loaded') { + iframe.removeAttribute('hidden'); + iframe.setAttribute('loadstate', 'loading'); + iframe.addEventListener('load', panel_loader, true); + } + } else { + debug("item("+ii/2+")"); + header.removeAttribute('selected'); + content.setAttribute('collapsed','true'); + + if (!panel.is_persistent()) { + iframe.setAttribute('src', 'about:blank'); + load_state = content.getAttribute('loadstate'); + if (load_state == 'loading') { + iframe.removeEventListener("load", panel_loader, true); + content.setAttribute('hidden','true'); + iframe.setAttribute('loadstate', 'never loaded'); + } + } + } + } + } + if (last_header) { + last_header.setAttribute('last-panel','true'); + } + + var no_panels_iframe = document.getElementById('sidebar-iframe-no-panels'); + if (have_set_top) { + no_panels_iframe.setAttribute('hidden','true'); + // The hide and show of 'sidebar-panels' should not be needed, + // but some old profiles may have this persisted as hidden (50973). + this.node.removeAttribute('hidden'); + } else { + no_panels_iframe.removeAttribute('hidden'); + } + + this.initialized = true; +} + + +////////////////////////////////////////////////////////////////////// +// sbPanel Class +// +// Like sbPanelList, this class is a wrapper around DOM representation +// of individual panels in the sidebar. This UI event handlers and the +// sidebar datasource observers call into this. This class is +// responsible for keeping the DOM state of the sidebar up to date. +// This class does not make any changes to the sidebar rdf datasource. +////////////////////////////////////////////////////////////////////// + +function sbPanel(id, header, index) +{ + // This constructor should only be called by sbPanelList class. + // To create a panel instance, use the helper functions in sbPanelList: + // sb_panel.get_panel_from_id(id) + // sb_panel.get_panel_from_header_node(dom_node) + // sb_panel.get_panel_from_header_index(index) + this.id = id; + this.header = header; + this.index = index; + this.parent = sidebarObj.panels; +} + +sbPanel.prototype.get_header = +function () +{ + return this.header; +} + +sbPanel.prototype.get_content = +function () +{ + return this.get_header().nextSibling; +} + +sbPanel.prototype.is_sandboxed = +function () +{ + if (typeof this.sandboxed == "undefined") { + var notificationbox = this.get_content().childNodes.item(1); + var unsandboxed_iframe = notificationbox.firstChild; + this.sandboxed = !unsandboxed_iframe.getAttribute('content').match(/^chrome:/); + } + return this.sandboxed; +} + +sbPanel.prototype.get_iframe = +function () +{ + if (typeof this.iframe == "undefined") { + var notificationbox = this.get_content().childNodes.item(1); + this.iframe = this.is_sandboxed() ? notificationbox.lastChild : + notificationbox.firstChild; + } + return this.iframe; +} + +// This exclude function is used on panels and on the panel picker menu. +// That is why it is hanging out in the global name space instead of +// minding its own business in the class. +function sb_panel_is_excluded(node) +{ + var exclude = node.getAttribute('exclude'); + return ( exclude && exclude != '' && + exclude.includes(sidebarObj.component)); +} +sbPanel.prototype.is_excluded = +function () +{ + return sb_panel_is_excluded(this.get_header()); +} + +sbPanel.prototype.is_in_view = +function() +{ + return (this.header.getAttribute("in-view") == "true"); +} + +sbPanel.prototype.is_selected = +function (panel_id) +{ + return 'true' == this.get_header().getAttribute('selected'); +} + +sbPanel.prototype.is_persistent = +function () +{ + var rv = false; + var datasource = sidebarObj.datasource; + var persistNode = datasource.GetTarget(RDF.GetResource(this.id), + RDF.GetResource(NC + "persist"), + true); + if (persistNode) + { + persistNode = + persistNode.QueryInterface(Ci.nsIRDFLiteral); + rv = persistNode.Value == 'true'; + } + + return rv; +} + +sbPanel.prototype.select = +function (force_reload) +{ + this.parent.select(this, force_reload); +} + +sbPanel.prototype.stop_load = +function () +{ + var iframe = this.get_iframe(); + var content = this.get_content(); + var load_state = iframe.getAttribute('loadstate'); + if (load_state == "loading") { + debug("Stop the presses"); + iframe.removeEventListener("load", panel_loader, true); + content.setAttribute("loadstate", "stopped"); + iframe.setAttribute('src', 'about:blank'); + toggleLoadarea(content); + } +} + +function toggleLoadarea(content) +{ + // toggle between "loading" and "load stopped" in the UI + var widgetBox = content.firstChild.firstChild; + var widgetBoxKids = widgetBox.childNodes; + var stopButton = widgetBoxKids.item(3); + var reloadButton = widgetBoxKids.item(4); + var loadingImage = widgetBox.firstChild; + var loadingText = loadingImage.nextSibling; + var loadStoppedText = loadingText.nextSibling; + + // sanity check + if (stopButton.getAttribute("type") != "stop") + { + debug("Error: Expected button of type=\"stop\" but didn't get one!"); + return; + } + + if (!stopButton.hidden) + { + // change button from "stop" to "reload" + stopButton.hidden = "true"; + reloadButton.removeAttribute("hidden"); + + // hide the loading image and set text to "load stopped" + loadingImage.hidden = "true"; + loadingText.hidden = "true"; + loadStoppedText.removeAttribute("hidden"); + } + else + { + // change button from "reload" to "stop" + stopButton.removeAttribute("hidden"); + reloadButton.hidden = "true"; + + // show the loading image and set text to "loading" + loadingImage.removeAttribute("hidden"); + loadingText.removeAttribute("hidden"); + loadStoppedText.hidden = "true"; + } +} + +sbPanel.prototype.exclude = +function () +{ + // Exclusion is handled by the datasource, + // but we need to make sure this panel is no longer selected. + this.get_header().removeAttribute('selected'); + this.parent.exclude(this); +} + +sbPanel.prototype.reload = +function () +{ + if (!this.is_excluded()) { + this.select(true); + } +} + +////////////////////////////////////////////////////////////////// +// Panels' RDF Datasource Observer +// +// This observer will ensure that the Sidebar UI stays current +// when the datasource changes. +// - When "refresh" is asserted, the sidebar refreshed. +// Currently this happens when a panel is included/excluded or +// added/removed (the later comes from the customize dialog). +// - When "refresh_panel" is asserted, the targeted panel is reloaded. +// Currently this happens when the customize panel dialog is closed. +////////////////////////////////////////////////////////////////// +var panel_observer = { + onAssert : function(ds,src,prop,target) { + //debug ("observer: assert"); + // "refresh" is asserted by select menu and by customize.js. + if (prop == RDF.GetResource(NC + "refresh")) { + sidebarObj.panels.initialized = false; // reset so panels are put in view + sidebarObj.panels.refresh(); + } else if (prop == RDF.GetResource(NC + "refresh_panel")) { + var panel_id = target.QueryInterface(Ci.nsIRDFLiteral).Value; + var panel = sidebarObj.panels.get_panel_from_id(panel_id); + panel.reload(); + } + }, + onUnassert : function(ds,src,prop,target) { + //debug ("observer: unassert"); + }, + onChange : function(ds,src,prop,old_target,new_target) { + //debug ("observer: change"); + }, + onMove : function(ds,old_src,new_src,prop,target) { + //debug ("observer: move"); + }, + onBeginUpdateBatch : function(ds) { + //debug ("observer: onBeginUpdateBatch"); + }, + onEndUpdateBatch : function(ds) { + //debug ("observer: onEndUpdateBatch"); + } +}; + +// Use an assertion to pass a "refresh" event to all the sidebars. +// They use observers to watch for this assertion (see above). +function refresh_all_sidebars() { + sidebarObj.datasource.Assert(RDF.GetResource(sidebarObj.resource), + RDF.GetResource(NC + "refresh"), + RDF.GetLiteral("true"), + true); + sidebarObj.datasource.Unassert(RDF.GetResource(sidebarObj.resource), + RDF.GetResource(NC + "refresh"), + RDF.GetLiteral("true")); +} + +////////////////////////////////////////////////////////////// +// Sidebar Init +////////////////////////////////////////////////////////////// +function sidebar_overlay_init() { + if (sidebar_is_collapsed() && !gAboutToUncollapse) + return; + gMustInit = false; + sidebarObj.panels = new sbPanelList('sidebar-panels'); + sidebarObj.datasource_uri = get_sidebar_datasource_uri(); + sidebarObj.datasource = RDF.GetDataSourceBlocking(sidebarObj.datasource_uri); + sidebarObj.resource = 'urn:sidebar:current-panel-list'; + + sidebarObj.master_datasources = sidebarObj.datasource_uri; + sidebarObj.master_resource = 'urn:sidebar:master-panel-list'; + sidebarObj.component = gPrivate ? "navigator:browser" : + document.documentElement.getAttribute('windowtype'); + debug("sidebarObj.component is " + sidebarObj.component); + + // Sync RDF with broadcasters. + SidebarBroadcastersToRDF(); + + // Initialize the display + var sidebar_element = document.getElementById('sidebar-box'); + var sidebar_menuitem = document.getElementById('sidebar-menu'); + if (sidebar_is_hidden()) { + if (sidebar_menuitem) { + sidebar_menuitem.setAttribute('checked', 'false'); + } + } else { + if (sidebar_menuitem) { + sidebar_menuitem.setAttribute('checked', 'true'); + } + + // for old profiles that don't persist the hidden attribute when splitter is not hidden. + var sidebar_splitter = document.getElementById('sidebar-splitter') + if (sidebar_splitter) + sidebar_splitter.setAttribute('hidden', 'false'); + + if (sidebarObj.never_built) { + sidebarObj.never_built = false; + + debug("sidebar = " + sidebarObj); + debug("sidebarObj.resource = " + sidebarObj.resource); + debug("sidebarObj.datasource_uri = " + sidebarObj.datasource_uri); + + // Obtain the pref for limiting the number of tabs in view, defaults to 8. + gNumTabsInViewPref = Services.prefs.getIntPref("sidebar.num_tabs_in_view", 8); + + // Show the header for the panels area. Use a splitter if there + // is stuff over the panels area. + var sidebar_panels_splitter = document.getElementById('sidebar-panels-splitter'); + if (sidebar_element.firstChild != sidebar_panels_splitter) { + debug("Showing the panels splitter"); + sidebar_panels_splitter.removeAttribute('hidden'); + } + } + if (sidebar_is_collapsed()) { + sidebarObj.collapsed = true; + } else { + sidebarObj.collapsed = false; + } + + sidebar_open_default_panel(100, 0); + } +} + +function sidebar_overlay_destruct() { + var panels = document.getElementById('sidebar-panels'); + debug("Removing observer from database."); + panels.database.RemoveObserver(panel_observer); +} + +var gBusyOpeningDefault = false; + +function sidebar_open_default_panel(wait, tries) { + // check for making function reentrant + if (gBusyOpeningDefault) + return; + gBusyOpeningDefault = true; + + var ds = sidebarObj.datasource; + var currentListRes = RDF.GetResource("urn:sidebar:current-panel-list"); + var panelListRes = RDF.GetResource("http://home.netscape.com/NC-rdf#panel-list"); + var container = ds.GetTarget(currentListRes, panelListRes, true); + if (container) { + // Add the user's current panel choices to the template builder, + // which will aggregate it with the other datasources that describe + // the individual panel's title, customize URL, and content URL. + var panels = document.getElementById('sidebar-panels'); + panels.database.AddDataSource(ds); + + debug("Adding observer to database."); + panels.database.AddObserver(panel_observer); + + // XXX This is a hack to force re-display + panels.builder.rebuild(); + } else { + if (tries < 3) { + // No children yet, try again later + setTimeout(sidebar_open_default_panel, wait, wait*2, ++tries); + gBusyOpeningDefault = false; + return; + } else { + sidebar_fixup_datasource(); + } + } + + sidebarObj.panels.refresh(); + gBusyOpeningDefault = false; + if (gCheckMissingPanels) + check_for_missing_panels(); +} + +function SidebarRebuild() { + sidebarObj.panels.initialized = false; // reset so panels are brought in view + var panels = document.getElementById("sidebar-panels"); + panels.builder.rebuild(); + sidebar_open_default_panel(100, 0); +} + +function check_for_missing_panels() { + var tabs = sidebarObj.panels.node.childNodes; + var currHeader; + var currTab; + for (var i = 2; i < tabs.length; i += 2) { + currHeader = tabs[i]; + currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i); + if (!currTab.is_excluded()) { + if (currHeader.hasAttribute("prereq") && currHeader.getAttribute("prereq") != "") { + var prereq_file = currHeader.getAttribute("prereq"); + var channel = + Services.io.newChannelFromURI(Services.io.newURI(prereq_file), + null, + Services.scriptSecurityManager.getSystemPrincipal(), + null, + Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, + Ci.nsIContentPolicy.TYPE_OTHER); + try { + channel.open(); + } + catch (ex) { + if (ex.result != Cr.NS_ERROR_FILE_NOT_FOUND) { + throw ex; + } + sidebarObj.datasource.Assert(RDF.GetResource(currHeader.getAttribute("id")), + RDF.GetResource(NC + "exclude"), + RDF.GetLiteral(sidebarObj.component), + true); + currTab.exclude(); + } + } + } + } + gCheckMissingPanels = false; +} + +////////////////////////////////////////////////////////////// +// Sidebar File and Datasource functions +////////////////////////////////////////////////////////////// + +function sidebar_get_panels_file() { + try { + // Use the fileLocator to look in the profile directory to find + // 'panels.rdf', which is the database of the user's currently + // selected panels. + // If <profile>/panels.rdf doesn't exist, GetFileLocation() will copy + // bin/defaults/profile/panels.rdf to <profile>/panels.rdf + var sidebar_file = GetSpecialDirectory(PANELS_RDF_FILE); + if (!sidebar_file.exists()) { + // This should not happen, as GetFileLocation() should copy + // defaults/panels.rdf to the users profile directory + debug("Sidebar panels file does not exist"); + throw("Panels file does not exist"); + } + return sidebar_file; + } catch (ex) { + // This should not happen + debug("Error: Unable to grab panels file.\n"); + throw(ex); + } + return null; +} + +function sidebar_revert_to_default_panels() { + try { + var sidebar_file = sidebar_get_panels_file(); + + sidebar_file.remove(false); + + // Since we just removed the panels file, + // this should copy the defaults over. + sidebar_file = sidebar_get_panels_file(); + + debug("sidebar defaults reloaded"); + var datasource = sidebarObj.datasource; + datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Refresh(true); + } catch (ex) { + debug("Error: Unable to reload panel defaults file.\n"); + } + return null; +} + +function get_sidebar_datasource_uri() { + try { + var sidebar_file = sidebar_get_panels_file(); + + var fileHandler = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler); + + return fileHandler.getURLSpecFromFile(sidebar_file); + } catch (ex) { + // This should not happen + debug("Error: Unable to load panels file.\n"); + } + return null; +} + +function sidebar_fixup_datasource() { + var datasource = sidebarObj.datasource; + var resource = RDF.GetResource(sidebarObj.resource); + + var panel_list = datasource.GetTarget(resource, + RDF.GetResource(NC+"panel-list"), + true); + if (!panel_list) { + debug("Sidebar datasource is an old format or busted\n"); + sidebar_revert_to_default_panels(); + } else { + // The datasource is ok, but it just has no panels. + // sidebar_refresh() will display some helper content. + // Do nothing here. + } +} + +////////////////////////////////////////////////////////////// +// Sidebar Interface for XUL +////////////////////////////////////////////////////////////// + +// Change the sidebar content to the selected panel. +// Called when a panel title is clicked. +function SidebarSelectPanel(header, should_popopen, should_unhide) { + debug("SidebarSelectPanel("+header+","+should_popopen+","+should_unhide+")"); + var panel = sidebarObj.panels.get_panel_from_header_node(header); + + if (!panel) { + return false; + } + + var popopen = false; + var unhide = false; + + if (panel.is_excluded()) { + return false; + } + if (sidebar_is_hidden()) { + if (should_unhide) { + unhide = true; + } else { + return false; + } + } + if (sidebar_is_collapsed()) { + if (should_popopen) { + popopen = true; + } else { + return false; + } + } + if (unhide) SidebarShowHide(); + if (popopen) SidebarExpandCollapse(); + + try { + panel.get_iframe().setAttribute('focusOnLoad', true); + } catch (ex) { + // ignore exception for cases where content isn't built yet + // e.g., auto opening search tab: we don't want to focus search field + } + if (!panel.is_selected()) panel.select(false); + + return true; +} + +function SidebarGetLastSelectedPanel() +{ + return (sidebarObj.panels && + sidebarObj.panels.node.getAttribute('last-selected-panel')); +} + +function SidebarGetRelativePanel(direction) +{ + // direction == 1 to view next panel, -1 to view prev panel + + if (sidebar_is_hidden()) + SidebarShowHide(); + if (sidebar_is_collapsed()) + SidebarExpandCollapse(); + + var currentPanel = sidebarObj.panels.get_panel_from_id(SidebarGetLastSelectedPanel()); + if (!currentPanel) { + sidebarObj.panels.select_default_panel(); + return; + } + + var newPanel = currentPanel; + + do { + var newPanelIndex = newPanel.index + (direction * 2); + if (newPanelIndex < 2 || newPanelIndex >= sidebarObj.panels.node.childNodes.length) + newPanel = (direction == 1)? sidebarObj.panels.find_first(): sidebarObj.panels.find_last(); + else + newPanel = sidebarObj.panels.get_panel_from_header_index(newPanelIndex); + + if (!newPanel) + break; + + if (!newPanel.is_excluded()) { + SidebarSelectPanel(newPanel.header, true, true); // found a panel that's not excluded to select -- do it + break; + } + } while (newPanel != currentPanel); // keep looking for a panel, but don't loop infinitely +} + +function SidebarStopPanelLoad(header) { + var panel = sidebarObj.panels.get_panel_from_header_node(header); + panel.stop_load(); +} + +function SidebarReloadPanel(header) { + var panel = sidebarObj.panels.get_panel_from_header_node(header); + panel.reload(); +} + +// No one is calling this right now. +function SidebarReload() { + sidebarObj.panels.refresh(); +} + +// Set up a lame hack to avoid opening two customize +// windows on a double click. +var gDisableCustomize = false; +function enable_customize() { + gDisableCustomize = false; +} + +// Bring up the Sidebar customize dialog. +function SidebarCustomize() { + // Use a single sidebar customize dialog + var customizeWindow = Services.wm.getMostRecentWindow('sidebar:customize'); + + if (customizeWindow) { + debug("Reuse existing customize dialog"); + customizeWindow.focus(); + } else { + debug("Open a new customize dialog"); + + if (false == gDisableCustomize) { + debug("First time creating customize dialog"); + gDisableCustomize = true; + + var panels = document.getElementById('sidebar-panels'); + + customizeWindow = window.openDialog( + 'chrome://communicator/content/sidebar/customize.xul', + '_blank','centerscreen,chrome,resizable,dialog=no,dependent', + sidebarObj.master_datasources, + sidebarObj.master_resource, + sidebarObj.datasource_uri, + sidebarObj.resource); + setTimeout(enable_customize, 2000); + } + } +} + +function BrowseMorePanels() +{ + var url = ''; + var browser_url = "chrome://navigator/content/navigator.xul"; + var locale; + try { + url = Services.prefs.getCharPref("sidebar.customize.directory.url"); + var temp = Services.prefs.getCharPref("browser.chromeURL"); + if (temp) + browser_url = temp; + } catch(ex) { + debug("Unable to get prefs: "+ex); + } + window.openDialog(browser_url, "_blank", "chrome,all,dialog=no", url); +} + + + +function sidebar_is_collapsed() { + var sidebar_splitter = document.getElementById('sidebar-splitter'); + return (sidebar_splitter && + sidebar_splitter.getAttribute('state') == 'collapsed'); +} + +function SidebarExpandCollapse() { + var sidebar_splitter = document.getElementById('sidebar-splitter'); + var sidebar_box = document.getElementById('sidebar-box'); + if (sidebar_splitter.getAttribute('state') == 'collapsed') { + if (gMustInit) + sidebar_overlay_init(); + debug("Expanding the sidebar"); + sidebar_splitter.removeAttribute('state'); + sidebar_box.removeAttribute('collapsed'); + SidebarSetButtonOpen(true); + } else { + debug("Collapsing the sidebar"); + sidebar_splitter.setAttribute('state', 'collapsed'); + sidebar_box.setAttribute('collapsed', 'true'); + SidebarSetButtonOpen(false); + } +} + +// sidebar_is_hidden() - Helper function for SidebarShowHide(). +function sidebar_is_hidden() { + var sidebar_title = document.getElementById('sidebar-title-box'); + var sidebar_box = document.getElementById('sidebar-box'); + return sidebar_box.getAttribute('hidden') == 'true' + || sidebar_title.getAttribute('hidden') == 'true'; +} + +// Show/Hide the entire sidebar. +// Invoked by the "View / Sidebar" menu option. +function SidebarShowHide() { + var sidebar_box = document.getElementById('sidebar-box'); + var title_box = document.getElementById('sidebar-title-box'); + var sidebar_panels_splitter = document.getElementById('sidebar-panels-splitter'); + var sidebar_panels_splitter_box = document.getElementById('sidebar-panels-splitter-box'); + var sidebar_splitter = document.getElementById('sidebar-splitter'); + var sidebar_menu_item = document.getElementById('sidebar-menu'); + var tabs_menu = document.getElementById('sidebar-panel-picker'); + + if (sidebar_is_hidden()) { + debug("Showing the sidebar"); + + // for older profiles: + sidebar_box.setAttribute('hidden', 'false'); + sidebar_panels_splitter_box.setAttribute('hidden', 'false'); + + sidebar_box.removeAttribute('collapsed'); + if (sidebar_splitter.getAttribute('state') == 'collapsed') + sidebar_splitter.removeAttribute('state'); + title_box.removeAttribute('hidden'); + sidebar_panels_splitter_box.removeAttribute('collapsed'); + sidebar_splitter.setAttribute('hidden', 'false'); + if (sidebar_box.firstChild != sidebar_panels_splitter) { + debug("Showing the panels splitter"); + sidebar_panels_splitter.removeAttribute('hidden'); + if (sidebar_panels_splitter.getAttribute('state') == 'collapsed') + sidebar_panels_splitter.removeAttribute('state'); + } + sidebar_overlay_init(); + sidebar_menu_item.setAttribute('checked', 'true'); + tabs_menu.removeAttribute('hidden'); + SidebarSetButtonOpen(true); + } else { + debug("Hiding the sidebar"); + var hide_everything = sidebar_panels_splitter.getAttribute('hidden') == 'true'; + if (hide_everything) { + debug("Hide everything"); + sidebar_box.setAttribute('collapsed', 'true'); + sidebar_splitter.setAttribute('hidden', 'true'); + } else { + sidebar_panels_splitter.setAttribute('hidden', 'true'); + } + title_box.setAttribute('hidden', 'true'); + sidebar_panels_splitter_box.setAttribute('collapsed', 'true'); + sidebar_menu_item.setAttribute('checked', 'false'); + tabs_menu.setAttribute('hidden', 'true'); + SidebarSetButtonOpen(false); + } + // Immediately save persistent values + document.persist('sidebar-title-box', 'hidden'); + PersistWidth(); + window.content.focus(); +} + +function SidebarGetState() { + if (sidebar_is_hidden()) + return "hidden"; + if (sidebar_is_collapsed()) + return "collapsed"; + return "visible"; +} + +function SidebarSetState(aState) { + document.getElementById("sidebar-box").hidden = aState != "visible"; + document.getElementById("sidebar-splitter").hidden = aState == "hidden"; +} + +function SidebarBuildPickerPopup() { + var menu = document.getElementById('sidebar-panel-picker-popup'); + menu.database.AddDataSource(sidebarObj.datasource); + menu.builder.rebuild(); + + for (var ii=3; ii < menu.childNodes.length; ii++) { + var panel_menuitem = menu.childNodes.item(ii); + if (sb_panel_is_excluded(panel_menuitem)) { + debug(ii+": "+panel_menuitem.getAttribute('label')+ ": excluded; uncheck."); + panel_menuitem.removeAttribute('checked'); + } else { + debug(ii+": "+panel_menuitem.getAttribute('label')+ ": included; check."); + panel_menuitem.setAttribute('checked', 'true'); + } + } +} + +function SidebarTogglePanel(panel_menuitem) { + if (!panel_menuitem.classList.contains("menuitem-sidebar") && + !panel_menuitem.classList.contains("texttab-sidebar")) + return; + + // Create a "container" wrapper around the current panels to + // manipulate the RDF:Seq more easily. + + var did_exclude = false; + var panel_id = panel_menuitem.getAttribute('id'); + var panel = sidebarObj.panels.get_panel_from_id(panel_id); + var panel_exclude = panel_menuitem.getAttribute('exclude') + if (panel_exclude == '') { + // Nothing excluded for this panel yet, so add this component to the list. + debug("Excluding " + panel_id + " from " + sidebarObj.component); + sidebarObj.datasource.Assert(RDF.GetResource(panel_id), + RDF.GetResource(NC + "exclude"), + RDF.GetLiteral(sidebarObj.component), + true); + panel.exclude(); + did_exclude = true; + } else { + // Panel has an exclude string, but it may or may not have the + // current component listed in the string. + debug("Current exclude string: " + panel_exclude); + var new_exclude = panel_exclude; + if (sb_panel_is_excluded(panel_menuitem)) { + debug("Plucking this component out of the exclude list"); + var replace_pat = new RegExp(sidebarObj.component + "\s*"); + new_exclude = new_exclude.replace(replace_pat, "").trimLeft(); + // did_exclude remains false + } else { + debug("Adding this component to the exclude list"); + new_exclude = new_exclude + " " + sidebarObj.component; + panel.exclude(); + did_exclude = true; + } + if (new_exclude == '') { + debug("Removing exclude list"); + sidebarObj.datasource.Unassert(RDF.GetResource(panel_id), + RDF.GetResource(NC + "exclude"), + RDF.GetLiteral(sidebarObj.component)); + } else { + debug("New exclude string: " + new_exclude); + exclude_target = + sidebarObj.datasource.GetTarget(RDF.GetResource(panel_id), + RDF.GetResource(NC + "exclude"), + true); + sidebarObj.datasource.Change(RDF.GetResource(panel_id), + RDF.GetResource(NC + "exclude"), + exclude_target, + RDF.GetLiteral(new_exclude)); + } + } + + var tabs = sidebarObj.panels.node.childNodes; + + if (did_exclude) + { + // if we excluded a tab in view then add another one + if (panel.is_in_view()) + { + // we excluded one so let's try to bring a non-excluded one into view + var newFirst = null; + var added = false; + for (var i = 2; i < tabs.length ; i += 2) + { + var currTab = sidebarObj.panels.get_panel_from_header_index(i); + var hasPotential = !currTab.is_excluded() && !currTab.is_in_view(); + + // set potential new first tab in case we can't find one after the + // tab that was just excluded + if (!newFirst && hasPotential) + newFirst = currTab; + + if (i > panel.index && hasPotential) + { + currTab.header.setAttribute("in-view", true); + added = true; + break; + } + } + if (!added && newFirst) + newFirst.header.setAttribute("in-view", true); + + // lose it from current view + panel.header.setAttribute("in-view", false); + } + } + else + { + panel.header.setAttribute("in-view", true); + + // if we have one too many tabs we better get rid of an old one + if (sidebarObj.panels.num_panels_in_view() > gNumTabsInViewPref) + { + // we included a new tab so let's take the last one out of view + for (i = 2; i < tabs.length; i += 2) + { + var currHeader = tabs[i]; + if (currHeader.hasAttribute("last-panel")) + currHeader.setAttribute("in-view", false); + } + } + + panel.select(false); + } + + if (did_exclude && !sidebarObj.panels.visible_panels_exist()) + // surrender focus to main content area + window.content.focus(); + else + // force all the sidebars to update + refresh_all_sidebars(); + + // Write the modified panels out. + sidebarObj.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); +} + +function SidebarNavigate(aDirection) +{ + debug("SidebarNavigate " + aDirection); + + var tabs = sidebarObj.panels.node.childNodes; + var i; + var currHeader; + var currTab; + // move forward a tab (down in the template) + if (aDirection > 0) + { + // ensure we have a tab below the last one + var foundLast = false; + var oldFirst = null; + for (i = 2; i < tabs.length; i += 2) + { + currHeader = tabs[i]; + currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i); + + if (!currTab.is_excluded()) + { + if (foundLast) + { + debug("toggling old first and new last"); + debug("new last: " + currHeader.getAttribute("id")); + debug("old first: " + oldFirst.getAttribute("id")); + currHeader.setAttribute("in-view", true); + oldFirst.setAttribute("in-view", false); + + // if old first was selected select new first instead + if (oldFirst.getAttribute("id") == + sidebarObj.panels.node.getAttribute("last-selected-panel")) + { + sidebarObj.panels.node.setAttribute('last-selected-panel', + currTab.id); + } + + break; + } + + if (!foundLast && currHeader.hasAttribute("last-panel")) + { + debug("found last"); + foundLast = true; + } + + // set the old first in case we find a new last below + // the old last and need to toggle the new first's ``in-view'' + if (!oldFirst && currTab.is_in_view()) + oldFirst = currHeader; + } + } + } + + // move back a tab (up in the template) + else if (aDirection < 0) + { + var newFirst = null, newLast = null; + var foundFirst = false; + for (i = 2; i < tabs.length; i += 2) + { + currHeader = tabs[i]; + currTab = new sbPanel(currHeader.getAttribute("id"), currHeader, i); + + if (!currTab.is_excluded()) + { + if (!foundFirst && currHeader.hasAttribute("top-panel")) + { + debug("found first"); + foundFirst = true; + } + if (!foundFirst) + { + debug("setting newFirst"); + newFirst = currHeader; + } + + if (currHeader.hasAttribute("last-panel")) + { + debug("found last"); + + // ensure we have a tab above the first one + if (newFirst) + { + debug("toggling new first and old last"); + debug("new first: " + newFirst.getAttribute("id")); + debug("old last: " + currHeader.getAttribute("id")); + + newFirst.setAttribute("in-view", true); + currHeader.setAttribute("in-view", false); // hide old last + + // if old last was selected, now select one above it + if (sidebarObj.panels.node.getAttribute("last-selected-panel") == + currTab.id) + { + sidebarObj.panels.node.setAttribute("last-selected-panel", + newLast.getAttribute("id")); + } + + break; + } + } + if (currTab.is_in_view()) + newLast = currHeader; + } + } + } + + if (aDirection) + sidebarObj.panels.update(false); +} + +////////////////////////////////////////////////////////////// +// Sidebar Hacks and Work-arounds +////////////////////////////////////////////////////////////// + +// SidebarCleanUpExpandCollapse() - Respond to grippy click. +function SidebarCleanUpExpandCollapse() { + // XXX Mini hack. Persist isn't working too well. Force the persist, + // but wait until the change has commited. + if (gMustInit) { + gAboutToUncollapse = true; + sidebar_overlay_init(); + } + + setTimeout(Persist, 100, "sidebar-box", "collapsed"); + setTimeout(() => sidebarObj.panels.refresh(), 100); +} + +function PersistHeight() { + // XXX Mini hack. Persist isn't working too well. Force the persist, + // but wait until the last drag has been committed. + // May want to do something smarter here like only force it if the + // height has really changed. + setTimeout(Persist, 100, "sidebar-panels-splitter-box", "height"); +} + +function PersistWidth() { + // XXX Mini hack. Persist isn't working too well. Force the persist, + // but wait until the width change has commited. Also see bug 16516. + setTimeout(Persist, 100, "sidebar-box", "width"); + + var is_collapsed = document.getElementById("sidebar-box") + .getAttribute("collapsed") == "true"; + SidebarSetButtonOpen(!is_collapsed); +} + +function Persist(aAttribute, aValue) { + document.persist(aAttribute, aValue); +} + +function SidebarFinishClick() { + PersistWidth(); + + var is_collapsed = document.getElementById('sidebar-box').getAttribute('collapsed') == 'true'; + debug("collapsed: " + is_collapsed); + if (is_collapsed != sidebarObj.collapsed) { + if (gMustInit) + sidebar_overlay_init(); + } +} + +function SidebarSetButtonOpen(aSidebarNowOpen) +{ + // change state so toolbar icon can be updated + var pt = document.getElementById("PersonalToolbar"); + if (pt) { + pt.setAttribute("prefixopen", aSidebarNowOpen); + + // set tooltip for toolbar icon + var header = document.getElementById("sidebar-title-box"); + var tooltip = header.getAttribute(aSidebarNowOpen ? + "tooltipclose" : "tooltipopen"); + pt.setAttribute("prefixtooltip", tooltip); + } +} + +function SidebarInitContextMenu(aMenu, aPopupNode) +{ + var panel = sidebarObj.panels.get_panel_from_header_node(aPopupNode); + var switchItem = document.getElementById("switch-ctx-item"); + var reloadItem = document.getElementById("reload-ctx-item"); + var stopItem = document.getElementById("stop-ctx-item"); + + // the current panel can be reloaded, but other panels are not showing + // any content, so we only allow you to switch to other panels + if (panel.is_selected()) + { + switchItem.setAttribute("collapsed", "true"); + reloadItem.removeAttribute("disabled"); + } + else + { + switchItem.removeAttribute("collapsed"); + reloadItem.setAttribute("disabled", "true"); + } + + // only if a panel is currently loading enable the ``Stop'' item + if (panel.get_iframe().getAttribute("loadstate") == "loading") + stopItem.removeAttribute("disabled"); + else + stopItem.setAttribute("disabled", "true"); +} + +/////////////////////////////////////////////////////////////// +// Handy Debug Tools +////////////////////////////////////////////////////////////// +var debug = null; +var dump_attributes = null; +var dump_tree = null; +if (!SB_DEBUG) { + debug = function (s) {}; + dump_attributes = function (node, depth) {}; + dump_tree = function (node) {}; + var _dump_tree_recur = function (node, depth, index) {}; +} else { + debug = function (s) { dump("-*- sbOverlay: " + s + "\n"); }; + + dump_attributes = function (node, depth) { + var attributes = node.attributes; + var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | . "; + + if (!attributes || attributes.length == 0) { + debug(indent.substr(indent.length - depth*2) + "no attributes"); + } + for (var ii=0; ii < attributes.length; ii++) { + var attr = attributes.item(ii); + debug(indent.substr(indent.length - depth*2) + attr.name + + "=" + attr.value); + } + } + dump_tree = function (node) { + _dump_tree_recur(node, 0, 0); + } + _dump_tree_recur = function (node, depth, index) { + if (!node) { + debug("dump_tree: node is null"); + } + var indent = "| | | | | | | | | | | | | | | | | | | | | | | | | | | | + "; + debug(indent.substr(indent.length - depth*2) + index + + " " + node.nodeName); + if (node.nodeType != Node.TEXT_NODE) { + dump_attributes(node, depth); + } + var kids = node.childNodes; + for (var ii=0; ii < kids.length; ii++) { + _dump_tree_recur(kids[ii], depth + 1, ii); + } + } +} + +function SidebarBroadcastersToRDF() +{ + // Only the broadcasters in browser are synced to panels.rdf + if (sidebarObj.component != "navigator:browser") + return; + + // Translation rules to translate between new broadcaster id and old RDF id. + const TRANSLATE = {viewBookmarksSidebar: "bookmarks", + viewHistorySidebar: "history", + viewSearchSidebar: "search", + viewAddressbookSidebar: "addressbook"}; + const URN_PREFIX = "urn:sidebar:panel:"; + + const RDFCU = Cc['@mozilla.org/rdf/container-utils;1'] + .getService(Ci.nsIRDFContainerUtils); + + /* + * Initialize RDF stuff. + */ + let ds = sidebarObj.datasource; + let panelListRes = RDF.GetResource(NC + "panel-list"); + + let currentListRes = RDF.GetResource(sidebarObj.resource); + let masterListRes = RDF.GetResource(sidebarObj.master_resource); + let currentTarget = ds.GetTarget(currentListRes, panelListRes, true); + let masterTarget = ds.GetTarget(masterListRes, panelListRes, true); + if (!masterTarget) { + // No "master-panel-list" found, so create it. + masterTarget = RDF.GetAnonymousResource(); + ds.Assert(masterListRes, panelListRes, masterTarget, true); + } + let currentSeq = RDFCU.MakeSeq(ds, currentTarget); + let masterSeq = RDFCU.MakeSeq(ds, masterTarget); + + /* + * Run over broadcasters in browser window and add/update RDF entries + * based on them. + */ + let titleRes = RDF.GetResource(NC + "title"); + let urlRes = RDF.GetResource(NC + "content"); + + let bset = document.getElementById("mainBroadcasterSet"); + let broadcasters = bset.getElementsByTagName("broadcaster"); + let bclist = {}; + for (let bId = 0; bId < broadcasters.length; bId++) { + let curBC = broadcasters[bId]; + let title = curBC.getAttribute("sidebartitle") || curBC.getAttribute("label"); + let url = curBC.getAttribute("sidebarurl"); + let bcid = (curBC.id in TRANSLATE) ? TRANSLATE[curBC.id] : curBC.id; + + if (!url || !title || !bcid) + continue; + + // This one is needed later to check for obsolete sidebars. + bclist[bcid] = 1; + + let panelRes = RDF.GetResource(URN_PREFIX + bcid); + + // Literals of values that should be in RDF. + let titleLit = RDF.GetLiteral(title); + let urlLit = RDF.GetLiteral(url); + // Literals of values that are in RDF. + let curtitleLit = ds.GetTarget(panelRes, titleRes, true); + let cururlLit = ds.GetTarget(panelRes, urlRes, true); + + // If the item doesn't already exist, create it. + if (!curtitleLit && !cururlLit) { + ds.Assert(panelRes, titleRes, titleLit, true); + ds.Assert(panelRes, urlRes, urlLit, true); + masterSeq.AppendElement(panelRes); + if (currentSeq.IndexOf(panelRes) == -1) + currentSeq.AppendElement(panelRes); + } + // Item already exists, but perhaps we need to update... + else { + let curtitle = curtitleLit.QueryInterface(Ci.nsIRDFLiteral).Value; + let cururl = cururlLit.QueryInterface(Ci.nsIRDFLiteral).Value; + + if (curtitle != title) + ds.Change(panelRes, titleRes, curtitleLit, titleLit); + + if (cururl != url) + ds.Change(panelRes, urlRes, cururlLit, urlLit); + } + } + + /* + * Do the same the other way around to delete obsolete sidebars. + */ + + let masterElements = masterSeq.GetElements(); + while (masterElements.hasMoreElements()) { + let curElementRes = masterElements.getNext(); + let curId = curElementRes.QueryInterface(Ci.nsIRDFResource).Value; + + if (curId.substr(0, URN_PREFIX.length) != URN_PREFIX) + continue; + + curId = curId.substr(URN_PREFIX.length); + if (!(curId in bclist)) { + let properties = ds.ArcLabelsOut(curElementRes); + while(properties.hasMoreElements()) { + let propertyRes = properties.getNext(); + let valueLit = ds.GetTarget(curElementRes, propertyRes, true); + ds.Unassert(curElementRes, propertyRes, valueLit); + } + masterSeq.RemoveElement(curElementRes, true); + if (currentSeq.IndexOf(curElementRes) != -1) + currentSeq.RemoveElement(curElementRes, true); + } + } + + // Write modified data. + sidebarObj.datasource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); +} + + +////////////////////////////////////////////////////////////// +// Install the load/unload handlers +////////////////////////////////////////////////////////////// +addEventListener("load", sidebar_overlay_init, false); +addEventListener("unload", sidebar_overlay_destruct, false); |