releases-comm-central/im/content/contact.xml

612 строки
20 KiB
XML

<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is the Instantbird messenging client, released
- 2007.
-
- The Initial Developer of the Original Code is
- Florian QUEZE <florian@instantbird.org>.
- Portions created by the Initial Developer are Copyright (C) 2007
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL or the LGPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<!DOCTYPE bindings [
<!ENTITY % instantbirdDTD SYSTEM "chrome://instantbird/locale/instantbird.dtd" >
%instantbirdDTD;
]>
<bindings id="contactBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:html="http://www.w3.org/1999/xhtml">
<binding id="contact" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content>
<xul:stack class="prplBuddyIcon" mousethrough="always">
<xul:image xbl:inherits="src=iconPrpl" class="protoIcon"/>
<xul:image class="statusIcon"/>
</xul:stack>
<xul:hbox flex="1" class="contact-hbox" mousethrough="always">
<xul:label crop="end" flex="1" mousethrough="always"
anonid="displayname" class="contactDisplayName"
xbl:inherits="value=displayname"/>
<xul:label crop="end" flex="100000" mousethrough="always"
anonid="statusText" class="contactStatusText"
xbl:inherits="value=statusTextWithDash"/>
<xul:button anonid="startChatBubble" class="startChatBubble"
tooltiptext="&openConversationCmd.label;"/>
</xul:hbox>
</content>
<implementation implements="nsIObserver">
<destructor>
<![CDATA[
if (this.contact) {
this.contact.removeObserver(this);
delete this.contact;
}
]]>
</destructor>
<method name="build">
<parameter name="aContact"/>
<parameter name="aGroup"/>
<body>
<![CDATA[
this.group = aGroup;
this.contact = aContact;
this.contact.addObserver(this);
this.addEventListener("transitionend", this._transitionEnd, true);
// Don't do the animation if inside a closed group
this.state = this.hasAttribute("collapsed") ? "collapsed" : "showing";
]]>
</body>
</method>
<property name="displayName"
onget="return this.contact.displayName;"/>
<property name="state"
onget="return this.getAttribute('state');">
<setter>
<![CDATA[
this.setAttribute('state', val);
if ("_transitionTimer" in this) {
clearTimeout(this._transitionTimer);
delete this._transitionTimer;
}
// If the new state will start a transition, add a timer to ensure
// it will actually be finished (see bug 675).
let delay;
if (val == "showing" || val == "collapsing")
delay = 200;
else if (val == "fading")
delay = 400 + 1000; // 400ms + 1000ms before starting
else
return val;
this._transitionTimer =
setTimeout(this._transitionEnd.bind(this), delay + 100);
return val;
]]>
</setter>
</property>
<property name="collapsed"
onget="return !!this.getAttribute('collapsed');">
<setter>
<![CDATA[
if (val) {
let state = this.state;
if (state == "fading" || state == "collapsing") {
if (state == "fading")
this.finishRemoveNode();
this.parentNode.removeChild(this);
return;
}
this.setAttribute("collapsed", val);
this.state = "collapsed";
}
else {
this.removeAttribute("collapsed");
this.state = "visible";
this.update();
}
return val;
]]>
</setter>
</property>
<!-- nsIObserver implementation -->
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
if (!("contact" in this)) {
/* groups usually receive contact notifications after contacts.
However the 'Other Contacts' group uses the contact notifications
to fire group notifications, so if we are displayed inside the
'Other Contacts' group, we may receive notifications here
even though we have already attempted to remove our observer.
Just ignore these notifications.
*/
return;
}
if (aTopic == "contact-preferred-buddy-changed" ||
aTopic == "contact-display-name-changed" ||
aTopic == "contact-status-changed")
this.update();
else if (aTopic == "contact-signed-on") {
if (this.state == "fading")
this.state = "visible";
}
else if (aTopic == "contact-removed" ||
(aTopic == "contact-moved-out" && aSubject.id == this.group.tag.id) ||
(aTopic == "contact-signed-off" && !this.group.showOffline))
this.removeNode();
else if (aTopic == "buddy-moved-into-contact" && this.hasAttribute("open")) {
let buddyElt = document.createElement("buddy");
if (this.childNodes[1].hasAttribute("dummy"))
this.removeChild(this.childNodes[1]);
this.appendChild(buddyElt);
buddyElt.build(aSubject, this);
}
else if (aTopic == "buddy-position-changed" && this.hasAttribute("open")) {
let id = aSubject.id;
for (let i = 0; i < this.childNodes.length; ++i) {
if (this.childNodes[i].buddy.id == id) {
this.childNodes[i].removeNode();
let newPosition = Number(aData);
if (newPosition > i)
++newPosition;
let ref = null;
if (newPosition < this.childNodes.length)
ref = this.childNodes[newPosition];
let buddyElt = document.createElement("buddy");
this.insertBefore(buddyElt, ref);
buddyElt.build(aSubject, this);
break;
}
}
}
]]>
</body>
</method>
<method name="update">
<body>
<![CDATA[
this.setAttribute("displayname", this.contact.displayName);
let statusText = this.contact.statusText;
if (statusText)
statusText = " - " + statusText;
this.setAttribute("statusTextWithDash", statusText);
let statusType = this.contact.statusType;
this.setAttribute("statusText", Status.toLabel(statusType) + statusText);
this.setAttribute("status", Status.toAttribute(statusType));
if (this.contact.canSendMessage)
this.setAttribute("cansend", "true");
else
this.removeAttribute("cansend");
let proto = this.contact.preferredBuddy.protocol;
this.setAttribute("iconPrpl", proto.iconBaseURI + "icon.png");
]]>
</body>
</method>
<method name="_transitionEnd">
<parameter name="aEvent"/>
<body>
<![CDATA[
if ("_transitionTimer" in this) {
clearTimeout(this._transitionTimer);
delete this._transitionTimer;
}
let state = this.state;
if (state == "showing") {
this.update();
this.state = "visible";
}
else if (state == "fading") {
this.state = "collapsing";
this.finishRemoveNode();
}
else if (state == "collapsing") {
// Ignore events of 'fading' transitions if the timer's already fired.
if (aEvent && ("propertyName" in aEvent) &&
aEvent.propertyName != "height")
return;
this.parentNode.removeChild(this);
}
]]>
</body>
</method>
<method name="finishRemoveNode">
<body>
<![CDATA[
this.contact.removeObserver(this);
this.group.removeContact(this);
delete this.contact;
]]>
</body>
</method>
<method name="removeNode">
<body>
<![CDATA[
let state = this.state;
if (this.hasAttribute("collapsed") ||
window.getComputedStyle(this).height == "0px") {
// If the contact is not visible, remove it immediately (without animation)
this.finishRemoveNode();
this.state = "collapsed"; // will remove pending animation timers.
this.parentNode.removeChild(this);
}
else if (state == "showing") {
// We are still doing the expand animation!
this.state = "collapsing";
this.finishRemoveNode();
}
else if (state != "fading" && state != "collapsing")
this.state = "fading";
]]>
</body>
</method>
<method name="startAliasing">
<body>
<![CDATA[
if (this.hasAttribute("aliasing"))
return; // prevent re-entry.
this.setAttribute("aliasing", "true");
let textbox =
document.getAnonymousElementByAttribute(this, "anonid", "displayname");
textbox.getBoundingClientRect(); // force binding attachmant by forcing layout
textbox.select();
// Some keys (home/end for example) can make the selected item
// of the richlistbox change without producing a blur event on
// our textbox. Make sure we watch richlistbox selection changes.
this._parentSelectListener = (function(aEvent) {
if (aEvent.target == this.parentNode)
this.finishAliasing(true);
}).bind(this);
this.parentNode.addEventListener("select", this._parentSelectListener, false);
]]>
</body>
</method>
<method name="finishAliasing">
<parameter name="aSave"/>
<body>
<![CDATA[
if (aSave) {
this.contact.alias =
document.getAnonymousElementByAttribute(this, "anonid", "displayname").value;
}
this.removeAttribute("aliasing");
this.parentNode.removeEventListener("select", this._parentSelectListener, false);
delete this._parentSelectListener;
this.parentNode.focus();
]]>
</body>
</method>
<method name="remove">
<body>
<![CDATA[
this.contact.remove();
]]>
</body>
</method>
<method name="canOpenConversation">
<body>
<![CDATA[
return this.contact.canSendMessage;
]]>
</body>
</method>
<method name="openConversation">
<body>
<![CDATA[
if (!("Conversations" in window))
Components.utils.import("resource:///modules/imWindows.jsm");
Conversations.focusConversation(this.contact.createConversation());
]]>
</body>
</method>
<method name="keyPress">
<parameter name="aEvent"/>
<body>this._keyPress(aEvent);</body>
</method>
<method name="_keyPress">
<parameter name="aEvent"/>
<body>
<![CDATA[
switch (aEvent.keyCode) {
// If Enter or Return is pressed, open a new conversation
case aEvent.DOM_VK_RETURN:
case aEvent.DOM_VK_ENTER:
if (this.hasAttribute("aliasing"))
this.finishAliasing(true);
else if (this.canOpenConversation())
this.openConversation();
break;
case aEvent.DOM_VK_F2:
if (!this.hasAttribute("aliasing"))
this.startAliasing();
break;
case aEvent.DOM_VK_ESCAPE:
if (this.hasAttribute("aliasing"))
this.finishAliasing(false);
break;
}
]]>
</body>
</method>
<method name="_DragOk">
<parameter name="aEvent"/>
<body>
<![CDATA[
aEvent.preventDefault();
if (this.hasAttribute("droptarget"))
return;
if ("_droptarget" in window)
window._droptarget.removeAttribute("droptarget");
window._droptarget = this;
this.setAttribute("droptarget", "true");
]]>
</body>
</method>
<method name="_DragLeave">
<body>
<![CDATA[
if (!this.hasAttribute("droptarget"))
return;
delete window._droptarget;
this.removeAttribute("droptarget");
]]>
</body>
</method>
<method name="_checkDrag">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (this.state != "visible")
return;
let dt = aEvent.dataTransfer;
if (dt.types.contains("application/x-ib-contact")) {
if (dt.getData("application/x-ib-contact") != this.contact.id)
this._DragOk(aEvent);
else
aEvent.stopPropagation();
}
else if (dt.types.contains("application/x-ib-buddy"))
this._DragOk(aEvent);
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="blur">
<![CDATA[
if (!this.hasAttribute("aliasing"))
return;
let win =
Components.classes["@mozilla.org/focus-manager;1"]
.getService(Components.interfaces.nsIFocusManager)
.activeWindow;
if (win == document.defaultView)
finishAliasing(true);
]]>
</handler>
<handler event="mousedown">
<![CDATA[
if (!this.hasAttribute("aliasing") && canOpenConversation() &&
event.originalTarget.getAttribute("anonid") == "startChatBubble")
openConversation();
]]>
</handler>
<handler event="click">
<![CDATA[
if (!this.hasAttribute("aliasing") && canOpenConversation() &&
event.detail == 2 &&
event.originalTarget.getAttribute("anonid") != "expander")
openConversation();
]]>
</handler>
<handler event="dragstart">
<![CDATA[
if (this.state != "visible")
return;
event.dataTransfer.setData("application/x-ib-contact",
this.contact.id);
event.stopPropagation();
]]>
</handler>
<handler event="drop">
<![CDATA[
let dt = event.dataTransfer;
if (dt.types.contains("application/x-ib-contact")) {
let id = dt.getData("application/x-ib-contact");
this.contact.mergeContact(Services.contacts.getContactById(id));
}
else if (dt.types.contains("application/x-ib-buddy")) {
let id = dt.getData("application/x-ib-buddy");
let from = Services.contacts.getBuddyById(id);
if (from.contact.id != this.contact.id)
contact.adoptBuddy(from);
else
contact.moveBuddyBefore(from);
}
else
throw "Invalid drop on buddy!";
this._DragLeave();
]]>
</handler>
<handler event="dragenter">
<![CDATA[
this._checkDrag(event);
]]>
</handler>
<handler event="dragover">
<![CDATA[
this._checkDrag(event);
]]>
</handler>
<handler event="dragleave">
<![CDATA[
this._DragLeave();
]]>
</handler>
</handlers>
</binding>
<binding id="contact-big" extends="chrome://instantbird/content/contact.xml#contact">
<content>
<xul:hbox flex="1" mousethrough="always">
<xul:stack class="prplBuddyIcon" mousethrough="always">
<xul:image xbl:inherits="src=iconPrpl" class="protoIcon"/>
<xul:image class="statusIcon"/>
</xul:stack>
<xul:vbox flex="1" class="contact-vbox" mousethrough="always">
<xul:hbox class="contact-hbox" mousethrough="always">
<xul:label crop="end" flex="1" mousethrough="always"
anonid="displayname" class="contactDisplayName"
xbl:inherits="value=displayname"/>
<xul:button anonid="startChatBubble" class="startChatBubble"
tooltiptext="&openConversationCmd.label;"/>
</xul:hbox>
<xul:hbox class="contact-hbox" mousethrough="always">
<xul:label crop="end" flex="1" mousethrough="always"
anonid="statusText" class="contactStatusText"
xbl:inherits="value=statusText"/>
<xul:button anonid="expander" class="expander-down"
tooltiptextexpand="&expandContactTooltip;"
tooltiptextcollapse="&collapseContactTooltip;"
tooltiptext="&expandContactTooltip;"/>
</xul:hbox>
</xul:vbox>
</xul:hbox>
<xul:vbox anonid="contactBuddies" class="contactBuddies">
<children/>
</xul:vbox>
</content>
<implementation>
<method name="open">
<body>
<![CDATA[
let className, tooltip;
if (!this.hasAttribute("open")) {
let buddies = this.contact.getBuddies();
for each (let buddy in buddies) {
let buddyElt = document.createElement("buddy");
this.appendChild(buddyElt);
buddyElt.build(buddy, this);
}
if (buddies.length == 1) {
let buddyElt = document.createElement("buddy");
buddyElt.setAttribute("dummy", "true");
this.appendChild(buddyElt);
}
this.setAttribute("open", "true");
[className, tooltip] = ["expander-up", "tooltiptextcollapse"];
}
else {
for (let i = this.childNodes.length - 1; i >= 0; --i)
this.childNodes[i].removeNode();
this.removeAttribute("open");
[className, tooltip] = ["expander-down", "tooltiptextexpand"];
}
let expander = document.getAnonymousElementByAttribute(this, "anonid",
"expander");
expander.setAttribute("class", className);
expander.setAttribute("tooltiptext", expander.getAttribute(tooltip));
]]>
</body>
</method>
<method name="keyPress">
<parameter name="aEvent"/>
<body>
<![CDATA[
if (!this.hasAttribute("aliasing")) {
switch (aEvent.keyCode) {
case aEvent.DOM_VK_LEFT:
if (this.hasAttribute("open"))
this.open();
break;
case aEvent.DOM_VK_RIGHT:
if (!this.hasAttribute("open"))
this.open();
break;
}
}
this._keyPress(aEvent); // inherited actions.
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="click">
<![CDATA[
if (event.originalTarget.getAttribute("anonid") == "expander")
this.open();
]]>
</handler>
</handlers>
</binding>
</bindings>