2012-08-20 17:52:26 -07:00
|
|
|
<?xml version="1.0"?>
|
|
|
|
|
|
|
|
<bindings id="socialChatBindings"
|
|
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
|
|
xmlns:xbl="http://www.mozilla.org/xbl"
|
|
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
|
|
|
|
|
|
<binding id="chatbox">
|
|
|
|
<content orient="vertical" mousethrough="never">
|
2012-09-25 14:38:58 -07:00
|
|
|
<xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected,activity"
|
2012-09-25 11:59:57 -07:00
|
|
|
onclick="document.getBindingParent(this).toggle();" align="baseline">
|
2012-08-20 17:52:26 -07:00
|
|
|
<xul:image class="chat-status-icon" xbl:inherits="src=image"/>
|
|
|
|
<xul:label class="chat-title" flex="1" xbl:inherits="value=label,crop"/>
|
|
|
|
<xul:toolbarbutton class="chat-close-button chat-toolbarbutton"
|
|
|
|
oncommand="document.getBindingParent(this).close();"/>
|
|
|
|
</xul:hbox>
|
|
|
|
<xul:iframe anonid="iframe" class="chat-frame" flex="1"
|
|
|
|
xbl:inherits="src,origin,collapsed=minimized" type="content"/>
|
|
|
|
</content>
|
|
|
|
|
|
|
|
<implementation implements="nsIDOMEventListener">
|
|
|
|
<field name="iframe" readonly="true">
|
|
|
|
document.getAnonymousElementByAttribute(this, "anonid", "iframe");
|
|
|
|
</field>
|
|
|
|
|
2012-08-23 17:10:07 -07:00
|
|
|
<property name="minimized">
|
|
|
|
<getter>
|
|
|
|
return this.getAttribute("minimized") == "true";
|
|
|
|
</getter>
|
|
|
|
<setter>
|
|
|
|
this.isActive = !val;
|
|
|
|
if (val)
|
|
|
|
this.setAttribute("minimized", "true");
|
|
|
|
else
|
|
|
|
this.removeAttribute("minimized");
|
|
|
|
</setter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="isActive">
|
|
|
|
<getter>
|
|
|
|
return this.iframe.docShell.isActive;
|
|
|
|
</getter>
|
|
|
|
<setter>
|
|
|
|
this.iframe.docShell.isActive = !!val;
|
|
|
|
|
|
|
|
// let the chat frame know if it is being shown or hidden
|
|
|
|
let evt = this.iframe.contentDocument.createEvent("CustomEvent");
|
2012-09-06 15:36:36 -07:00
|
|
|
evt.initCustomEvent(val ? "socialFrameShow" : "socialFrameHide", true, true, {});
|
2012-08-23 17:10:07 -07:00
|
|
|
this.iframe.contentDocument.documentElement.dispatchEvent(evt);
|
|
|
|
</setter>
|
|
|
|
</property>
|
|
|
|
|
2012-08-20 17:52:26 -07:00
|
|
|
<method name="init">
|
|
|
|
<parameter name="aProvider"/>
|
|
|
|
<parameter name="aURL"/>
|
|
|
|
<parameter name="aCallback"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
this._callback = aCallback;
|
|
|
|
this.setAttribute("origin", aProvider.origin);
|
|
|
|
this.setAttribute("src", aURL);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="close">
|
|
|
|
<body><![CDATA[
|
|
|
|
this.parentNode.remove(this);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="toggle">
|
|
|
|
<body><![CDATA[
|
2012-08-23 17:10:07 -07:00
|
|
|
this.minimized = !this.minimized;
|
2012-08-20 17:52:26 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
</implementation>
|
|
|
|
|
|
|
|
<handlers>
|
|
|
|
<handler event="focus" phase="capturing">
|
|
|
|
this.parentNode.selectedChat = this;
|
|
|
|
</handler>
|
2012-09-25 14:38:58 -07:00
|
|
|
<handler event="DOMContentLoaded"><![CDATA[
|
2012-08-23 17:10:07 -07:00
|
|
|
this.isActive = !this.minimized;
|
|
|
|
if (this._callback) this._callback(this.iframe.contentWindow);
|
2012-09-25 14:38:58 -07:00
|
|
|
let chatbox = this;
|
|
|
|
function chatActivity() {
|
|
|
|
chatbox.setAttribute("activity", true);
|
|
|
|
chatbox.parentNode.updateTitlebar(chatbox);
|
|
|
|
};
|
|
|
|
let iframeWindow = this.iframe.contentWindow;
|
|
|
|
iframeWindow.addEventListener("socialChatActivity", chatActivity);
|
|
|
|
iframeWindow.addEventListener("unload", function unload() {
|
|
|
|
iframeWindow.removeEventListener("unload", unload);
|
|
|
|
iframeWindow.removeEventListener("socialChatActivity", chatActivity);
|
|
|
|
});
|
|
|
|
]]></handler>
|
|
|
|
<handler event="DOMTitleChanged"><![CDATA[
|
|
|
|
this.setAttribute('label', this.iframe.contentDocument.title);
|
|
|
|
this.parentNode.updateTitlebar(this);
|
2012-08-23 17:10:07 -07:00
|
|
|
]]></handler>
|
2012-08-20 17:52:26 -07:00
|
|
|
<handler event="DOMLinkAdded"><![CDATA[
|
|
|
|
// much of this logic is from DOMLinkHandler in browser.js
|
|
|
|
// this sets the presence icon for a chat user, we simply use favicon style updating
|
|
|
|
let link = event.originalTarget;
|
|
|
|
let rel = link.rel && link.rel.toLowerCase();
|
|
|
|
if (!link || !link.ownerDocument || !rel || !link.href)
|
|
|
|
return;
|
|
|
|
if (link.rel.indexOf("icon") < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let uri = DOMLinkHandler.getLinkIconURI(link);
|
|
|
|
if (!uri)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// we made it this far, use it
|
|
|
|
this.setAttribute('image', uri.spec);
|
2012-09-25 14:38:58 -07:00
|
|
|
this.parentNode.updateTitlebar(this);
|
2012-08-20 17:52:26 -07:00
|
|
|
]]></handler>
|
|
|
|
</handlers>
|
|
|
|
</binding>
|
|
|
|
|
|
|
|
<binding id="chatbar">
|
|
|
|
<content>
|
|
|
|
<xul:hbox align="end" pack="end" anonid="innerbox" class="chatbar-innerbox" mousethrough="always" flex="1">
|
|
|
|
<xul:toolbarbutton anonid="nub" class="chatbar-button" type="menu" collapsed="true" mousethrough="never">
|
|
|
|
<xul:menupopup anonid="nubMenu" oncommand="document.getBindingParent(this).swapChat(event)"/>
|
|
|
|
</xul:toolbarbutton>
|
|
|
|
<xul:spacer flex="1" anonid="spacer" class="chatbar-overflow-spacer"/>
|
|
|
|
<children/>
|
|
|
|
</xul:hbox>
|
|
|
|
</content>
|
|
|
|
|
|
|
|
<implementation implements="nsIDOMEventListener">
|
|
|
|
|
|
|
|
<field name="innerbox" readonly="true">
|
|
|
|
document.getAnonymousElementByAttribute(this, "anonid", "innerbox");
|
|
|
|
</field>
|
|
|
|
|
|
|
|
<field name="menupopup" readonly="true">
|
|
|
|
document.getAnonymousElementByAttribute(this, "anonid", "nubMenu");
|
|
|
|
</field>
|
|
|
|
|
2012-09-25 14:38:58 -07:00
|
|
|
<field name="nub" readonly="true">
|
|
|
|
document.getAnonymousElementByAttribute(this, "anonid", "nub");
|
|
|
|
</field>
|
|
|
|
|
2012-08-20 17:52:26 -07:00
|
|
|
<property name="emptyWidth">
|
|
|
|
<getter>
|
|
|
|
return document.getAnonymousElementByAttribute(this, "anonid", "spacer").boxObject.width;
|
|
|
|
</getter>
|
|
|
|
</property>
|
|
|
|
|
2012-09-04 14:42:06 -07:00
|
|
|
<property name="selectedChat">
|
|
|
|
<getter><![CDATA[
|
|
|
|
return this._selectedChat;
|
|
|
|
]]></getter>
|
|
|
|
<setter><![CDATA[
|
|
|
|
if (this._selectedChat)
|
|
|
|
this._selectedChat.removeAttribute("selected");
|
|
|
|
this._selectedChat = val;
|
|
|
|
if (val) {
|
|
|
|
this._selectedChat.setAttribute("selected", "true");
|
2012-09-25 14:38:58 -07:00
|
|
|
this._selectedChat.removeAttribute("activity");
|
2012-09-04 14:42:06 -07:00
|
|
|
}
|
|
|
|
]]></setter>
|
|
|
|
</property>
|
2012-08-20 17:52:26 -07:00
|
|
|
|
|
|
|
<field name="menuitemMap">new WeakMap()</field>
|
2012-08-26 16:51:24 -07:00
|
|
|
<field name="chatboxForURL">new Map();</field>
|
2012-08-20 17:52:26 -07:00
|
|
|
|
|
|
|
<property name="firstCollapsedChild">
|
|
|
|
<getter><![CDATA[
|
|
|
|
let child = this.lastChild;
|
|
|
|
while (child && !child.collapsed) {
|
|
|
|
child = child.previousSibling;
|
|
|
|
}
|
|
|
|
return child;
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="firstVisibleChild">
|
|
|
|
<getter><![CDATA[
|
|
|
|
let child = this.firstChild;
|
|
|
|
while (child && child.collapsed) {
|
|
|
|
child = child.nextSibling;
|
|
|
|
}
|
|
|
|
return child;
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<property name="firstRemovableChild">
|
|
|
|
<getter><![CDATA[
|
|
|
|
let child = this.firstChild;
|
|
|
|
// find the first visible non-focused chatbox, always keep one visible if we
|
|
|
|
// have enough width to do so.
|
|
|
|
while (child &&
|
|
|
|
(child.collapsed || child == this.selectedChat)) {
|
|
|
|
child = child.nextSibling;
|
|
|
|
}
|
|
|
|
if (!child && this.selectedChat) {
|
|
|
|
child = this.selectedChat;
|
|
|
|
}
|
|
|
|
return child;
|
|
|
|
]]></getter>
|
|
|
|
</property>
|
|
|
|
|
|
|
|
<method name="resize">
|
|
|
|
<body><![CDATA[
|
|
|
|
let child = this.firstCollapsedChild;
|
2012-08-31 10:58:27 -07:00
|
|
|
if (child && this.emptyWidth > child.viewWidth) {
|
2012-08-20 17:52:26 -07:00
|
|
|
this.showChat(child);
|
|
|
|
}
|
|
|
|
if (!this.firstCollapsedChild) {
|
|
|
|
window.removeEventListener("resize", this);
|
|
|
|
this.menupopup.parentNode.collapsed = true;
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2012-09-25 14:38:58 -07:00
|
|
|
<method name="updateTitlebar">
|
|
|
|
<parameter name="aChatbox"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
if (aChatbox.collapsed) {
|
|
|
|
let menuitem = this.menuitemMap.get(aChatbox);
|
|
|
|
if (aChatbox.getAttribute("activity")) {
|
|
|
|
menuitem.setAttribute("activity", true);
|
|
|
|
this.nub.setAttribute("activity", true);
|
|
|
|
}
|
|
|
|
menuitem.setAttribute("label", aChatbox.getAttribute("label"));
|
|
|
|
menuitem.setAttribute("image", aChatbox.getAttribute("image"));
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2012-08-20 17:52:26 -07:00
|
|
|
<method name="handleEvent">
|
|
|
|
<parameter name="aEvent"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
if (aEvent.type == "resize") {
|
|
|
|
this.resize();
|
|
|
|
}
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="swapChat">
|
|
|
|
<parameter name="aEvent"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
let menuitem = aEvent.target;
|
|
|
|
let newChat = menuitem.chat;
|
|
|
|
let oldChat = this.firstVisibleChild;
|
|
|
|
if (oldChat)
|
|
|
|
this.collapseChat(oldChat);
|
|
|
|
if (newChat)
|
|
|
|
this.showChat(newChat);
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="collapseChat">
|
|
|
|
<parameter name="aChatbox"/>
|
|
|
|
<body><![CDATA[
|
2012-08-31 10:58:27 -07:00
|
|
|
aChatbox.viewWidth = aChatbox.getBoundingClientRect().width;
|
2012-08-20 17:52:26 -07:00
|
|
|
aChatbox.collapsed = true;
|
2012-08-23 17:10:07 -07:00
|
|
|
aChatbox.isActive = false;
|
2012-08-20 17:52:26 -07:00
|
|
|
let menu = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
|
2012-09-25 14:38:58 -07:00
|
|
|
menu.setAttribute("class", "menuitem-iconic");
|
2012-08-20 17:52:26 -07:00
|
|
|
menu.setAttribute("label", aChatbox.iframe.contentDocument.title);
|
2012-09-25 14:38:58 -07:00
|
|
|
menu.setAttribute("image", aChatbox.getAttribute("image"));
|
2012-08-20 17:52:26 -07:00
|
|
|
menu.chat = aChatbox;
|
|
|
|
this.menuitemMap.set(aChatbox, menu);
|
|
|
|
this.menupopup.appendChild(menu);
|
|
|
|
this.menupopup.parentNode.collapsed = false;
|
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="showChat">
|
|
|
|
<parameter name="aChatbox"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
let menuitem = this.menuitemMap.get(aChatbox);
|
|
|
|
this.menuitemMap.delete(aChatbox);
|
|
|
|
this.menupopup.removeChild(menuitem);
|
|
|
|
aChatbox.collapsed = false;
|
2012-08-23 17:10:07 -07:00
|
|
|
aChatbox.isActive = !aChatbox.minimized;
|
2012-08-20 17:52:26 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="remove">
|
|
|
|
<parameter name="aChatbox"/>
|
|
|
|
<body><![CDATA[
|
|
|
|
if (this.selectedChat == aChatbox) {
|
|
|
|
this.selectedChat = aChatbox.previousSibling ? aChatbox.previousSibling : aChatbox.nextSibling
|
|
|
|
}
|
|
|
|
this.removeChild(aChatbox);
|
|
|
|
this.resize();
|
2012-08-26 16:51:24 -07:00
|
|
|
this.chatboxForURL.delete(aChatbox.getAttribute('src'));
|
2012-08-20 17:52:26 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
<method name="removeAll">
|
|
|
|
<body><![CDATA[
|
|
|
|
while (this.firstChild) {
|
|
|
|
this.removeChild(this.firstChild);
|
|
|
|
}
|
2012-08-26 16:51:24 -07:00
|
|
|
this.chatboxForURL = new Map();
|
2012-08-20 17:52:26 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
2012-08-26 16:51:24 -07:00
|
|
|
<method name="openChat">
|
2012-08-20 17:52:26 -07:00
|
|
|
<parameter name="aProvider"/>
|
|
|
|
<parameter name="aURL"/>
|
|
|
|
<parameter name="aCallback"/>
|
2012-08-26 16:51:24 -07:00
|
|
|
<parameter name="aMode"/>
|
2012-08-20 17:52:26 -07:00
|
|
|
<body><![CDATA[
|
2012-08-26 16:51:24 -07:00
|
|
|
let cb = this.chatboxForURL.get(aURL);
|
|
|
|
if (cb) {
|
|
|
|
cb = cb.get();
|
|
|
|
if (cb.parentNode) {
|
|
|
|
// ensure this chatbox is visible
|
|
|
|
if (this.selectedChat != cb)
|
|
|
|
this.selectedChat = cb;
|
|
|
|
if (cb.collapsed)
|
|
|
|
this.showChat(cb);
|
2012-10-04 21:13:11 -07:00
|
|
|
if (aCallback)
|
|
|
|
aCallback(cb.iframe.contentWindow);
|
2012-08-26 16:51:24 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.chatboxForURL.delete(aURL);
|
|
|
|
}
|
|
|
|
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
|
|
|
|
if (aMode == "minimized")
|
2012-10-15 16:15:52 -07:00
|
|
|
cb.setAttribute("minimized", "true");
|
2012-08-20 17:52:26 -07:00
|
|
|
this.selectedChat = cb;
|
2012-08-26 16:51:24 -07:00
|
|
|
this.insertBefore(cb, this.firstChild);
|
2012-08-20 17:52:26 -07:00
|
|
|
cb.init(aProvider, aURL, aCallback);
|
2012-08-26 16:51:24 -07:00
|
|
|
this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
|
2012-08-20 17:52:26 -07:00
|
|
|
]]></body>
|
|
|
|
</method>
|
|
|
|
|
|
|
|
</implementation>
|
|
|
|
<handlers>
|
2012-09-25 14:38:58 -07:00
|
|
|
<handler event="popupshown"><![CDATA[
|
|
|
|
this.nub.removeAttribute("activity");
|
|
|
|
]]></handler>
|
2012-08-20 17:52:26 -07:00
|
|
|
<handler event="overflow"><![CDATA[
|
|
|
|
// make sure we're not getting an overflow from content
|
|
|
|
if (event.originalTarget != this.innerbox)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let hasHidden = this.firstCollapsedChild;
|
|
|
|
let child = this.firstRemovableChild;
|
|
|
|
if (child)
|
|
|
|
this.collapseChat(child);
|
|
|
|
if (!hasHidden) {
|
|
|
|
window.addEventListener("resize", this);
|
|
|
|
}
|
|
|
|
]]></handler>
|
|
|
|
</handlers>
|
|
|
|
</binding>
|
|
|
|
|
|
|
|
</bindings>
|