2012-07-11 18:31:19 -07:00
|
|
|
// 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/.
|
|
|
|
|
|
|
|
let SocialUI = {
|
|
|
|
// Called on delayed startup to initialize UI
|
|
|
|
init: function SocialUI_init() {
|
|
|
|
Services.obs.addObserver(this, "social:pref-changed", false);
|
2012-07-15 16:12:13 -07:00
|
|
|
Services.obs.addObserver(this, "social:ambient-notification-changed", false);
|
|
|
|
Services.obs.addObserver(this, "social:profile-changed", false);
|
|
|
|
|
2012-07-18 11:40:05 -07:00
|
|
|
Services.prefs.addObserver("social.sidebar.open", this, false);
|
2012-09-26 17:40:18 -07:00
|
|
|
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
2012-07-18 11:40:05 -07:00
|
|
|
|
2012-06-22 15:01:34 -07:00
|
|
|
gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
|
|
|
|
|
2012-07-11 18:31:19 -07:00
|
|
|
Social.init(this._providerReady.bind(this));
|
|
|
|
},
|
|
|
|
|
|
|
|
// Called on window unload
|
|
|
|
uninit: function SocialUI_uninit() {
|
|
|
|
Services.obs.removeObserver(this, "social:pref-changed");
|
2012-07-15 16:12:13 -07:00
|
|
|
Services.obs.removeObserver(this, "social:ambient-notification-changed");
|
|
|
|
Services.obs.removeObserver(this, "social:profile-changed");
|
2012-07-18 11:40:05 -07:00
|
|
|
|
|
|
|
Services.prefs.removeObserver("social.sidebar.open", this);
|
2012-09-26 17:40:18 -07:00
|
|
|
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
2012-07-15 16:12:13 -07:00
|
|
|
showProfile: function SocialUI_showProfile() {
|
|
|
|
if (Social.provider)
|
2012-08-31 12:57:42 -07:00
|
|
|
openUILinkIn(Social.provider.profile.profileURL, "tab");
|
2012-07-15 16:12:13 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
observe: function SocialUI_observe(subject, topic, data) {
|
|
|
|
switch (topic) {
|
|
|
|
case "social:pref-changed":
|
2012-08-20 14:18:50 -07:00
|
|
|
// Exceptions here sometimes don't get reported properly, report them
|
|
|
|
// manually :(
|
|
|
|
try {
|
|
|
|
this.updateToggleCommand();
|
|
|
|
SocialShareButton.updateButtonHiddenState();
|
|
|
|
SocialToolbar.updateButtonHiddenState();
|
|
|
|
SocialSidebar.updateSidebar();
|
2012-08-20 17:52:26 -07:00
|
|
|
SocialChatBar.update();
|
2012-08-23 17:11:02 -07:00
|
|
|
SocialFlyout.unload();
|
2012-08-20 14:18:50 -07:00
|
|
|
} catch (e) {
|
|
|
|
Components.utils.reportError(e);
|
|
|
|
throw e;
|
|
|
|
}
|
2012-07-15 16:12:13 -07:00
|
|
|
break;
|
|
|
|
case "social:ambient-notification-changed":
|
|
|
|
SocialToolbar.updateButton();
|
|
|
|
break;
|
|
|
|
case "social:profile-changed":
|
|
|
|
SocialToolbar.updateProfile();
|
2012-07-25 10:35:57 -07:00
|
|
|
SocialShareButton.updateProfileInfo();
|
2012-09-23 16:49:28 -07:00
|
|
|
SocialChatBar.update();
|
2012-07-15 16:12:13 -07:00
|
|
|
break;
|
2012-07-18 11:40:05 -07:00
|
|
|
case "nsPref:changed":
|
|
|
|
SocialSidebar.updateSidebar();
|
2012-09-26 17:40:18 -07:00
|
|
|
SocialToolbar.updateButton();
|
2012-07-15 16:12:13 -07:00
|
|
|
}
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
2012-07-22 20:49:28 -07:00
|
|
|
get toggleCommand() {
|
|
|
|
return document.getElementById("Social:Toggle");
|
|
|
|
},
|
|
|
|
|
2012-07-11 18:31:19 -07:00
|
|
|
// Called once Social.jsm's provider has been set
|
|
|
|
_providerReady: function SocialUI_providerReady() {
|
2012-06-22 15:01:34 -07:00
|
|
|
// If we couldn't find a provider, nothing to do here.
|
|
|
|
if (!Social.provider)
|
|
|
|
return;
|
|
|
|
|
2012-07-22 20:49:28 -07:00
|
|
|
this.updateToggleCommand();
|
|
|
|
|
|
|
|
let toggleCommand = this.toggleCommand;
|
2012-08-24 14:46:11 -07:00
|
|
|
let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
|
|
|
|
let label = gNavigatorBundle.getFormattedString("social.toggle.label",
|
|
|
|
[Social.provider.name,
|
|
|
|
brandShortName]);
|
|
|
|
let accesskey = gNavigatorBundle.getString("social.toggle.accesskey");
|
2012-07-22 20:49:28 -07:00
|
|
|
toggleCommand.setAttribute("label", label);
|
|
|
|
toggleCommand.setAttribute("accesskey", accesskey);
|
|
|
|
|
2012-07-15 16:12:13 -07:00
|
|
|
SocialToolbar.init();
|
2012-07-11 18:31:19 -07:00
|
|
|
SocialShareButton.init();
|
2012-07-18 11:40:05 -07:00
|
|
|
SocialSidebar.init();
|
2012-06-22 15:01:34 -07:00
|
|
|
},
|
|
|
|
|
2012-07-22 20:49:28 -07:00
|
|
|
updateToggleCommand: function SocialUI_updateToggleCommand() {
|
|
|
|
let toggleCommand = this.toggleCommand;
|
2012-09-24 14:57:12 -07:00
|
|
|
toggleCommand.setAttribute("checked", Services.prefs.getBoolPref("social.enabled"));
|
2012-07-22 20:49:28 -07:00
|
|
|
|
|
|
|
// FIXME: bug 772808: menu items don't inherit the "hidden" state properly,
|
|
|
|
// need to update them manually.
|
|
|
|
// This should just be: toggleCommand.hidden = !Social.active;
|
|
|
|
for (let id of ["appmenu_socialToggle", "menu_socialToggle"]) {
|
|
|
|
let el = document.getElementById(id);
|
|
|
|
if (!el)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (Social.active)
|
|
|
|
el.removeAttribute("hidden");
|
|
|
|
else
|
|
|
|
el.setAttribute("hidden", "true");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-06-22 15:01:34 -07:00
|
|
|
// This handles "ActivateSocialFeature" events fired against content documents
|
|
|
|
// in this window.
|
|
|
|
_activationEventHandler: function SocialUI_activationHandler(e) {
|
|
|
|
// Nothing to do if Social is already active, or we don't have a provider
|
|
|
|
// to enable yet.
|
|
|
|
if (Social.active || !Social.provider)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let targetDoc = e.target;
|
|
|
|
|
|
|
|
// Event must be fired against the document
|
|
|
|
if (!(targetDoc instanceof HTMLDocument))
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Ignore events fired in background tabs
|
|
|
|
if (targetDoc.defaultView.top != content)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Check that the associated document's origin is in our whitelist
|
|
|
|
let prePath = targetDoc.documentURIObject.prePath;
|
2012-08-06 18:00:59 -07:00
|
|
|
let whitelist = Services.prefs.getCharPref("social.activation.whitelist");
|
2012-06-22 15:01:34 -07:00
|
|
|
if (whitelist.split(",").indexOf(prePath) == -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// If the last event was received < 1s ago, ignore this one
|
|
|
|
let now = Date.now();
|
|
|
|
if (now - Social.lastEventReceived < 1000)
|
|
|
|
return;
|
|
|
|
Social.lastEventReceived = now;
|
|
|
|
|
|
|
|
// Enable the social functionality, and indicate that it was activated
|
|
|
|
Social.active = true;
|
|
|
|
|
|
|
|
// Show a warning, allow undoing the activation
|
|
|
|
let description = document.getElementById("social-activation-message");
|
|
|
|
let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
|
2012-08-24 14:46:11 -07:00
|
|
|
let message = gNavigatorBundle.getFormattedString("social.activated.description",
|
2012-06-22 15:01:34 -07:00
|
|
|
[Social.provider.name, brandShortName]);
|
|
|
|
description.value = message;
|
|
|
|
|
|
|
|
SocialUI.notificationPanel.hidden = false;
|
|
|
|
|
|
|
|
setTimeout(function () {
|
|
|
|
SocialUI.notificationPanel.openPopup(SocialToolbar.button, "bottomcenter topright");
|
|
|
|
}.bind(this), 0);
|
|
|
|
},
|
|
|
|
|
|
|
|
get notificationPanel() {
|
|
|
|
return document.getElementById("socialActivatedNotification")
|
|
|
|
},
|
|
|
|
|
|
|
|
undoActivation: function SocialUI_undoActivation() {
|
|
|
|
Social.active = false;
|
|
|
|
this.notificationPanel.hidePopup();
|
2012-09-23 16:49:28 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
haveLoggedInUser: function SocialUI_haveLoggedInUser() {
|
|
|
|
return !!(Social.provider && Social.provider.profile && Social.provider.profile.userName);
|
2012-07-11 18:31:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-20 17:52:26 -07:00
|
|
|
let SocialChatBar = {
|
|
|
|
get chatbar() {
|
|
|
|
return document.getElementById("pinnedchats");
|
|
|
|
},
|
|
|
|
// Whether the chats can be shown for this window.
|
|
|
|
get canShow() {
|
2012-09-23 16:49:28 -07:00
|
|
|
if (!SocialUI.haveLoggedInUser())
|
|
|
|
return false;
|
2012-08-20 17:52:26 -07:00
|
|
|
let docElem = document.documentElement;
|
|
|
|
let chromeless = docElem.getAttribute("disablechrome") ||
|
|
|
|
docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
|
|
|
|
return Social.uiVisible && !chromeless;
|
|
|
|
},
|
2012-08-26 16:51:24 -07:00
|
|
|
openChat: function(aProvider, aURL, aCallback, aMode) {
|
2012-08-20 17:52:26 -07:00
|
|
|
if (this.canShow)
|
2012-08-26 16:51:24 -07:00
|
|
|
this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
|
2012-08-20 17:52:26 -07:00
|
|
|
},
|
|
|
|
update: function() {
|
|
|
|
if (!this.canShow)
|
|
|
|
this.chatbar.removeAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-23 17:11:02 -07:00
|
|
|
function sizeSocialPanelToContent(iframe) {
|
|
|
|
// FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
|
|
|
|
let doc = iframe.contentDocument;
|
|
|
|
if (!doc) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// "notif" is an implementation detail that we should get rid of
|
|
|
|
// eventually
|
|
|
|
let body = doc.getElementById("notif") || doc.body;
|
2012-09-25 23:22:38 -07:00
|
|
|
if (!body) {
|
2012-08-23 17:11:02 -07:00
|
|
|
return;
|
|
|
|
}
|
2012-09-25 23:22:38 -07:00
|
|
|
// XXX - do we want a max for width and height here?
|
|
|
|
// The 300 and 330 defaults also seem arbitrary, so should be revisited.
|
|
|
|
// BUT - for at least one provider, the scrollWidth/offsetWidth/css width
|
|
|
|
// isn't set appropriately, so the 330 is "fixed" for now...
|
|
|
|
iframe.style.width = "330px";
|
|
|
|
// offsetHeight doesn't include margins, so account for that.
|
|
|
|
let cs = doc.defaultView.getComputedStyle(body);
|
|
|
|
let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
|
|
|
|
let height = computedHeight || 300;
|
2012-08-23 17:11:02 -07:00
|
|
|
iframe.style.height = height + "px";
|
|
|
|
}
|
|
|
|
|
2012-09-25 23:22:38 -07:00
|
|
|
function setupDynamicPanelResizer(iframe) {
|
|
|
|
let doc = iframe.contentDocument;
|
|
|
|
let mo = new iframe.contentWindow.MutationObserver(function(mutations) {
|
|
|
|
sizeSocialPanelToContent(iframe);
|
|
|
|
});
|
|
|
|
// Observe anything that causes the size to change.
|
|
|
|
let config = {attributes: true, characterData: true, childList: true, subtree: true};
|
|
|
|
mo.observe(doc, config);
|
|
|
|
doc.addEventListener("unload", function() {
|
|
|
|
if (mo) {
|
|
|
|
mo.disconnect();
|
|
|
|
mo = null;
|
|
|
|
}
|
|
|
|
}, false);
|
|
|
|
sizeSocialPanelToContent(iframe);
|
|
|
|
}
|
|
|
|
|
2012-08-23 17:11:02 -07:00
|
|
|
let SocialFlyout = {
|
|
|
|
get panel() {
|
|
|
|
return document.getElementById("social-flyout-panel");
|
|
|
|
},
|
|
|
|
|
|
|
|
dispatchPanelEvent: function(name) {
|
|
|
|
let doc = this.panel.firstChild.contentDocument;
|
|
|
|
let evt = doc.createEvent("CustomEvent");
|
|
|
|
evt.initCustomEvent(name, true, true, {});
|
|
|
|
doc.documentElement.dispatchEvent(evt);
|
|
|
|
},
|
|
|
|
|
|
|
|
_createFrame: function() {
|
|
|
|
let panel = this.panel;
|
|
|
|
if (!Social.provider || panel.firstChild)
|
|
|
|
return;
|
|
|
|
// create and initialize the panel for this window
|
|
|
|
let iframe = document.createElement("iframe");
|
|
|
|
iframe.setAttribute("type", "content");
|
2012-09-06 16:13:37 -07:00
|
|
|
iframe.setAttribute("class", "social-panel-frame");
|
2012-08-23 17:11:02 -07:00
|
|
|
iframe.setAttribute("flex", "1");
|
|
|
|
iframe.setAttribute("origin", Social.provider.origin);
|
|
|
|
panel.appendChild(iframe);
|
|
|
|
},
|
|
|
|
|
|
|
|
unload: function() {
|
|
|
|
let panel = this.panel;
|
2012-09-25 23:23:25 -07:00
|
|
|
panel.hidePopup();
|
2012-08-23 17:11:02 -07:00
|
|
|
if (!panel.firstChild)
|
|
|
|
return
|
|
|
|
panel.removeChild(panel.firstChild);
|
|
|
|
},
|
|
|
|
|
|
|
|
onShown: function(aEvent) {
|
|
|
|
let iframe = this.panel.firstChild;
|
|
|
|
iframe.docShell.isActive = true;
|
|
|
|
iframe.docShell.isAppTab = true;
|
|
|
|
if (iframe.contentDocument.readyState == "complete") {
|
|
|
|
this.dispatchPanelEvent("socialFrameShow");
|
|
|
|
} else {
|
|
|
|
// first time load, wait for load and dispatch after load
|
|
|
|
iframe.addEventListener("load", function panelBrowserOnload(e) {
|
|
|
|
iframe.removeEventListener("load", panelBrowserOnload, true);
|
|
|
|
setTimeout(function() {
|
|
|
|
SocialFlyout.dispatchPanelEvent("socialFrameShow");
|
|
|
|
}, 0);
|
|
|
|
}, true);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
onHidden: function(aEvent) {
|
|
|
|
this.panel.firstChild.docShell.isActive = false;
|
|
|
|
this.dispatchPanelEvent("socialFrameHide");
|
|
|
|
},
|
|
|
|
|
|
|
|
open: function(aURL, yOffset, aCallback) {
|
|
|
|
if (!Social.provider)
|
|
|
|
return;
|
|
|
|
let panel = this.panel;
|
|
|
|
if (!panel.firstChild)
|
|
|
|
this._createFrame();
|
|
|
|
panel.hidden = false;
|
|
|
|
let iframe = panel.firstChild;
|
|
|
|
|
|
|
|
let src = iframe.getAttribute("src");
|
|
|
|
if (src != aURL) {
|
|
|
|
iframe.addEventListener("load", function documentLoaded() {
|
|
|
|
iframe.removeEventListener("load", documentLoaded, true);
|
2012-09-25 23:22:38 -07:00
|
|
|
setupDynamicPanelResizer(iframe);
|
2012-08-23 17:11:02 -07:00
|
|
|
if (aCallback) {
|
|
|
|
try {
|
|
|
|
aCallback(iframe.contentWindow);
|
|
|
|
} catch(e) {
|
|
|
|
Cu.reportError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, true);
|
|
|
|
iframe.setAttribute("src", aURL);
|
|
|
|
}
|
|
|
|
else if (aCallback) {
|
|
|
|
try {
|
|
|
|
aCallback(iframe.contentWindow);
|
|
|
|
} catch(e) {
|
|
|
|
Cu.reportError(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sizeSocialPanelToContent(iframe);
|
|
|
|
let anchor = document.getElementById("social-sidebar-browser");
|
2012-09-20 22:18:48 -07:00
|
|
|
if (panel.state == "open") {
|
|
|
|
// this is painful - there is no way to say "move to a new anchor offset",
|
|
|
|
// only "move to new screen pos". So we remember the last yOffset,
|
|
|
|
// calculate the adjustment needed to the new yOffset, then calc the
|
|
|
|
// screen Y position.
|
|
|
|
let yAdjust = yOffset - this.yOffset;
|
|
|
|
let box = panel.boxObject;
|
|
|
|
panel.moveTo(box.screenX, box.screenY + yAdjust);
|
|
|
|
} else {
|
|
|
|
panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
|
|
|
|
}
|
|
|
|
this.yOffset = yOffset;
|
2012-08-23 17:11:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-11 18:31:19 -07:00
|
|
|
let SocialShareButton = {
|
2012-08-27 04:16:38 -07:00
|
|
|
// promptImages and promptMessages being null means we are yet to get the
|
|
|
|
// message back from the provider with the images and icons (or that we got
|
|
|
|
// the response but determined it was invalid.)
|
|
|
|
promptImages: null,
|
|
|
|
promptMessages: null,
|
|
|
|
|
2012-07-18 11:40:05 -07:00
|
|
|
// Called once, after window load, when the Social.provider object is initialized
|
2012-07-11 18:31:19 -07:00
|
|
|
init: function SSB_init() {
|
2012-07-15 16:12:13 -07:00
|
|
|
this.updateButtonHiddenState();
|
2012-07-25 10:35:57 -07:00
|
|
|
this.updateProfileInfo();
|
|
|
|
},
|
2012-07-17 10:47:04 -07:00
|
|
|
|
2012-07-25 10:35:57 -07:00
|
|
|
updateProfileInfo: function SSB_updateProfileInfo() {
|
2012-09-24 20:54:34 -07:00
|
|
|
let profileRow = document.getElementById("unsharePopupHeader");
|
2012-07-17 10:47:04 -07:00
|
|
|
let profile = Social.provider.profile;
|
2012-09-06 13:47:01 -07:00
|
|
|
this.promptImages = null;
|
|
|
|
this.promptMessages = null;
|
2012-08-02 15:30:19 -07:00
|
|
|
if (profile && profile.displayName) {
|
2012-07-17 10:47:04 -07:00
|
|
|
profileRow.hidden = false;
|
|
|
|
let portrait = document.getElementById("socialUserPortrait");
|
2012-09-25 11:39:09 -07:00
|
|
|
portrait.setAttribute("src", profile.portrait || "chrome://global/skin/icons/information-32.png");
|
2012-07-17 10:47:04 -07:00
|
|
|
let displayName = document.getElementById("socialUserDisplayName");
|
|
|
|
displayName.setAttribute("label", profile.displayName);
|
|
|
|
} else {
|
|
|
|
profileRow.hidden = true;
|
2012-09-06 13:47:01 -07:00
|
|
|
this.updateButtonHiddenState();
|
|
|
|
return;
|
2012-07-17 10:47:04 -07:00
|
|
|
}
|
2012-08-27 04:16:38 -07:00
|
|
|
// XXX - this shouldn't be done as part of updateProfileInfo, but instead
|
|
|
|
// whenever we notice the provider has changed - but the concept of
|
|
|
|
// "provider changed" will only exist once bug 774520 lands.
|
|
|
|
// get the recommend-prompt info.
|
2012-09-11 19:48:38 -07:00
|
|
|
let port = Social.provider.getWorkerPort();
|
2012-08-27 04:16:38 -07:00
|
|
|
if (port) {
|
|
|
|
port.onmessage = function(evt) {
|
|
|
|
if (evt.data.topic == "social.user-recommend-prompt-response") {
|
|
|
|
port.close();
|
|
|
|
this.acceptRecommendInfo(evt.data.data);
|
|
|
|
this.updateButtonHiddenState();
|
|
|
|
this.updateShareState();
|
|
|
|
}
|
|
|
|
}.bind(this);
|
|
|
|
port.postMessage({topic: "social.user-recommend-prompt"});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
acceptRecommendInfo: function SSB_acceptRecommendInfo(data) {
|
|
|
|
// Accept *and validate* the user-recommend-prompt-response message.
|
|
|
|
let promptImages = {};
|
|
|
|
let promptMessages = {};
|
|
|
|
function reportError(reason) {
|
|
|
|
Cu.reportError("Invalid recommend data from provider: " + reason + ": sharing is disabled for this provider");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!data ||
|
|
|
|
!data.images || typeof data.images != "object" ||
|
|
|
|
!data.messages || typeof data.messages != "object") {
|
|
|
|
return reportError("data is missing valid 'images' or 'messages' elements");
|
|
|
|
}
|
|
|
|
for (let sub of ["share", "unshare"]) {
|
|
|
|
let url = data.images[sub];
|
|
|
|
if (!url || typeof url != "string" || url.length == 0) {
|
|
|
|
return reportError('images["' + sub + '"] is missing or not a non-empty string');
|
|
|
|
}
|
|
|
|
// resolve potentially relative URLs then check the scheme is acceptable.
|
|
|
|
url = Services.io.newURI(Social.provider.origin, null, null).resolve(url);
|
|
|
|
let uri = Services.io.newURI(url, null, null);
|
|
|
|
if (!uri.schemeIs("http") && !uri.schemeIs("https") && !uri.schemeIs("data")) {
|
|
|
|
return reportError('images["' + sub + '"] does not have a valid scheme');
|
|
|
|
}
|
|
|
|
promptImages[sub] = url;
|
|
|
|
}
|
2012-09-24 20:54:34 -07:00
|
|
|
for (let sub of ["shareTooltip", "unshareTooltip",
|
|
|
|
"sharedLabel", "unsharedLabel", "unshareLabel",
|
|
|
|
"portraitLabel",
|
|
|
|
"unshareConfirmLabel", "unshareConfirmAccessKey",
|
|
|
|
"unshareCancelLabel", "unshareCancelAccessKey"]) {
|
2012-08-27 04:16:38 -07:00
|
|
|
if (typeof data.messages[sub] != "string" || data.messages[sub].length == 0) {
|
|
|
|
return reportError('messages["' + sub + '"] is not a valid string');
|
|
|
|
}
|
|
|
|
promptMessages[sub] = data.messages[sub];
|
|
|
|
}
|
|
|
|
this.promptImages = promptImages;
|
|
|
|
this.promptMessages = promptMessages;
|
|
|
|
return true;
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
get shareButton() {
|
|
|
|
return document.getElementById("share-button");
|
|
|
|
},
|
2012-09-24 20:54:34 -07:00
|
|
|
get unsharePopup() {
|
|
|
|
return document.getElementById("unsharePopup");
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
2012-09-24 20:54:34 -07:00
|
|
|
dismissUnsharePopup: function SSB_dismissUnsharePopup() {
|
|
|
|
this.unsharePopup.hidePopup();
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
2012-07-15 16:12:13 -07:00
|
|
|
updateButtonHiddenState: function SSB_updateButtonHiddenState() {
|
2012-07-11 18:31:19 -07:00
|
|
|
let shareButton = this.shareButton;
|
|
|
|
if (shareButton)
|
2012-09-06 13:47:01 -07:00
|
|
|
shareButton.hidden = !Social.uiVisible || this.promptImages == null ||
|
2012-09-23 16:49:28 -07:00
|
|
|
!SocialUI.haveLoggedInUser();
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
onClick: function SSB_onClick(aEvent) {
|
|
|
|
if (aEvent.button != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't bubble to the textbox, to avoid unwanted selection of the address.
|
|
|
|
aEvent.stopPropagation();
|
|
|
|
|
|
|
|
this.sharePage();
|
|
|
|
},
|
|
|
|
|
|
|
|
panelShown: function SSB_panelShown(aEvent) {
|
2012-09-24 20:54:34 -07:00
|
|
|
function updateElement(id, attrs) {
|
|
|
|
let el = document.getElementById(id);
|
|
|
|
Object.keys(attrs).forEach(function(attr) {
|
|
|
|
el.setAttribute(attr, attrs[attr]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
let continueSharingButton = document.getElementById("unsharePopupContinueSharingButton");
|
|
|
|
continueSharingButton.focus();
|
|
|
|
updateElement("unsharePopupContinueSharingButton",
|
|
|
|
{label: this.promptMessages.unshareCancelLabel,
|
|
|
|
accesskey: this.promptMessages.unshareCancelAccessKey});
|
|
|
|
updateElement("unsharePopupStopSharingButton",
|
|
|
|
{label: this.promptMessages.unshareConfirmLabel,
|
|
|
|
accesskey: this.promptMessages.unshareConfirmAccessKey});
|
|
|
|
updateElement("socialUserPortrait",
|
|
|
|
{"aria-label": this.promptMessages.portraitLabel});
|
|
|
|
updateElement("socialUserRecommendedText",
|
|
|
|
{value: this.promptMessages.unshareLabel});
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
sharePage: function SSB_sharePage() {
|
2012-09-24 20:54:34 -07:00
|
|
|
this.unsharePopup.hidden = false;
|
2012-07-18 11:40:05 -07:00
|
|
|
|
2012-07-11 18:31:19 -07:00
|
|
|
let uri = gBrowser.currentURI;
|
|
|
|
if (!Social.isPageShared(uri)) {
|
|
|
|
Social.sharePage(uri);
|
|
|
|
this.updateShareState();
|
|
|
|
} else {
|
2012-09-24 20:54:34 -07:00
|
|
|
this.unsharePopup.openPopup(this.shareButton, "bottomcenter topright");
|
2012-07-11 18:31:19 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
unsharePage: function SSB_unsharePage() {
|
|
|
|
Social.unsharePage(gBrowser.currentURI);
|
|
|
|
this.updateShareState();
|
2012-09-24 20:54:34 -07:00
|
|
|
this.dismissUnsharePopup();
|
2012-07-11 18:31:19 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
updateShareState: function SSB_updateShareState() {
|
|
|
|
let currentPageShared = Social.isPageShared(gBrowser.currentURI);
|
|
|
|
|
|
|
|
// Provide a11y-friendly notification of share.
|
|
|
|
let status = document.getElementById("share-button-status");
|
|
|
|
if (status) {
|
2012-08-27 04:16:38 -07:00
|
|
|
// XXX - this should also be capable of reflecting that the page was
|
|
|
|
// unshared (ie, it needs to manage three-states: (1) nothing done, (2)
|
|
|
|
// shared, (3) shared then unshared)
|
|
|
|
// Note that we *do* have an appropriate string from the provider for
|
|
|
|
// this (promptMessages['unsharedLabel'] but currently lack a way of
|
|
|
|
// tracking this state)
|
2012-07-11 18:31:19 -07:00
|
|
|
let statusString = currentPageShared ?
|
2012-08-27 04:16:38 -07:00
|
|
|
this.promptMessages['sharedLabel'] : "";
|
2012-07-11 18:31:19 -07:00
|
|
|
status.setAttribute("value", statusString);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the share button, if present
|
|
|
|
let shareButton = this.shareButton;
|
2012-08-27 04:16:38 -07:00
|
|
|
if (!shareButton || shareButton.hidden)
|
2012-07-11 18:31:19 -07:00
|
|
|
return;
|
|
|
|
|
2012-08-27 04:16:38 -07:00
|
|
|
let imageURL;
|
2012-07-11 18:31:19 -07:00
|
|
|
if (currentPageShared) {
|
|
|
|
shareButton.setAttribute("shared", "true");
|
2012-08-27 04:16:38 -07:00
|
|
|
shareButton.setAttribute("tooltiptext", this.promptMessages['unshareTooltip']);
|
|
|
|
imageURL = this.promptImages["unshare"]
|
2012-07-11 18:31:19 -07:00
|
|
|
} else {
|
|
|
|
shareButton.removeAttribute("shared");
|
2012-08-27 04:16:38 -07:00
|
|
|
shareButton.setAttribute("tooltiptext", this.promptMessages['shareTooltip']);
|
|
|
|
imageURL = this.promptImages["share"]
|
2012-07-11 18:31:19 -07:00
|
|
|
}
|
2012-08-27 04:16:38 -07:00
|
|
|
shareButton.style.backgroundImage = 'url("' + encodeURI(imageURL) + '")';
|
2012-07-11 18:31:19 -07:00
|
|
|
}
|
|
|
|
};
|
2012-07-15 16:12:13 -07:00
|
|
|
|
|
|
|
var SocialToolbar = {
|
|
|
|
// Called once, after window load, when the Social.provider object is initialized
|
|
|
|
init: function SocialToolbar_init() {
|
2012-09-28 12:41:08 -07:00
|
|
|
document.getElementById("social-provider-image").setAttribute("image", Social.provider.iconURL);
|
|
|
|
|
|
|
|
let statusAreaPopup = document.getElementById("social-statusarea-popup");
|
|
|
|
statusAreaPopup.addEventListener("popupshown", function(e) {
|
|
|
|
this.button.setAttribute("open", "true");
|
|
|
|
}.bind(this));
|
|
|
|
statusAreaPopup.addEventListener("popuphidden", function(e) {
|
|
|
|
this.button.removeAttribute("open");
|
|
|
|
}.bind(this));
|
|
|
|
|
2012-07-15 16:12:13 -07:00
|
|
|
this.updateButton();
|
|
|
|
this.updateProfile();
|
|
|
|
},
|
|
|
|
|
2012-09-28 12:41:08 -07:00
|
|
|
get button() {
|
|
|
|
return document.getElementById("social-toolbar-button");
|
|
|
|
},
|
|
|
|
|
2012-07-15 16:12:13 -07:00
|
|
|
updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
|
2012-09-28 12:41:08 -07:00
|
|
|
this.button.hidden = !Social.uiVisible;
|
2012-09-23 16:49:28 -07:00
|
|
|
if (!SocialUI.haveLoggedInUser()) {
|
2012-09-28 12:41:08 -07:00
|
|
|
["social-notification-box",
|
|
|
|
"social-status-iconbox"].forEach(function removeChildren(parentId) {
|
|
|
|
let parent = document.getElementById(parentId);
|
|
|
|
while(parent.hasChildNodes())
|
|
|
|
parent.removeChild(parent.firstChild);
|
|
|
|
});
|
2012-08-16 18:02:23 -07:00
|
|
|
}
|
2012-07-15 16:12:13 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
updateProfile: function SocialToolbar_updateProfile() {
|
|
|
|
// Profile may not have been initialized yet, since it depends on a worker
|
|
|
|
// response. In that case we'll be called again when it's available, via
|
|
|
|
// social:profile-changed
|
|
|
|
let profile = Social.provider.profile || {};
|
2012-09-25 11:39:09 -07:00
|
|
|
let userPortrait = profile.portrait || "chrome://global/skin/icons/information-32.png";
|
2012-07-15 16:12:13 -07:00
|
|
|
document.getElementById("social-statusarea-user-portrait").setAttribute("src", userPortrait);
|
|
|
|
|
|
|
|
let notLoggedInLabel = document.getElementById("social-statusarea-notloggedin");
|
|
|
|
let userNameBtn = document.getElementById("social-statusarea-username");
|
|
|
|
if (profile.userName) {
|
|
|
|
notLoggedInLabel.hidden = true;
|
|
|
|
userNameBtn.hidden = false;
|
|
|
|
userNameBtn.label = profile.userName;
|
|
|
|
} else {
|
|
|
|
notLoggedInLabel.hidden = false;
|
|
|
|
userNameBtn.hidden = true;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
updateButton: function SocialToolbar_updateButton() {
|
|
|
|
this.updateButtonHiddenState();
|
|
|
|
let provider = Social.provider;
|
|
|
|
let iconNames = Object.keys(provider.ambientNotificationIcons);
|
2012-09-28 12:41:08 -07:00
|
|
|
let iconBox = document.getElementById("social-status-iconbox");
|
2012-08-16 18:02:23 -07:00
|
|
|
let notifBox = document.getElementById("social-notification-box");
|
2012-08-23 11:23:17 -07:00
|
|
|
let panel = document.getElementById("social-notification-panel");
|
|
|
|
panel.hidden = false;
|
2012-08-22 13:56:14 -07:00
|
|
|
let notificationFrames = document.createDocumentFragment();
|
2012-08-16 18:02:23 -07:00
|
|
|
let iconContainers = document.createDocumentFragment();
|
|
|
|
|
2012-09-26 17:40:18 -07:00
|
|
|
let command = document.getElementById("Social:ToggleNotifications");
|
|
|
|
command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
|
|
|
|
|
2012-08-16 18:02:23 -07:00
|
|
|
for each(let name in iconNames) {
|
|
|
|
let icon = provider.ambientNotificationIcons[name];
|
|
|
|
|
2012-08-22 13:56:14 -07:00
|
|
|
let notificationFrameId = "social-status-" + icon.name;
|
|
|
|
let notificationFrame = document.getElementById(notificationFrameId);
|
|
|
|
if (!notificationFrame) {
|
|
|
|
notificationFrame = document.createElement("iframe");
|
|
|
|
notificationFrame.setAttribute("type", "content");
|
2012-09-06 16:13:37 -07:00
|
|
|
notificationFrame.setAttribute("class", "social-panel-frame");
|
2012-08-22 13:56:14 -07:00
|
|
|
notificationFrame.setAttribute("id", notificationFrameId);
|
|
|
|
notificationFrame.setAttribute("mozbrowser", "true");
|
|
|
|
notificationFrames.appendChild(notificationFrame);
|
2012-07-15 16:12:13 -07:00
|
|
|
}
|
2012-08-22 13:56:14 -07:00
|
|
|
notificationFrame.setAttribute("origin", provider.origin);
|
|
|
|
if (notificationFrame.getAttribute("src") != icon.contentPanel)
|
|
|
|
notificationFrame.setAttribute("src", icon.contentPanel);
|
2012-08-16 18:02:23 -07:00
|
|
|
|
|
|
|
let iconId = "social-notification-icon-" + icon.name;
|
2012-09-28 12:41:08 -07:00
|
|
|
let iconContainer = document.getElementById(iconId);
|
|
|
|
let iconImage, iconCounter;
|
|
|
|
if (iconContainer) {
|
|
|
|
iconImage = iconContainer.getElementsByClassName("social-notification-icon-image")[0];
|
|
|
|
iconCounter = iconContainer.getElementsByClassName("social-notification-icon-counter")[0];
|
2012-08-16 18:02:23 -07:00
|
|
|
} else {
|
2012-09-28 12:41:08 -07:00
|
|
|
iconContainer = document.createElement("box");
|
|
|
|
iconContainer.setAttribute("id", iconId);
|
|
|
|
iconContainer.classList.add("social-notification-icon-container");
|
|
|
|
iconContainer.addEventListener("click", function (e) { SocialToolbar.showAmbientPopup(iconContainer); }, false);
|
|
|
|
|
|
|
|
iconImage = document.createElement("image");
|
|
|
|
iconImage.classList.add("social-notification-icon-image");
|
|
|
|
iconImage = iconContainer.appendChild(iconImage);
|
2012-08-16 18:02:23 -07:00
|
|
|
|
2012-09-28 12:41:08 -07:00
|
|
|
iconCounter = document.createElement("box");
|
|
|
|
iconCounter.classList.add("social-notification-icon-counter");
|
|
|
|
iconCounter.appendChild(document.createTextNode(""));
|
|
|
|
iconCounter = iconContainer.appendChild(iconCounter);
|
|
|
|
|
|
|
|
iconContainers.appendChild(iconContainer);
|
|
|
|
}
|
|
|
|
if (iconImage.getAttribute("src") != icon.iconURL)
|
|
|
|
iconImage.setAttribute("src", icon.iconURL);
|
|
|
|
iconImage.setAttribute("notificationFrameId", notificationFrameId);
|
2012-09-27 16:57:37 -07:00
|
|
|
|
2012-09-28 12:41:08 -07:00
|
|
|
iconCounter.collapsed = !icon.counter;
|
|
|
|
iconCounter.firstChild.textContent = icon.counter || "";
|
2012-08-16 18:02:23 -07:00
|
|
|
}
|
2012-08-22 13:56:14 -07:00
|
|
|
notifBox.appendChild(notificationFrames);
|
2012-08-16 18:02:23 -07:00
|
|
|
iconBox.appendChild(iconContainers);
|
2012-07-15 16:12:13 -07:00
|
|
|
},
|
|
|
|
|
2012-09-28 12:41:08 -07:00
|
|
|
showAmbientPopup: function SocialToolbar_showAmbientPopup(iconContainer) {
|
|
|
|
let iconImage = iconContainer.firstChild;
|
2012-07-15 16:12:13 -07:00
|
|
|
let panel = document.getElementById("social-notification-panel");
|
2012-08-16 18:02:23 -07:00
|
|
|
let notifBox = document.getElementById("social-notification-box");
|
2012-09-28 12:41:08 -07:00
|
|
|
let notificationFrame = document.getElementById(iconImage.getAttribute("notificationFrameId"));
|
2012-07-15 16:12:13 -07:00
|
|
|
|
2012-08-23 17:11:02 -07:00
|
|
|
// Clear dimensions on all browsers so the panel size will
|
|
|
|
// only use the selected browser.
|
|
|
|
let frameIter = notifBox.firstElementChild;
|
|
|
|
while (frameIter) {
|
|
|
|
frameIter.collapsed = (frameIter != notificationFrame);
|
|
|
|
frameIter = frameIter.nextElementSibling;
|
2012-07-15 16:12:13 -07:00
|
|
|
}
|
|
|
|
|
2012-08-20 11:25:47 -07:00
|
|
|
function dispatchPanelEvent(name) {
|
2012-08-22 13:56:14 -07:00
|
|
|
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
2012-08-20 11:25:47 -07:00
|
|
|
evt.initCustomEvent(name, true, true, {});
|
2012-08-22 13:56:14 -07:00
|
|
|
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
2012-08-20 11:25:47 -07:00
|
|
|
}
|
|
|
|
|
2012-08-23 17:10:07 -07:00
|
|
|
panel.addEventListener("popuphidden", function onpopuphiding() {
|
|
|
|
panel.removeEventListener("popuphidden", onpopuphiding);
|
2012-09-28 12:41:08 -07:00
|
|
|
SocialToolbar.button.removeAttribute("open");
|
2012-08-23 17:10:07 -07:00
|
|
|
notificationFrame.docShell.isActive = false;
|
2012-08-20 11:25:47 -07:00
|
|
|
dispatchPanelEvent("socialFrameHide");
|
|
|
|
});
|
|
|
|
|
|
|
|
panel.addEventListener("popupshown", function onpopupshown() {
|
|
|
|
panel.removeEventListener("popupshown", onpopupshown);
|
2012-09-28 12:41:08 -07:00
|
|
|
SocialToolbar.button.setAttribute("open", "true");
|
2012-08-23 17:10:07 -07:00
|
|
|
notificationFrame.docShell.isActive = true;
|
2012-08-22 13:56:14 -07:00
|
|
|
notificationFrame.docShell.isAppTab = true;
|
|
|
|
if (notificationFrame.contentDocument.readyState == "complete") {
|
2012-09-25 23:22:38 -07:00
|
|
|
setupDynamicPanelResizer(notificationFrame);
|
2012-08-20 11:25:47 -07:00
|
|
|
dispatchPanelEvent("socialFrameShow");
|
|
|
|
} else {
|
|
|
|
// first time load, wait for load and dispatch after load
|
2012-08-22 13:56:14 -07:00
|
|
|
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
|
|
|
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
2012-09-25 23:22:38 -07:00
|
|
|
setupDynamicPanelResizer(notificationFrame);
|
2012-08-20 11:25:47 -07:00
|
|
|
setTimeout(function() {
|
|
|
|
dispatchPanelEvent("socialFrameShow");
|
|
|
|
}, 0);
|
|
|
|
}, true);
|
|
|
|
}
|
2012-07-15 16:12:13 -07:00
|
|
|
});
|
|
|
|
|
2012-09-28 12:41:08 -07:00
|
|
|
panel.openPopup(iconImage, "bottomcenter topleft", 0, 0, false, false);
|
2012-07-15 16:12:13 -07:00
|
|
|
}
|
|
|
|
}
|
2012-07-18 11:40:05 -07:00
|
|
|
|
|
|
|
var SocialSidebar = {
|
|
|
|
// Called once, after window load, when the Social.provider object is initialized
|
|
|
|
init: function SocialSidebar_init() {
|
2012-07-25 16:27:20 -07:00
|
|
|
let sbrowser = document.getElementById("social-sidebar-browser");
|
|
|
|
// setting isAppTab causes clicks on untargeted links to open new tabs
|
|
|
|
sbrowser.docShell.isAppTab = true;
|
|
|
|
|
2012-07-18 11:40:05 -07:00
|
|
|
this.updateSidebar();
|
|
|
|
},
|
|
|
|
|
|
|
|
// Whether the sidebar can be shown for this window.
|
|
|
|
get canShow() {
|
|
|
|
return Social.uiVisible && Social.provider.sidebarURL && !this.chromeless;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Whether this is a "chromeless window" (e.g. popup window). We don't show
|
|
|
|
// the sidebar in these windows.
|
|
|
|
get chromeless() {
|
|
|
|
let docElem = document.documentElement;
|
|
|
|
return docElem.getAttribute('disablechrome') ||
|
|
|
|
docElem.getAttribute('chromehidden').indexOf("extrachrome") >= 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Whether the user has toggled the sidebar on (for windows where it can appear)
|
|
|
|
get enabled() {
|
|
|
|
return Services.prefs.getBoolPref("social.sidebar.open");
|
|
|
|
},
|
|
|
|
|
2012-08-03 14:42:40 -07:00
|
|
|
dispatchEvent: function(aType, aDetail) {
|
|
|
|
let sbrowser = document.getElementById("social-sidebar-browser");
|
|
|
|
let evt = sbrowser.contentDocument.createEvent("CustomEvent");
|
|
|
|
evt.initCustomEvent(aType, true, true, aDetail ? aDetail : {});
|
|
|
|
sbrowser.contentDocument.documentElement.dispatchEvent(evt);
|
|
|
|
},
|
|
|
|
|
2012-07-18 11:40:05 -07:00
|
|
|
updateSidebar: function SocialSidebar_updateSidebar() {
|
|
|
|
// Hide the toggle menu item if the sidebar cannot appear
|
|
|
|
let command = document.getElementById("Social:ToggleSidebar");
|
|
|
|
command.hidden = !this.canShow;
|
|
|
|
|
|
|
|
// Hide the sidebar if it cannot appear, or has been toggled off.
|
|
|
|
// Also set the command "checked" state accordingly.
|
|
|
|
let hideSidebar = !this.canShow || !this.enabled;
|
|
|
|
let broadcaster = document.getElementById("socialSidebarBroadcaster");
|
|
|
|
broadcaster.hidden = hideSidebar;
|
|
|
|
command.setAttribute("checked", !hideSidebar);
|
|
|
|
|
|
|
|
let sbrowser = document.getElementById("social-sidebar-browser");
|
2012-08-20 14:18:50 -07:00
|
|
|
sbrowser.docShell.isActive = !hideSidebar;
|
2012-08-03 14:42:40 -07:00
|
|
|
if (hideSidebar) {
|
2012-08-20 11:25:47 -07:00
|
|
|
this.dispatchEvent("socialFrameHide");
|
2012-08-03 14:42:40 -07:00
|
|
|
// If we're disabled, unload the sidebar content
|
|
|
|
if (!this.canShow) {
|
|
|
|
sbrowser.removeAttribute("origin");
|
|
|
|
sbrowser.setAttribute("src", "about:blank");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Make sure the right sidebar URL is loaded
|
|
|
|
if (sbrowser.getAttribute("origin") != Social.provider.origin) {
|
|
|
|
sbrowser.setAttribute("origin", Social.provider.origin);
|
|
|
|
sbrowser.setAttribute("src", Social.provider.sidebarURL);
|
|
|
|
sbrowser.addEventListener("load", function sidebarOnShow() {
|
|
|
|
sbrowser.removeEventListener("load", sidebarOnShow);
|
|
|
|
// let load finish, then fire our event
|
|
|
|
setTimeout(function () {
|
2012-08-20 11:25:47 -07:00
|
|
|
SocialSidebar.dispatchEvent("socialFrameShow");
|
2012-08-03 14:42:40 -07:00
|
|
|
}, 0);
|
|
|
|
});
|
|
|
|
} else {
|
2012-08-20 11:25:47 -07:00
|
|
|
this.dispatchEvent("socialFrameShow");
|
2012-08-03 14:42:40 -07:00
|
|
|
}
|
2012-07-18 11:40:05 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|