Bug 953992 - new buddy list backend.
This commit is contained in:
Родитель
c94c279962
Коммит
16efff4710
|
@ -354,8 +354,7 @@ Logger.prototype = {
|
|||
["new-conversation", "new-text",
|
||||
"conversation-closed", "conversation-left-chat",
|
||||
"account-connected", "account-disconnected",
|
||||
"buddy-signed-on", "buddy-signed-off",
|
||||
"buddy-away", "buddy-idle"].forEach(function(aEvent) {
|
||||
"account-buddy-status-changed"].forEach(function(aEvent) {
|
||||
obs.addObserver(this, aEvent, false);
|
||||
}, this);
|
||||
break;
|
||||
|
@ -381,7 +380,7 @@ Logger.prototype = {
|
|||
" signed off");
|
||||
closeLogForAccount(aSubject);
|
||||
break;
|
||||
default:
|
||||
case "account-buddy-status-changed":
|
||||
let status;
|
||||
if (!aSubject.online)
|
||||
status = "Offline";
|
||||
|
@ -394,13 +393,15 @@ Logger.prototype = {
|
|||
else
|
||||
status = "Unavailable";
|
||||
|
||||
let statusText = aSubject.status;
|
||||
let statusText = aSubject.statusText;
|
||||
if (statusText)
|
||||
status += " (\"" + statusText + "\")";
|
||||
|
||||
let name = aSubject.buddyName;
|
||||
let nameText = (aSubject.buddyAlias || name) + " (" + name + ")";
|
||||
let nameText = aSubject.displayName + " (" + aSubject.userName + ")";
|
||||
getLogForAccount(aSubject.account).logEvent(nameText + " is now " + status);
|
||||
break;
|
||||
default:
|
||||
throw "Unexpected notification " + aTopic;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -40,8 +40,8 @@ var addBuddy = {
|
|||
onload: function ab_onload() {
|
||||
this.pcs = Components.classes["@instantbird.org/purple/core;1"]
|
||||
.getService(Ci.purpleICoreService);
|
||||
this.pts = Components.classes["@instantbird.org/purple/tags;1"]
|
||||
.getService(Ci.purpleITagsService);
|
||||
this.pts = Components.classes["@instantbird.org/purple/tags-service;1"]
|
||||
.getService(Ci.imITagsService);
|
||||
this.buildAccountList();
|
||||
this.buildTagList();
|
||||
},
|
||||
|
|
|
@ -35,8 +35,9 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const events = ["buddy-signed-on",
|
||||
"buddy-added",
|
||||
const events = ["contact-availability-changed",
|
||||
"contact-added",
|
||||
"contact-moved",
|
||||
"account-disconnected",
|
||||
"status-changed",
|
||||
"purple-quit"];
|
||||
|
@ -88,13 +89,14 @@ buddyListContextMenu.prototype = {
|
|||
Components.classes["@mozilla.org/intl/stringbundle;1"]
|
||||
.getService(Components.interfaces.nsIStringBundleService)
|
||||
.createBundle("chrome://instantbird/locale/instantbird.properties");
|
||||
let buddy = this.target.buddy;
|
||||
let displayName = buddy.alias || buddy.name;
|
||||
let contact = this.target.contact;
|
||||
let displayName = contact.displayName;
|
||||
let promptTitle = bundle.formatStringFromName("buddy.deletePrompt.title",
|
||||
[displayName], 1);
|
||||
if (displayName != buddy.name)
|
||||
displayName += " (" + buddy.name + ")";
|
||||
let proto = buddy.getAccount(0).protocol.name;
|
||||
let userName = contact.preferredBuddy.userName;
|
||||
if (displayName != userName)
|
||||
displayName += " (" + userName + ")";
|
||||
let proto = contact.preferredBuddy.protocol.name; // FIXME build a list
|
||||
let promptMessage = bundle.formatStringFromName("buddy.deletePrompt.message",
|
||||
[displayName, proto], 2);
|
||||
let deleteButton = bundle.GetStringFromName("buddy.deletePrompt.button");
|
||||
|
@ -117,8 +119,8 @@ buddyListContextMenu.prototype = {
|
|||
popup.removeChild(item);
|
||||
|
||||
let groupId = this.target.group.groupId;
|
||||
let pts = Components.classes["@instantbird.org/purple/tags;1"]
|
||||
.getService(Ci.purpleITagsService);
|
||||
let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
|
||||
.getService(Ci.imITagsService);
|
||||
|
||||
let sortFunction = function (a, b) {
|
||||
let [a, b] = [a.name.toLowerCase(), b.name.toLowerCase()];
|
||||
|
@ -157,8 +159,8 @@ buddyListContextMenu.prototype = {
|
|||
{value: false}) || !name.value)
|
||||
return; // the user canceled
|
||||
|
||||
let pts = Components.classes["@instantbird.org/purple/tags;1"]
|
||||
.getService(Ci.purpleITagsService);
|
||||
let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
|
||||
.getService(Ci.imITagsService);
|
||||
let tag = pts.getTagByName(name.value) || pts.createTag(name.value);
|
||||
this.target.moveTo(tag.id);
|
||||
},
|
||||
|
@ -207,21 +209,22 @@ var buddyList = {
|
|||
else
|
||||
item.removeAttribute("checked");
|
||||
|
||||
let pts = Components.classes["@instantbird.org/purple/tags;1"]
|
||||
.getService(Ci.purpleITagsService);
|
||||
let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
|
||||
.getService(Ci.imITagsService);
|
||||
let blistBox = document.getElementById("buddylistbox");
|
||||
pts.getTags().forEach(function (aTag) {
|
||||
let elt = document.getElementById("group" + aTag.id);
|
||||
if (!elt && showOffline) {
|
||||
if (elt)
|
||||
elt.showOffline = showOffline;
|
||||
else if (showOffline) {
|
||||
elt = document.createElement("group");
|
||||
blistBox.appendChild(elt);
|
||||
elt._showOffline = true;
|
||||
if (!elt.build(aTag))
|
||||
blistBox.removeChild(elt);
|
||||
}
|
||||
if (elt)
|
||||
elt.showOffline = showOffline;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTopic == "status-changed") {
|
||||
|
@ -237,17 +240,24 @@ var buddyList = {
|
|||
return;
|
||||
}
|
||||
|
||||
var pab = aSubject.QueryInterface(Ci.purpleIAccountBuddy);
|
||||
var group = pab.tag;
|
||||
var groupId = "group" + group.id;
|
||||
if ((aTopic == "buddy-signed-on" ||
|
||||
(aTopic == "buddy-added" && (this._showOffline || pab.online))) &&
|
||||
!document.getElementById(groupId)) {
|
||||
let groupElt = document.createElement("group");
|
||||
document.getElementById("buddylistbox").appendChild(groupElt);
|
||||
if (this._showOffline)
|
||||
groupElt._showOffline = true;
|
||||
groupElt.build(group);
|
||||
// aSubject is an imIContact
|
||||
if (aSubject.online || this._showOffline) {
|
||||
aSubject.getTags().forEach(function (aTag) {
|
||||
if (!document.getElementById("group" + aTag.id)) {
|
||||
let groupElt = document.createElement("group");
|
||||
let blistBox = document.getElementById("buddylistbox");
|
||||
blistBox.appendChild(groupElt);
|
||||
if (this._showOffline)
|
||||
groupElt._showOffline = true;
|
||||
if (!groupElt.build(aTag)) {
|
||||
// Broken group or notification?
|
||||
// This should never happen as there will always be at least
|
||||
// one contact shown.
|
||||
// (We test the aSubject.online || this._showOffline to ensure it.)
|
||||
blistBox.removeChild(groupElt);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -507,8 +517,8 @@ var buddyList = {
|
|||
.setAttribute("checked", "true");
|
||||
}
|
||||
|
||||
let pts = Components.classes["@instantbird.org/purple/tags;1"]
|
||||
.getService(Ci.purpleITagsService);
|
||||
let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
|
||||
.getService(Ci.imITagsService);
|
||||
let blistBox = document.getElementById("buddylistbox");
|
||||
pts.getTags().forEach(function (aTag) {
|
||||
let groupElt = document.createElement("group");
|
||||
|
|
|
@ -54,7 +54,16 @@
|
|||
anonid="displayname" class="buddyDisplayName"
|
||||
xbl:inherits="value=displayname"/>
|
||||
</content>
|
||||
<implementation>
|
||||
<implementation implements="nsIObserver">
|
||||
|
||||
<destructor>
|
||||
<![CDATA[
|
||||
if (this.contact) {
|
||||
this.contact.removeObserver(this);
|
||||
delete this.contact;
|
||||
}
|
||||
]]>
|
||||
</destructor>
|
||||
|
||||
<!-- delay in milliseconds before starting the fade out animation -->
|
||||
<field name="animationDelay">1000</field>
|
||||
|
@ -63,22 +72,16 @@
|
|||
<field name="animationInterval">20</field>
|
||||
|
||||
<method name="build">
|
||||
<parameter name="aBuddy"/>
|
||||
<parameter name="aContact"/>
|
||||
<parameter name="aGroup"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.group = aGroup;
|
||||
this.buddy = aBuddy;
|
||||
this.buddyId = aBuddy.id; // used by group.xml
|
||||
this.name = aBuddy.name;
|
||||
this.setAttribute("displayname", aBuddy.alias || this.name);
|
||||
//this.setAttribute("id", "buddy" + this.buddyId);
|
||||
this.setAttribute("iconPrpl",
|
||||
aBuddy.getAccount(0).protocol.iconBaseURI + "icon.png");
|
||||
this.accounts = { };
|
||||
this.accountsCount = 0;
|
||||
// We don't call this.update yet because this.accounts is empty
|
||||
this.contact = aContact;
|
||||
this.update();
|
||||
this.contact.addObserver(this);
|
||||
#ifndef WINCE
|
||||
|
||||
// Don't do the animation if inside a closed group
|
||||
if (this.hasAttribute("collapsed"))
|
||||
return;
|
||||
|
@ -91,76 +94,56 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<property name="offline">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
for (var id in this.accounts)
|
||||
if (this.accounts[id].online)
|
||||
return false;
|
||||
return true;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<!-- returns true if no account remain and the buddy should be removed from the list -->
|
||||
<method name="removeOfflineAccounts">
|
||||
<!-- nsIObserver implementation -->
|
||||
<method name="observe">
|
||||
<parameter name="aSubject"/>
|
||||
<parameter name="aTopic"/>
|
||||
<parameter name="aData"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let offlineAccounts = [];
|
||||
for (var id in this.accounts)
|
||||
if (!this.accounts[id].online)
|
||||
offlineAccounts.push(id);
|
||||
|
||||
if (offlineAccounts.length == this.accountsCount)
|
||||
return true;
|
||||
|
||||
offlineAccounts.forEach(function (aId) { delete this.accounts[aId]; }, this);
|
||||
this.accountsCount -= offlineAccounts.length;
|
||||
<![CDATA[
|
||||
if (aTopic == "contact-preferred-buddy-changed" ||
|
||||
aTopic == "contact-display-name-changed" ||
|
||||
aTopic == "contact-availability-changed") {
|
||||
this.update();
|
||||
return false;
|
||||
]]>
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTopic == "contact-signed-on")
|
||||
this.cancelRemoveNode();
|
||||
else if (aTopic == "contact-removed" ||
|
||||
(aTopic == "contact-moved-out" && aSubject.id == this.group.tag.id) ||
|
||||
(aTopic == "contact-signed-off" && !this.group.showOffline))
|
||||
this.removeNode();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="update">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var accounts = [];
|
||||
for (var id in this.accounts)
|
||||
accounts.push(this.accounts[id]);
|
||||
this.setAttribute("displayname", this.contact.displayName);
|
||||
|
||||
let status;
|
||||
if (accounts.every(function(pab) !pab.online))
|
||||
status = "offline";
|
||||
else if (accounts.every(function(pab) pab.idle))
|
||||
status = "idle";
|
||||
else if (accounts.every(function(pab) pab.mobile))
|
||||
status = "mobile";
|
||||
else if (accounts.every(function(pab) !pab.available))
|
||||
status = "away";
|
||||
else
|
||||
status = "available";
|
||||
this.setAttribute("status", status);
|
||||
let accountsWithAlias = accounts.filter(function(pab) pab.buddyAlias);
|
||||
let alias;
|
||||
if (accountsWithAlias.length)
|
||||
alias = accountsWithAlias[0].buddyAlias;
|
||||
this.setAttribute("displayname", alias || this.name);
|
||||
const imIStatusInfo = Components.interfaces.imIStatusInfo;
|
||||
let statusNames = {};
|
||||
statusNames[imIStatusInfo.STATUS_UNKNOWN] = "offline"; // Should not happen
|
||||
statusNames[imIStatusInfo.STATUS_OFFLINE] = "offline";
|
||||
statusNames[imIStatusInfo.STATUS_MOBILE] = "mobile";
|
||||
statusNames[imIStatusInfo.STATUS_IDLE] = "idle";
|
||||
statusNames[imIStatusInfo.STATUS_AWAY] = "away";
|
||||
//XXX should UNAVAILABLE (= busy) make a difference?
|
||||
statusNames[imIStatusInfo.STATUS_UNAVAILABLE] = "away";
|
||||
statusNames[imIStatusInfo.STATUS_AVAILABLE] = "available";
|
||||
this.setAttribute("status", statusNames[this.contact.statusType]);
|
||||
|
||||
let proto = this.contact.preferredBuddy.protocol;
|
||||
this.setAttribute("iconPrpl", proto.iconBaseURI + "icon.png");
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="addAccount">
|
||||
<parameter name="aPab"/>
|
||||
<method name="cancelRemoveNode">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var id = aPab.account.id;
|
||||
if (id in this.accounts)
|
||||
return;
|
||||
this.accounts[id] = aPab;
|
||||
++this.accountsCount;
|
||||
this.update();
|
||||
|
||||
if (this.hasAttribute("removing")) {
|
||||
this.removeAttribute("removing");
|
||||
this.removeAttribute("collapsing");
|
||||
|
@ -180,40 +163,31 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="removeAccount">
|
||||
<parameter name="aPab"/>
|
||||
<method name="finishRemoveNode">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var id = aPab.account.id;
|
||||
if (!(id in this.accounts))
|
||||
return; // this used to throw, but this seems to be harmless
|
||||
//throw "This buddy (" + aPab.buddy.name +
|
||||
") has not been added for account " + aPab.account.name;
|
||||
this.contact.removeObserver(this);
|
||||
this.group.removeContact(this);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
delete this.accounts[id];
|
||||
--this.accountsCount;
|
||||
|
||||
if (this.accountsCount) {
|
||||
this.update();
|
||||
return;
|
||||
}
|
||||
|
||||
// No account left, this node is now useless, remove it
|
||||
this.removing = true;
|
||||
<method name="removeNode">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.hasAttribute("expanding")) {
|
||||
// We are still doing the expand animation!
|
||||
clearInterval(this.animInterval);
|
||||
this.setAttribute("collapsing", "true");
|
||||
this.removeAttribute("expanding", "true");
|
||||
this.animInterval = setInterval(this._animateCollapse, this.animationInterval, this);
|
||||
this.group.removeBuddy(this);
|
||||
this.finishRemoveNode();
|
||||
}
|
||||
else if (this.hasAttribute("collapsed"))
|
||||
// If the buddy is not visible, remove it immediately (without animation)
|
||||
this.group.removeBuddy(this);
|
||||
this.finishRemoveNode();
|
||||
else
|
||||
this.animTimeout = setTimeout(this._startAnimation, this.animationDelay, this);
|
||||
this.setAttribute("status", "offline");
|
||||
this.setAttribute("removing", "true");
|
||||
]]>
|
||||
</body>
|
||||
|
@ -249,7 +223,7 @@
|
|||
<body>
|
||||
<![CDATA[
|
||||
if (aSave) {
|
||||
this.buddy.alias =
|
||||
this.contact.alias =
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "displayname").value;
|
||||
}
|
||||
this.removeAttribute("aliasing");
|
||||
|
@ -268,14 +242,9 @@
|
|||
if (currentGroupId == aGroupId)
|
||||
return;
|
||||
|
||||
let pts = Components.classes["@instantbird.org/purple/tags;1"]
|
||||
.getService(Ci.purpleITagsService);
|
||||
let newGroup = pts.getTagById(aGroupId);
|
||||
for (var id in this.accounts) {
|
||||
let pab = this.accounts[id];
|
||||
if (pab.tag.id == currentGroupId)
|
||||
pab.tag = newGroup;
|
||||
}
|
||||
let pts = Components.classes["@instantbird.org/purple/tags-service;1"]
|
||||
.getService(Ci.imITagsService);
|
||||
this.contact.move(this.group.tag, pts.getTagById(aGroupId));
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -283,12 +252,7 @@
|
|||
<method name="remove">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let currentGroupId = this.group.groupId;
|
||||
for (var id in this.accounts) {
|
||||
let pab = this.accounts[id];
|
||||
if (pab.tag.id == currentGroupId)
|
||||
pab.remove();
|
||||
}
|
||||
this.contact.remove();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -318,7 +282,7 @@
|
|||
aThis.style.height = (aThis.animHeight = aThis.clientHeight) + "px";
|
||||
aThis.setAttribute("collapsing", "true");
|
||||
aThis.animInterval = setInterval(aThis._animateCollapse, this.animationInterval, aThis);
|
||||
aThis.group.removeBuddy(aThis);
|
||||
aThis.finishRemoveNode();
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
|
@ -363,10 +327,7 @@
|
|||
<method name="canOpenConversation">
|
||||
<body>
|
||||
<![CDATA[
|
||||
for (var id in this.accounts)
|
||||
if (this.accounts[id].canSendMessage)
|
||||
return true;
|
||||
return false;
|
||||
return this.contact.canSendMessage;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -374,18 +335,7 @@
|
|||
<method name="openConversation">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var conv;
|
||||
for (var id in this.accounts) {
|
||||
if (!this.accounts[id].canSendMessage)
|
||||
continue;
|
||||
|
||||
// this will send a new-conversation notification that will force
|
||||
// opening a conversation window if there isn't one around
|
||||
conv = this.accounts[id].createConversation();
|
||||
break;
|
||||
}
|
||||
|
||||
Conversations.focusConversation(conv);
|
||||
Conversations.focusConversation(this.contact.createConversation());
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
|
|
@ -97,19 +97,11 @@
|
|||
if (val == this._buddy)
|
||||
return val;
|
||||
|
||||
let os =
|
||||
Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
const events = [
|
||||
"buddy-update",
|
||||
"buddy-idle",
|
||||
"buddy-away"
|
||||
];
|
||||
|
||||
if (!val)
|
||||
events.forEach(function(aName) { os.removeObserver(this, aName); }, this);
|
||||
this._buddy.buddy.removeObserver(this);
|
||||
else
|
||||
events.forEach(function(aName) { os.addObserver(this, aName, false); }, this);
|
||||
val.buddy.addObserver(this);
|
||||
|
||||
return (this._buddy = val);
|
||||
]]>
|
||||
</setter>
|
||||
|
@ -243,9 +235,9 @@
|
|||
this.elt = aElt;
|
||||
|
||||
this.reset();
|
||||
let name = aBuddy.buddyName;
|
||||
let alias = aBuddy.buddyAlias;
|
||||
this.setAttribute("displayname", alias || name);
|
||||
let name = aBuddy.userName;
|
||||
let displayName = aBuddy.displayName;
|
||||
this.setAttribute("displayname", displayName);
|
||||
let account = aBuddy.account;
|
||||
this.setAttribute("iconPrpl", account.protocol.iconBaseURI + "icon.png");
|
||||
if (aElt.hasAttribute("status"))
|
||||
|
@ -254,7 +246,7 @@
|
|||
this.removeAttribute("status");
|
||||
this.setBuddyIcon(aBuddy.buddyIconFilename);
|
||||
|
||||
if (alias != name)
|
||||
if (displayName != name)
|
||||
this.addRow(this.bundle.GetStringFromName("buddy.screenname"), name);
|
||||
|
||||
this.addRow(this.bundle.GetStringFromName("buddy.account"), account.name);
|
||||
|
@ -313,7 +305,10 @@
|
|||
<parameter name="aData"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (aSubject == this.buddy)
|
||||
if (aSubject == this.buddy &&
|
||||
(aTopic == "account-buddy-status-changed" ||
|
||||
aTopic == "account-display-name-changed" ||
|
||||
aTopic == "account-buddy-icon-changed"))
|
||||
this.updateTooltipFromBuddy(this.buddy, this.elt);
|
||||
]]>
|
||||
</body>
|
||||
|
@ -333,14 +328,7 @@
|
|||
if (elt.localName != "buddy")
|
||||
return false;
|
||||
|
||||
var buddy;
|
||||
for (var id in elt.accounts) {
|
||||
buddy = elt.accounts[id];
|
||||
break;
|
||||
}
|
||||
if (!buddy)
|
||||
throw "no account in this buddy!";
|
||||
|
||||
let buddy = elt.contact.preferredBuddy.preferredAccountBuddy;
|
||||
return updateTooltipFromBuddy(buddy, elt);
|
||||
]]>
|
||||
</handler>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="group" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
|
||||
<content>
|
||||
<content persist="closed">
|
||||
<xul:image class="twisty"/>
|
||||
<xul:label flex="1" crop="end" xbl:inherits="value=name"/>
|
||||
</content>
|
||||
|
@ -67,13 +67,17 @@
|
|||
<parameter name="aGroup"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.groupId = aGroup.id;
|
||||
this.tag = aGroup;
|
||||
this.name = aGroup.name;
|
||||
this.buddies = [ ];
|
||||
let contacts = this.tag.getContacts();
|
||||
if (!this.showOffline)
|
||||
contacts = contacts.filter((function (c) c.online), this);
|
||||
if (!contacts.length)
|
||||
return false;
|
||||
|
||||
this.groupId = aGroup.id;
|
||||
this.contacts = [ ];
|
||||
this.contactsById = {};
|
||||
this.setAttribute("id", "group" + this.groupId);
|
||||
this.setAttribute("name", this.name);
|
||||
this.setAttribute("persist", "closed");
|
||||
|
||||
// restore the potential persisted value
|
||||
var source = Components.classes["@mozilla.org/rdf/datasource;1?name=local-store"]
|
||||
|
@ -82,22 +86,13 @@
|
|||
.getService(Components.interfaces.nsIRDFService);
|
||||
var elt = RDF.GetResource(document.location + "#" + this.id);
|
||||
if (source.HasAssertion(elt, RDF.GetResource("closed"),
|
||||
RDF.GetLiteral("true"), true)) {
|
||||
RDF.GetLiteral("true"), true))
|
||||
this.setAttribute("closed", "true");
|
||||
this._updateGroupLabel();
|
||||
this._updateClosedState(true);
|
||||
}
|
||||
|
||||
let empty = true;
|
||||
this.tag.getBuddies().forEach(function (aBuddy) {
|
||||
aBuddy.getAccountBuddies()
|
||||
.filter(function (b) (this.showOffline || b.online) && b.tag.id == this.groupId, this)
|
||||
.forEach(function(b) { this.addBuddy(b); empty = false; }, this);
|
||||
}, this);
|
||||
|
||||
if (!empty)
|
||||
this.tag.addObserver(this);
|
||||
return !empty;
|
||||
contacts.forEach(this.addContact, this);
|
||||
this._updateGroupLabel();
|
||||
this.tag.addObserver(this);
|
||||
return true;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -112,16 +107,16 @@
|
|||
<![CDATA[
|
||||
this._showOffline = val;
|
||||
if (val) {
|
||||
this.tag.getBuddies().forEach(function (aBuddy) {
|
||||
aBuddy.getAccountBuddies()
|
||||
.filter(function (b) !b.online && b.tag.id == this.groupId, this)
|
||||
.forEach(function(b) { this.addBuddy(b); }, this);
|
||||
}, this);
|
||||
this.tag.getContacts().filter(function (c) !c.online)
|
||||
.forEach(this.addContact, this);
|
||||
this._updateGroupLabel();
|
||||
}
|
||||
else {
|
||||
this.buddies.filter(function (b) b.removeOfflineAccounts())
|
||||
.forEach(function (b) { b.parentNode.removeChild(b);
|
||||
this.removeBuddy(b); }, this);
|
||||
this.contacts.filter(function (b) !b.contact.online)
|
||||
.forEach(function (b) {
|
||||
b.finishRemoveNode();
|
||||
b.parentNode.removeChild(b);
|
||||
}, this);
|
||||
}
|
||||
return val;
|
||||
]]>
|
||||
|
@ -135,58 +130,49 @@
|
|||
<parameter name="aData"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.showOffline) {
|
||||
if (aTopic == "buddy-added")
|
||||
this.addBuddy(aSubject);
|
||||
else if (aTopic == "buddy-deleted" || aTopic == "buddy-removed-from-group")
|
||||
this.signedOff(aSubject);
|
||||
else
|
||||
this.updateBuddy(aSubject);
|
||||
if (this.showOffline && (aTopic == "contact-added" ||
|
||||
aTopic == "contact-moved-in") ||
|
||||
!this.showOffline && aSubject.online &&
|
||||
(aTopic == "contact-availability-changed" ||
|
||||
aTopic == "contact-added" ||
|
||||
aTopic == "contact-moved-in")) {
|
||||
this.addContact(aSubject);
|
||||
this._updateGroupLabel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTopic == "buddy-signed-on" || (aTopic == "buddy-added" && aSubject.online))
|
||||
this.addBuddy(aSubject);
|
||||
else if (aTopic == "buddy-signed-off" || aTopic == "buddy-removed")
|
||||
this.signedOff(aSubject);
|
||||
else if (aTopic == "buddy-idle" || aTopic == "buddy-away" || aTopic == "buddy-alias")
|
||||
this.updateBuddy(aSubject);
|
||||
if (aTopic == "contact-no-longer-dummy") {
|
||||
let oldId = parseInt(aData);
|
||||
if (this.contactsById.hasOwnProperty(oldId)) {
|
||||
let contact = this.contactsById[oldId];
|
||||
delete this.contactsById[oldId];
|
||||
this.contactsById[contact.contact.id] = contact;
|
||||
}
|
||||
return;
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="addBuddy">
|
||||
<parameter name="aPab"/>
|
||||
<method name="addContact">
|
||||
<parameter name="aContact"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var buddy = aPab.buddy;
|
||||
if (this.contactsById.hasOwnProperty(aContact.id))
|
||||
return;
|
||||
|
||||
var buddyElt;
|
||||
for (var i = 0; i < this.buddies.length; ++i) {
|
||||
if (this.buddies[i].buddyId == buddy.id) {
|
||||
buddyElt = this.buddies[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
var buddyElt = document.createElement("buddy");
|
||||
if (this.hasAttribute("closed"))
|
||||
buddyElt.setAttribute("collapsed", "true");
|
||||
|
||||
if (!buddyElt) {
|
||||
buddyElt = document.createElement("buddy");
|
||||
let groupIsHidden = this.hasAttribute("closed");
|
||||
if (groupIsHidden)
|
||||
buddyElt.setAttribute("collapsed", "true");
|
||||
var last = this;
|
||||
if (this.contacts.length)
|
||||
last = this.contacts[this.contacts.length - 1];
|
||||
|
||||
var last = this;
|
||||
if (this.buddies.length)
|
||||
last = this.buddies[this.buddies.length - 1];
|
||||
|
||||
this.parentNode.insertBefore(buddyElt, last.nextSibling);
|
||||
buddyElt.build(aPab.buddy, this);
|
||||
this.buddies.push(buddyElt);
|
||||
|
||||
if (groupIsHidden)
|
||||
this._updateGroupLabel();
|
||||
}
|
||||
buddyElt.addAccount(aPab);
|
||||
this.parentNode.insertBefore(buddyElt, last.nextSibling);
|
||||
buddyElt.build(aContact, this);
|
||||
this.contacts.push(buddyElt);
|
||||
this.contactsById[aContact.id] = buddyElt;
|
||||
|
||||
if (this.hasAttribute("collapsing")) {
|
||||
this.removeAttribute("collapsing");
|
||||
|
@ -200,35 +186,20 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="signedOff">
|
||||
<parameter name="aPab"/>
|
||||
<method name="removeContact">
|
||||
<parameter name="aContact"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var buddy = aPab.buddy;
|
||||
|
||||
for (var i = 0; i < this.buddies.length; ++i) {
|
||||
if (this.buddies[i].buddyId == buddy.id) {
|
||||
this.buddies[i].removeAccount(aPab);
|
||||
return;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="removeBuddy">
|
||||
<parameter name="aBuddy"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var i = this.buddies.indexOf(aBuddy);
|
||||
var i = this.contacts.indexOf(aContact);
|
||||
if (i == -1)
|
||||
throw "Removing a buddy that doesn't exist?";
|
||||
throw "Removing a contact that doesn't exist?";
|
||||
|
||||
// remove the buddy from the array
|
||||
this.buddies.splice(i, 1);
|
||||
// remove the contact from the array
|
||||
this.contacts.splice(i, 1);
|
||||
delete this.contactsById[aContact.contact.id];
|
||||
|
||||
// Check if some buddy remain in the group, if empty remove it
|
||||
if (!this.buddies.length) {
|
||||
// Check if some contacts remain in the group, if empty remove it
|
||||
if (!this.contacts.length) {
|
||||
this.style.height = (this.animHeight = this.clientHeight) + "px";
|
||||
this.setAttribute("collapsing", "true");
|
||||
this.animInterval = setInterval(this._animateCollapse, this.animationInterval, this);
|
||||
|
@ -258,28 +229,12 @@
|
|||
</body>
|
||||
</method>
|
||||
|
||||
<method name="updateBuddy">
|
||||
<parameter name="aPab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var buddy = aPab.buddy;
|
||||
|
||||
for (var i = 0; i < this.buddies.length; ++i) {
|
||||
if (this.buddies[i].buddyId == buddy.id) {
|
||||
this.buddies[i].update();
|
||||
return;
|
||||
}
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="_updateClosedState">
|
||||
<parameter name="aClosed"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
for (let i = 0; i < this.buddies.length; ++i)
|
||||
this.buddies[i].collapsed = aClosed;
|
||||
for each (let contact in this.contacts)
|
||||
contact.collapsed = aClosed;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
@ -304,9 +259,9 @@
|
|||
<method name="_updateGroupLabel">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let name = this.name;
|
||||
let name = this.tag.name;
|
||||
if (this.hasAttribute("closed"))
|
||||
name += " (" + this.buddies.length + ")";
|
||||
name += " (" + this.contacts.length + ")";
|
||||
|
||||
this.setAttribute("name", name);
|
||||
]]>
|
||||
|
|
|
@ -0,0 +1,312 @@
|
|||
/* ***** 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
|
||||
* 2010.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Florian QUEZE <florian@instantbird.org>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* 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 ***** */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
#include "nsIObserver.idl"
|
||||
#include "nsISimpleEnumerator.idl"
|
||||
#include "purpleIConversation.idl"
|
||||
|
||||
interface imITag;
|
||||
interface imIContact;
|
||||
interface imIBuddy;
|
||||
interface imIAccountBuddy;
|
||||
interface purpleIProtocol;
|
||||
|
||||
[scriptable, uuid(f1619b49-310b-47aa-ab1c-238aba084c62)]
|
||||
interface imIContactsService: nsISupports {
|
||||
void initContacts();
|
||||
void unInitContacts();
|
||||
|
||||
imIContact getContactById(in long aId);
|
||||
imIBuddy getBuddyById(in long aId);
|
||||
imIBuddy getBuddyByNameAndProtocol(in AUTF8String aNormalizedName,
|
||||
in purpleIProtocol aPrpl);
|
||||
|
||||
// These 3 functions are called by the protocol plugins when
|
||||
// synchronizing the buddy list with the server stored list,
|
||||
// or after user operations have been performed.
|
||||
void accountBuddyAdded(in imIAccountBuddy aAccountBuddy);
|
||||
void accountBuddyRemoved(in imIAccountBuddy aAccountBuddy);
|
||||
void accountBuddyMoved(in imIAccountBuddy aAccountBuddy,
|
||||
in imITag aOldTag, in imITag aNewTag);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f799a9c2-23f2-4fd1-96fb-515bad238f8c)]
|
||||
interface imITagsService: nsISupports {
|
||||
// Create a new tags or return the existing one if it already exists
|
||||
imITag createTag(in AUTF8String aName);
|
||||
// Get an existing tag by (numeric) id. Returns null if not found.
|
||||
imITag getTagById(in long aId);
|
||||
// Get an existing tag by name (will do an SQL query). Returns null
|
||||
// if not found.
|
||||
imITag getTagByName(in AUTF8String aName);
|
||||
// Get an array of all existing tags.
|
||||
void getTags([optional] out unsigned long tagCount,
|
||||
[retval, array, size_is(tagCount)] out imITag tags);
|
||||
};
|
||||
|
||||
[scriptable, uuid(c211e5e2-f0a4-4a86-9e4c-3f6b905628a5)]
|
||||
interface imITag: nsISupports {
|
||||
readonly attribute long id;
|
||||
attribute AUTF8String name;
|
||||
|
||||
// Get an array of all the contacts associated with this tag.
|
||||
// Contacts can either "have the tag" (added by user action) or
|
||||
// have inherited the tag because it was the server side group for
|
||||
// one of the AccountBuddy of the contact.
|
||||
void getContacts([optional] out unsigned long contactCount,
|
||||
[retval, array, size_is(contactCount)] out imIContact contacts);
|
||||
|
||||
void addObserver(in nsIObserver aObserver);
|
||||
void removeObserver(in nsIObserver aObserver);
|
||||
/* Observers will be notified of changes related to the contacts
|
||||
* that have the tag: contact-*, buddy-*, account-buddy-*
|
||||
* notifications forwarded respectively from the imIContact,
|
||||
* imIBuddy and imIAccountBuddy instances.
|
||||
*/
|
||||
|
||||
// Exposed for add-on authors. All usage by Instantbird will come from
|
||||
// the imITag implementation so it wasn't required to expose this.
|
||||
// This can be used to dispatch custom notifications to the
|
||||
// observers of the tag.
|
||||
void notifyObservers(in nsISupports aObj, in string aEvent,
|
||||
[optional] in wstring aData);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f13dc4fc-5334-45cb-aa58-a92851955e55)]
|
||||
interface imIStatusInfo: nsISupports {
|
||||
// Name suitable for display in the UI. Can either be the username,
|
||||
// the server side alias, or the user set local alias of the contact.
|
||||
readonly attribute AUTF8String displayName;
|
||||
readonly attribute AUTF8String buddyIconFilename;
|
||||
|
||||
const short STATUS_UNKNOWN = 0;
|
||||
const short STATUS_OFFLINE = 1;
|
||||
const short STATUS_INVISIBLE = 2;
|
||||
const short STATUS_MOBILE = 3;
|
||||
const short STATUS_IDLE = 4;
|
||||
const short STATUS_AWAY = 5;
|
||||
const short STATUS_UNAVAILABLE = 6;
|
||||
const short STATUS_AVAILABLE = 7;
|
||||
|
||||
// numerical value used to compare the availability of two buddies
|
||||
// based on their current status.
|
||||
// Use it only for immediate comparisons, do not store the value,
|
||||
// it can change between versions for a same status of the buddy.
|
||||
readonly attribute long statusType;
|
||||
|
||||
readonly attribute boolean online; // (statusType > STATUS_OFFLINE)
|
||||
readonly attribute boolean available; // (statusType == STATUS_AVAILABLE)
|
||||
readonly attribute boolean idle; // (statusType == STATUS_IDLE)
|
||||
readonly attribute boolean mobile; // (statusType == STATUS_MOBILE)
|
||||
|
||||
readonly attribute AUTF8String statusText;
|
||||
|
||||
// Gives more detail to compare the availability of two buddies with the same
|
||||
// status type.
|
||||
// Example: 2 buddies may have been idle for various amounts of times.
|
||||
readonly attribute long availabilityDetails;
|
||||
|
||||
// True if the buddy is online or if the account supports sending
|
||||
// offline messages to the buddy.
|
||||
readonly attribute boolean canSendMessage;
|
||||
|
||||
// enumerator of purpleTooltipInfo components
|
||||
nsISimpleEnumerator getTooltipInfo();
|
||||
|
||||
// Will select the buddy automatically based on availability, and
|
||||
// the account (if needed) based on the account order in the account
|
||||
// manager.
|
||||
purpleIConversation createConversation();
|
||||
};
|
||||
|
||||
|
||||
[scriptable, uuid(f585b0df-f6ad-40d5-9de4-c58b14af13e4)]
|
||||
interface imIContact: imIStatusInfo {
|
||||
// The id will be positive if the contact is real (stored in the
|
||||
// SQLite database) and negative if the instance is a dummy contact
|
||||
// holding only a single buddy without aliases or additional tags.
|
||||
readonly attribute long id;
|
||||
attribute AUTF8String alias;
|
||||
|
||||
void getTags([optional] out unsigned long tagCount,
|
||||
[retval, array, size_is(tagCount)] out imITag tags);
|
||||
|
||||
void move(in imITag aOldTag, in imITag aNewtag);
|
||||
|
||||
readonly attribute imIBuddy preferredBuddy;
|
||||
void getBuddies([optional] out unsigned long buddyCount,
|
||||
[retval, array, size_is(buddyCount)] out imIBuddy buddies);
|
||||
|
||||
// remove the contact from the buddy list. Will also remove the
|
||||
// associated buddies.
|
||||
void remove();
|
||||
|
||||
void addObserver(in nsIObserver aObserver);
|
||||
void removeObserver(in nsIObserver aObserver);
|
||||
/* Observers will be notified of changes related to the contact.
|
||||
* aSubject will point to the imIContact object
|
||||
* (with some exceptions for contact-moved-* notifications).
|
||||
*
|
||||
* Fired notifications:
|
||||
* contact-availability-changed
|
||||
* when either statusType or availabilityDetails has changed.
|
||||
* contact-signed-on
|
||||
* contact-signed-off
|
||||
* contact-status-changed
|
||||
* when either statusType or statusText has changed.
|
||||
* contact-display-name-changed
|
||||
* when the alias (or serverAlias of the most available buddy if
|
||||
* no alias is set) has changed.
|
||||
* The old display name is provided in aData.
|
||||
* contact-preferred-buddy-changed
|
||||
* The buddy that would be favored to start a conversation has changed.
|
||||
* contact-moved, contact-moved-in, contact-moved-out
|
||||
* contact-moved is notified through the observer service
|
||||
* contact-moved-in is notified to
|
||||
* - the contact observers (aSubject is the new tag)
|
||||
* - the new tag (aSubject is the contact instance)
|
||||
* contact-moved-out is notified to
|
||||
* - the contact observers (aSubject is the old tag)
|
||||
* - the old tag (aSubject is the contact instance)
|
||||
* contact-no-longer-dummy
|
||||
* When a real contact is created to replace a dummy contact.
|
||||
* The old (negative) id will be given in aData.
|
||||
* See also the comment above the 'id' attribute.
|
||||
*
|
||||
* Observers will also receive all the (forwarded) notifications
|
||||
* from the linked buddies (imIBuddy instances) and their account
|
||||
* buddies (imIAccountBuddy instances).
|
||||
*/
|
||||
|
||||
// Exposed for add-on authors. All usage by Instantbird will come from
|
||||
// the imIContact implementation so it wasn't required to expose this.
|
||||
// This can be used to dispatch custom notifications to the
|
||||
// observers of the contact and its tags.
|
||||
// The notification will also be forwarded to the observer service.
|
||||
void notifyObservers(in nsISupports aObj, in string aEvent,
|
||||
[optional] in wstring aData);
|
||||
};
|
||||
|
||||
|
||||
[scriptable, uuid(fea582a0-3839-4d80-bcab-0ff82ae8f97f)]
|
||||
interface imIBuddy: imIStatusInfo {
|
||||
readonly attribute long id;
|
||||
readonly attribute purpleIProtocol protocol;
|
||||
readonly attribute AUTF8String userName; // may be formatted
|
||||
readonly attribute AUTF8String normalizedName; // normalized userName
|
||||
// The optional server alias is in displayName (inherited from imIStatusInfo)
|
||||
// displayName = serverAlias || userName.
|
||||
|
||||
readonly attribute imIContact contact;
|
||||
readonly attribute imIAccountBuddy preferredAccountBuddy;
|
||||
void getAccountBuddies([optional] out unsigned long accountBuddyCount,
|
||||
[retval, array, size_is(accountBuddyCount)] out imIAccountBuddy accountBuddies);
|
||||
|
||||
// remove the buddy from the buddy list. If the contact becomes empty, it will be removed too.
|
||||
void remove();
|
||||
|
||||
void addObserver(in nsIObserver aObserver);
|
||||
void removeObserver(in nsIObserver aObserver);
|
||||
/* Observers will be notified of changes related to the buddy.
|
||||
* aSubject will point to the imIBuddy object.
|
||||
* Fired notifications:
|
||||
* buddy-availability-changed
|
||||
* when either statusType or availabilityDetails has changed.
|
||||
* buddy-signed-on
|
||||
* buddy-signed-off
|
||||
* buddy-status-changed
|
||||
* when either statusType or statusText has changed.
|
||||
* buddy-display-name-changed
|
||||
* when the serverAlias has changed.
|
||||
* The old display name is provided in aData.
|
||||
* buddy-preferred-account-changed
|
||||
* The account that would be favored to start a conversation has changed.
|
||||
*
|
||||
* Observers will also receive all the (forwarded) notifications
|
||||
* from the linked account buddies (imIAccountBuddy instances).
|
||||
*/
|
||||
|
||||
// Exposed for add-on authors. All usage by Instantbird will come from
|
||||
// the imIBuddy implementation so it wasn't required to expose this.
|
||||
// This can be used to dispatch custom notifications to the
|
||||
// observers of the buddy, its contact and its tags.
|
||||
// The contact will forward the notifications to the observer service.
|
||||
void notifyObservers(in nsISupports aObj, in string aEvent,
|
||||
[optional] in wstring aData);
|
||||
|
||||
// observe should only be called by the imIAccountBuddy
|
||||
// implementations to report changes.
|
||||
void observe(in nsISupports aObj, in string aEvent,
|
||||
[optional] in wstring aData);
|
||||
};
|
||||
|
||||
/* imIAccountBuddy implementations can send notifications to their buddy:
|
||||
*
|
||||
* For all of them, aSubject points to the imIAccountBuddy object.
|
||||
*
|
||||
* Supported notifications:
|
||||
* account-buddy-availability-changed
|
||||
* when either statusType or availabilityDetails has changed.
|
||||
* account-buddy-signed-on
|
||||
* account-buddy-signed-off
|
||||
* account-buddy-status-changed
|
||||
* when either statusType or statusText has changed.
|
||||
* account-buddy-display-name-changed
|
||||
* when the serverAlias has changed.
|
||||
* The old display name is provided in aData.
|
||||
*
|
||||
* All notifications (even unsupported ones) will be forwarded to the contact,
|
||||
* its tags and nsObserverService.
|
||||
*/
|
||||
[scriptable, uuid(114d24ff-56a1-4fd6-9822-4992efb6e036)]
|
||||
interface imIAccountBuddy: imIStatusInfo {
|
||||
// The setter is for internal use only. buddy will be set by the
|
||||
// Contacts service when accountBuddyAdded is called on this
|
||||
// instance of imIAccountBuddy.
|
||||
attribute imIBuddy buddy;
|
||||
readonly attribute purpleIAccount account;
|
||||
// Setting the tag will move the buddy to a different group on the
|
||||
// server-stored buddy list.
|
||||
attribute imITag tag;
|
||||
readonly attribute AUTF8String userName;
|
||||
readonly attribute AUTF8String normalizedName;
|
||||
attribute AUTF8String serverAlias;
|
||||
|
||||
// remove the buddy from the buddy list of this account.
|
||||
void remove();
|
||||
};
|
|
@ -40,7 +40,7 @@
|
|||
#include "nsISimpleEnumerator.idl"
|
||||
#include "nsIObserver.idl"
|
||||
|
||||
interface purpleIAccountBuddy;
|
||||
interface imIAccountBuddy;
|
||||
interface purpleIAccount;
|
||||
interface nsIURI;
|
||||
interface nsIDOMDocument;
|
||||
|
@ -102,7 +102,7 @@ interface purpleIConversation: nsISupports {
|
|||
interface purpleIConvIM: purpleIConversation {
|
||||
|
||||
/* The buddy at the remote end of the conversation */
|
||||
readonly attribute purpleIAccountBuddy buddy;
|
||||
readonly attribute imIAccountBuddy buddy;
|
||||
|
||||
/* The remote buddy is not currently typing */
|
||||
const short NOT_TYPING = 0;
|
||||
|
|
|
@ -0,0 +1,924 @@
|
|||
/* ***** 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
|
||||
* 2010.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Florian QUEZE <florian@instantbird.org>.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* 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 ***** */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "obs",
|
||||
"@mozilla.org/observer-service;1",
|
||||
"nsIObserverService");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "prefs",
|
||||
"@mozilla.org/preferences-service;1",
|
||||
"nsIPrefBranch");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "pcs",
|
||||
"@instantbird.org/purple/core;1",
|
||||
"purpleICoreService");
|
||||
XPCOMUtils.defineLazyGetter(this, "DBConn", function() pcs.storageConnection);
|
||||
|
||||
|
||||
var AccountsById = { };
|
||||
function getAccountById(aId) {
|
||||
if (AccountsById.hasOwnProperty(aId))
|
||||
return AccountsById[aId];
|
||||
|
||||
let account = null;
|
||||
try {
|
||||
account = pcs.getAccountByNumericId(aId);
|
||||
} catch (x) { /* Not found */ }
|
||||
|
||||
AccountsById[aId] = account;
|
||||
return account;
|
||||
}
|
||||
|
||||
function TagsService() { }
|
||||
TagsService.prototype = {
|
||||
get wrappedJSObject() this,
|
||||
createTag: function(aName) {
|
||||
// If the tag already exists, we don't want to create a duplicate.
|
||||
let tag = this.getTagByName(aName);
|
||||
if (tag)
|
||||
return tag;
|
||||
|
||||
let statement = DBConn.createStatement("INSERT INTO tags (name, position) VALUES(:name, 0)");
|
||||
statement.params.name = aName;
|
||||
statement.executeStep();
|
||||
|
||||
tag = new Tag(DBConn.lastInsertRowID, aName);
|
||||
Tags.push(tag);
|
||||
return tag;
|
||||
},
|
||||
// Get an existing tag by (numeric) id. Returns null if not found.
|
||||
getTagById: function(aId) TagsById[aId],
|
||||
// Get an existing tag by name (will do an SQL query). Returns null
|
||||
// if not found.
|
||||
getTagByName: function(aName) {
|
||||
let statement = DBConn.createStatement("SELECT id FROM tags where name = :name");
|
||||
statement.params.name = aName;
|
||||
if (!statement.executeStep())
|
||||
return null;
|
||||
return this.getTagById(statement.row.id);
|
||||
},
|
||||
// Get an array of all existing tags.
|
||||
getTags: function(aTagCount) {
|
||||
if (aTagCount)
|
||||
aTagCount.value = Tags.length;
|
||||
return Tags;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.imITagsService]),
|
||||
classDescription: "Tags",
|
||||
classID: Components.ID("{1fa92237-4303-4384-b8ac-4e65b50810a5}"),
|
||||
contractID: "@instantbird.org/purple/tags-service;1"
|
||||
};
|
||||
|
||||
// TODO move into the tagsService
|
||||
var Tags = [];
|
||||
var TagsById = { };
|
||||
|
||||
function Tag(aId, aName) {
|
||||
this._id = aId;
|
||||
this._name = aName;
|
||||
this._contacts = [];
|
||||
this._observers = [];
|
||||
|
||||
TagsById[this.id] = this;
|
||||
}
|
||||
Tag.prototype = {
|
||||
get id() this._id,
|
||||
get name() this._name,
|
||||
set name(aNewName) {
|
||||
var statement = DBConn.createStatement("UPDATE tags SET name = :name WHERE id = :id");
|
||||
statement.params.name = aNewName;
|
||||
statement.params.id = this._id;
|
||||
statement.execute();
|
||||
|
||||
//FIXME move the account buddies if some use this tag as their group
|
||||
return aNewName;
|
||||
},
|
||||
getContacts: function(aContactCount) {
|
||||
let contacts = this._contacts.filter(function(c) !c._empty);
|
||||
if (aContactCount)
|
||||
aContactCount.value = contacts.length;
|
||||
return contacts;
|
||||
},
|
||||
_addContact: function (aContact) {
|
||||
this._contacts.push(aContact);
|
||||
},
|
||||
_removeContact: function (aContact) {
|
||||
let index = this._contacts.indexOf(aContact);
|
||||
if (index != -1)
|
||||
this._contacts.splice(index, 1);
|
||||
},
|
||||
|
||||
addObserver: function(aObserver) {
|
||||
if (this._observers.indexOf(aObserver) == -1)
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
removeObserver: function(aObserver) {
|
||||
let index = this._observers.indexOf(aObserver);
|
||||
if (index != -1)
|
||||
this._observers.splice(index, 1);
|
||||
},
|
||||
notifyObservers: function(aSubject, aTopic, aData) {
|
||||
for each (let observer in this._observers)
|
||||
observer.observe(aSubject, aTopic, aData);
|
||||
},
|
||||
|
||||
getInterfaces: function(countRef) {
|
||||
var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imITag];
|
||||
countRef.value = interfaces.length;
|
||||
return interfaces;
|
||||
},
|
||||
getHelperForLanguage: function(language) null,
|
||||
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
|
||||
flags: 0,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.imITag, Ci.nsIClassInfo])
|
||||
};
|
||||
|
||||
var ContactsById = { };
|
||||
var LastDummyContactId = 0;
|
||||
function Contact(aId, aAlias) {
|
||||
// Assign a negative id to dummy contacts that have a single buddy
|
||||
this._id = aId || --LastDummyContactId;
|
||||
this._alias = aAlias;
|
||||
this._tags = [];
|
||||
this._buddies = [];
|
||||
this._observers = [];
|
||||
|
||||
ContactsById[this._id] = this;
|
||||
}
|
||||
Contact.prototype = {
|
||||
_id: 0,
|
||||
get id() this._id,
|
||||
get alias() this._alias,
|
||||
set alias(aNewAlias) {
|
||||
if (this._id < 0) {
|
||||
// Create a real contact for this dummy contact
|
||||
let statement = DBConn.createStatement("INSERT INTO contacts (alias) VALUES(:alias)");
|
||||
statement.params.alias = aNewAlias;
|
||||
statement.execute();
|
||||
delete ContactsById[this._id];
|
||||
let oldId = this._id;
|
||||
this._id = DBConn.lastInsertRowID;
|
||||
ContactsById[this._id] = this;
|
||||
this._notifyObservers("no-longer-dummy", oldId.toString());
|
||||
// Update the contact_id for the single existing buddy of this contact
|
||||
statement = DBConn.createStatement("UPDATE buddies SET contact_id = :id WHERE id = :buddy_id");
|
||||
statement.params.id = this._id;
|
||||
statement.params.buddy_id = this._buddies[0].id;
|
||||
statement.execute();
|
||||
}
|
||||
else {
|
||||
let statement = DBConn.createStatement("UPDATE contacts SET alias = :alias WHERE id = :id");
|
||||
statement.params.alias = aNewAlias;
|
||||
statement.params.id = this._id;
|
||||
statement.execute();
|
||||
}
|
||||
let oldDisplayName = this.displayName;
|
||||
this._alias = aNewAlias;
|
||||
this._notifyObservers("display-name-changed", oldDisplayName);
|
||||
for each (let buddy in this._buddies)
|
||||
for each (let accountBuddy in buddy._accounts)
|
||||
accountBuddy.serverAlias = aNewAlias;
|
||||
return aNewAlias;
|
||||
},
|
||||
|
||||
getTags: function(aTagCount) {
|
||||
if (aTagCount)
|
||||
aTagCount.value = this._tags.length;
|
||||
return this._tags;
|
||||
},
|
||||
hasTag: function(aTag) this._tags.some(function (t) t.id == aTag.id),
|
||||
_massMove: false,
|
||||
move: function(aOldTag, aNewtag) {
|
||||
if (!this.hasTag(aOldTag))
|
||||
throw "Attempting to remove a tag that the contact doesn't have";
|
||||
|
||||
// FIXME: If the old tag is part of the local tags, just change that tag.
|
||||
// Doesn't matter as long as there's no UI to add a tag to a contact.
|
||||
// Later we will want to keep 2 arrays (one for local tags and one
|
||||
// for inherited tags) and concatenate them during getTags calls.
|
||||
|
||||
this._massMove = true;
|
||||
let moved = false;
|
||||
this._buddies.forEach(function (aBuddy) {
|
||||
aBuddy._accounts.forEach(function (aAccountBuddy) {
|
||||
if (aAccountBuddy.tag.id == aOldTag.id) {
|
||||
if (aBuddy._accounts.some(function(ab)
|
||||
ab.account.numericId == aAccountBuddy.account.numericId &&
|
||||
ab.tag.id == aNewtag.id)) {
|
||||
aAccountBuddy.remove();
|
||||
moved = true;
|
||||
}
|
||||
else {
|
||||
try {
|
||||
aAccountBuddy.tag = aNewtag;
|
||||
moved = true;
|
||||
} catch (e) {
|
||||
// Ignore failures. Some protocol plugins may not implement this.
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
this._massMove = false;
|
||||
if (moved)
|
||||
this._moved(aOldTag, aNewtag);
|
||||
},
|
||||
_isTagInherited: function(aTag) {
|
||||
for each (let buddy in this._buddies)
|
||||
for each (let accountBuddy in buddy._accounts)
|
||||
if (accountBuddy.tag.id == aTag.id)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
_moved: function(aOldTag, aNewTag) {
|
||||
if (this._massMove)
|
||||
return;
|
||||
|
||||
// Avoid xpconnect wrappers.
|
||||
aNewTag = aNewTag && TagsById[aNewTag.id];
|
||||
aOldTag = aOldTag && TagsById[aOldTag.id];
|
||||
|
||||
// Decide what we need to do. Return early if nothing to do.
|
||||
let shouldRemove =
|
||||
aOldTag && this.hasTag(aOldTag) && !this._isTagInherited(aOldTag);
|
||||
let shouldAdd =
|
||||
aNewTag && !this.hasTag(aNewTag) && this._isTagInherited(aNewTag);
|
||||
if (!shouldRemove && !shouldAdd)
|
||||
return;
|
||||
|
||||
// Apply the changes.
|
||||
let tags = this._tags;
|
||||
if (shouldRemove) {
|
||||
tags = tags.filter(function(aTag) aTag.id != aOldTag.id);
|
||||
aOldTag._removeContact(this);
|
||||
}
|
||||
if (shouldAdd) {
|
||||
tags.push(aNewTag);
|
||||
aNewTag._addContact(this);
|
||||
}
|
||||
this._tags = tags;
|
||||
|
||||
// Finally, notify of the changes.
|
||||
if (shouldRemove) {
|
||||
aOldTag.notifyObservers(this, "contact-moved-out");
|
||||
for each (let observer in this._observers)
|
||||
observer.observe(aOldTag, "contact-moved-out");
|
||||
}
|
||||
if (shouldAdd) {
|
||||
aNewTag.notifyObservers(this, "contact-moved-in");
|
||||
for each (let observer in this._observers)
|
||||
observer.observe(aNewTag, "contact-moved-in");
|
||||
}
|
||||
obs.notifyObservers(this, "contact-moved", null);
|
||||
},
|
||||
|
||||
getBuddies: function(aBuddyCount) {
|
||||
if (aBuddyCount)
|
||||
aBuddyCount.value = this._buddies.length;
|
||||
return this._buddies;
|
||||
},
|
||||
get _empty() this._buddies.length == 0 ||
|
||||
this._buddies.every(function(b) b._empty),
|
||||
|
||||
remove: function() {
|
||||
for each (let buddy in this._buddies)
|
||||
buddy.remove();
|
||||
},
|
||||
|
||||
// imIStatusInfo implementation
|
||||
_preferredBuddy: null,
|
||||
get preferredBuddy() {
|
||||
if (!this._preferredBuddy)
|
||||
this._updatePreferredBuddy();
|
||||
return this._preferredBuddy;
|
||||
},
|
||||
set preferredBuddy(aBuddy) {
|
||||
let shouldNotify = this._preferredBuddy != null;
|
||||
let oldDisplayName =
|
||||
this._preferredBuddy && this._preferredBuddy.displayName;
|
||||
this._preferredBuddy = aBuddy;
|
||||
if (shouldNotify)
|
||||
this._notifyObservers("preferred-buddy-changed");
|
||||
if (oldDisplayName && this._preferredBuddy.displayName != oldDisplayName)
|
||||
this._notifyObservers("display-name-changed", oldDisplayName);
|
||||
this._updateStatus();
|
||||
},
|
||||
// aBuddy indicate which buddy's availability has changed.
|
||||
_updatePreferredBuddy: function(aBuddy) {
|
||||
if (aBuddy) {
|
||||
if (!this._preferredBuddy) {
|
||||
this.preferredBuddy = aBuddy;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aBuddy.id == this._preferredBuddy.id) {
|
||||
// The suggested buddy is already preferred, check if its
|
||||
// availability has changed.
|
||||
if (aBuddy.statusType > this._statusType ||
|
||||
(aBuddy.statusType == this._statusType &&
|
||||
aBuddy.availabilityDetails >= this._availabilityDetails)) {
|
||||
// keep the currently preferred buddy, only update the status.
|
||||
this._updateStatus();
|
||||
return;
|
||||
}
|
||||
// We aren't sure that the currently preferred buddy should
|
||||
// still be preferred. Let's go through the list!
|
||||
}
|
||||
else {
|
||||
// The suggested buddy is not currently preferred. If it is
|
||||
// more available, prefer it!
|
||||
if (aBuddy.statusType > this._statusType ||
|
||||
(aBuddy.statusType == this._statusType &&
|
||||
aBuddy.availabilityDetails > this._availabilityDetails))
|
||||
this.preferredBuddy = aBuddy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let preferred;
|
||||
//TODO take into account the order of the buddies in the contact.
|
||||
for each (let buddy in this._buddies) {
|
||||
if (!preferred || preferred.statusType < buddy.statusType ||
|
||||
(preferred.statusType == buddy.statusType &&
|
||||
preferred.availabilityDetails < buddy.availabilityDetails))
|
||||
preferred = buddy;
|
||||
}
|
||||
if (preferred && (!this._preferredBuddy ||
|
||||
preferred.id != this._preferredBuddy.id))
|
||||
this.preferredBuddy = preferred;
|
||||
},
|
||||
_updateStatus: function() {
|
||||
let buddy = this._preferredBuddy; // for convenience
|
||||
|
||||
// Decide which notifications should be fired.
|
||||
let notifications = [];
|
||||
if (this._statusType != buddy.statusType ||
|
||||
this._availabilityDetails != buddy.availabilityDetails)
|
||||
notifications.push("availability-changed");
|
||||
if (this._statusType != buddy.statusType ||
|
||||
this._statusText != buddy.statusText) {
|
||||
notifications.push("status-changed");
|
||||
if (this.online && buddy.statusType <= Ci.imIStatusInfo.STATUS_OFFLINE)
|
||||
notifications.push("signed-off");
|
||||
if (!this.online && buddy.statusType > Ci.imIStatusInfo.STATUS_OFFLINE)
|
||||
notifications.push("signed-on");
|
||||
}
|
||||
|
||||
// Actually change the stored status.
|
||||
[this._statusType, this._statusText, this._availabilityDetails] =
|
||||
[buddy.statusType, buddy.statusText, buddy.availabilityDetails];
|
||||
|
||||
// Fire the notifications.
|
||||
notifications.forEach(function(aTopic) {
|
||||
this._notifyObservers(aTopic);
|
||||
}, this);
|
||||
},
|
||||
get displayName() this._alias || this.preferredBuddy.displayName,
|
||||
get buddyIconFilename() this.preferredBuddy.buddyIconFileName,
|
||||
_statusType: 0,
|
||||
get statusType() this._statusType,
|
||||
get online() this.statusType > Ci.imIStatusInfo.STATUS_OFFLINE,
|
||||
get available() this.statusType == Ci.imIStatusInfo.STATUS_AVAILABLE,
|
||||
get idle() this.statusType == Ci.imIStatusInfo.STATUS_IDLE,
|
||||
get mobile() this.statusType == Ci.imIStatusInfo.STATUS_MOBILE,
|
||||
_statusText: "",
|
||||
get statusText() this._statusText,
|
||||
_availabilityDetails: 0,
|
||||
get availabilityDetails() this._availabilityDetails,
|
||||
get canSendMessage() this.preferredBuddy.canSendMessage,
|
||||
//XXX should we list the buddies in the tooltip?
|
||||
getTooltipInfo: function() this.preferredBuddy.getTooltipInfo(),
|
||||
createConversation: function() this.preferredBuddy.createConversation(),
|
||||
|
||||
addObserver: function(aObserver) {
|
||||
if (this._observers.indexOf(aObserver) == -1)
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
removeObserver: function(aObserver) {
|
||||
if (!this.hasOwnProperty("_observers"))
|
||||
return;
|
||||
|
||||
let index = this._observers.indexOf(aObserver);
|
||||
if (index != -1)
|
||||
this._observers.splice(index, 1);
|
||||
},
|
||||
// internal calls + calls from add-ons
|
||||
notifyObservers: function(aSubject, aTopic, aData) {
|
||||
for each (let observer in this._observers)
|
||||
observer.observe(aSubject, aTopic, aData);
|
||||
for each (let tag in this._tags)
|
||||
tag.notifyObservers(aSubject, aTopic, aData);
|
||||
obs.notifyObservers(aSubject, aTopic, aData);
|
||||
},
|
||||
_notifyObservers: function(aTopic, aData) {
|
||||
this.notifyObservers(this, "contact-" + aTopic, aData);
|
||||
},
|
||||
|
||||
// This is called by the imIBuddy implementations.
|
||||
_observe: function(aSubject, aTopic, aData) {
|
||||
// Forward the notification.
|
||||
this.notifyObservers(aSubject, aTopic, aData);
|
||||
|
||||
let isPreferredBuddy =
|
||||
aSubject instanceof Buddy && aSubject.id == this.preferredBuddy.id;
|
||||
switch (aTopic) {
|
||||
case "buddy-availability-changed":
|
||||
this._updatePreferredBuddy(aSubject);
|
||||
break;
|
||||
case "buddy-status-changed":
|
||||
if (isPreferredBuddy)
|
||||
this._updateStatus();
|
||||
break;
|
||||
case "buddy-display-name-changed":
|
||||
if (isPreferredBuddy && !this._alias)
|
||||
this._notifyObservers("display-name-changed", aData);
|
||||
break;
|
||||
case "buddy-added":
|
||||
// Currently buddies are always added in dummy empty contacts,
|
||||
// later we may want to check this._buddies.length == 1.
|
||||
this._notifyObservers("added");
|
||||
break;
|
||||
case "buddy-removed":
|
||||
if (this._buddies.length == 1) {
|
||||
if (this._id > 0) {
|
||||
let statement =
|
||||
DBConn.createStatement("DELETE FROM contacts WHERE id = :id");
|
||||
statement.params.id = this._id;
|
||||
statement.execute();
|
||||
}
|
||||
this._notifyObservers("removed");
|
||||
|
||||
delete ContactsById[this._id];
|
||||
for each (let tag in this._tags)
|
||||
tag._removeContact(this);
|
||||
delete this._tags;
|
||||
delete this._buddies;
|
||||
delete this._observers;
|
||||
}
|
||||
else {
|
||||
let index = this._buddies.indexOf(aSubject);
|
||||
if (index != -1)
|
||||
this._buddies.splice(index, 1);
|
||||
else
|
||||
throw "Removing an unknown buddy from contact " + this._id;
|
||||
if (this._preferredBuddy.id == aSubject.id) {
|
||||
this._preferredBuddy = null;
|
||||
this._updatePreferredBuddy();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getInterfaces: function(countRef) {
|
||||
var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imIContact];
|
||||
countRef.value = interfaces.length;
|
||||
return interfaces;
|
||||
},
|
||||
getHelperForLanguage: function(language) null,
|
||||
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
|
||||
flags: 0,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.imIContact, Ci.nsIClassInfo])
|
||||
};
|
||||
|
||||
var BuddiesById = { };
|
||||
function Buddy(aId, aKey, aName, aSrvAlias, aContactId) {
|
||||
this._id = aId;
|
||||
this._key = aKey;
|
||||
this._name = aName;
|
||||
if (aSrvAlias)
|
||||
this._srvAlias = aSrvAlias;
|
||||
this._accounts = [];
|
||||
this._observers = [];
|
||||
|
||||
if (aContactId)
|
||||
this._contact = ContactsById[aContactId];
|
||||
else
|
||||
this._contact = new Contact(null, null);
|
||||
|
||||
this._contact._buddies.push(this);
|
||||
|
||||
BuddiesById[this._id] = this;
|
||||
}
|
||||
Buddy.prototype = {
|
||||
get id() this._id,
|
||||
destroy: function() {
|
||||
delete this._accounts;
|
||||
delete this._observers;
|
||||
delete this._preferredAccount;
|
||||
},
|
||||
get protocol() this._accounts[0].account.protocol,
|
||||
get userName() this._name,
|
||||
get normalizedName() this._key,
|
||||
_srvAlias: "",
|
||||
_contact: null,
|
||||
get contact() this._contact,
|
||||
getAccountBuddies: function(aAccountBuddyCount) {
|
||||
if (aAccountBuddyCount)
|
||||
aAccountBuddyCount.value = this._accounts.length;
|
||||
return this._accounts;
|
||||
},
|
||||
|
||||
_addAccount: function(aAccountBuddy, aTag) {
|
||||
this._accounts.push(aAccountBuddy);
|
||||
let contact = this._contact;
|
||||
if (this._contact._tags.indexOf(aTag) == -1) {
|
||||
this._contact._tags.push(aTag);
|
||||
aTag._addContact(contact);
|
||||
}
|
||||
|
||||
if (!this._preferredAccount)
|
||||
this._preferredAccount = aAccountBuddy;
|
||||
},
|
||||
get _empty() this._accounts.length == 0,
|
||||
|
||||
remove: function() {
|
||||
for each (let account in this._accounts)
|
||||
account.remove();
|
||||
},
|
||||
|
||||
// imIStatusInfo implementation
|
||||
_preferredAccount: null,
|
||||
get preferredAccountBuddy() this._preferredAccount,
|
||||
_isPreferredAccount: function(aAccountBuddy)
|
||||
aAccountBuddy.account.numericId == this._preferredAccount.account.numericId,
|
||||
set preferredAccount(aAccount) {
|
||||
let oldDisplayName =
|
||||
this._preferredAccount && this._preferredAccount.displayName;
|
||||
this._preferredAccount = aAccount;
|
||||
this._notifyObservers("preferred-account-changed");
|
||||
if (oldDisplayName && this._preferredAccount.displayName != oldDisplayName)
|
||||
this._notifyObservers("display-name-changed", oldDisplayName);
|
||||
this._updateStatus();
|
||||
},
|
||||
// aAccount indicate which account's availability has changed.
|
||||
_updatePreferredAccount: function(aAccount) {
|
||||
if (aAccount) {
|
||||
if (aAccount.account.numericId == this._preferredAccount.account.numericId) {
|
||||
// The suggested account is already preferred, check if its
|
||||
// availability has changed.
|
||||
if (aAccount.statusType > this._statusType ||
|
||||
(aAccount.statusType == this._statusType &&
|
||||
aAccount.availabilityDetails >= this._availabilityDetails)) {
|
||||
// keep the currently preferred account, only update the status.
|
||||
this._updateStatus();
|
||||
return;
|
||||
}
|
||||
// We aren't sure that the currently preferred account should
|
||||
// still be preferred. Let's go through the list!
|
||||
}
|
||||
else {
|
||||
// The suggested account is not currently preferred. If it is
|
||||
// more available, prefer it!
|
||||
if (aAccount.statusType > this._statusType ||
|
||||
(aAccount.statusType == this._statusType &&
|
||||
aAccount.availabilityDetails > this._availabilityDetails))
|
||||
this.preferredAccount = aAccount;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let preferred;
|
||||
//TODO take into account the order of the account-manager list.
|
||||
for each (let account in this._accounts) {
|
||||
if (!preferred || preferred.statusType < account.statusType ||
|
||||
(preferred.statusType == account.statusType &&
|
||||
preferred.availabilityDetails < account.availabilityDetails))
|
||||
preferred = account;
|
||||
}
|
||||
if (!this._preferredAccount) {
|
||||
if (preferred)
|
||||
this.preferredAccount = preferred;
|
||||
return;
|
||||
}
|
||||
if (preferred.account.numericId != this._preferredAccount.account.numericId)
|
||||
this.preferredAccount = preferred;
|
||||
else
|
||||
this._updateStatus();
|
||||
},
|
||||
_updateStatus: function() {
|
||||
let account = this._preferredAccount; // for convenience
|
||||
|
||||
// Decide which notifications should be fired.
|
||||
let notifications = [];
|
||||
if (this._statusType != account.statusType ||
|
||||
this._availabilityDetails != account.availabilityDetails)
|
||||
notifications.push("availability-changed");
|
||||
if (this._statusType != account.statusType ||
|
||||
this._statusText != account.statusText) {
|
||||
notifications.push("status-changed");
|
||||
if (this.online && account.statusType <= Ci.imIStatusInfo.STATUS_OFFLINE)
|
||||
notifications.push("signed-off");
|
||||
if (!this.online && account.statusType > Ci.imIStatusInfo.STATUS_OFFLINE)
|
||||
notifications.push("signed-on");
|
||||
}
|
||||
|
||||
// Actually change the stored status.
|
||||
[this._statusType, this._statusText, this._availabilityDetails] =
|
||||
[account.statusType, account.statusText, account.availabilityDetails];
|
||||
|
||||
// Fire the notifications.
|
||||
notifications.forEach(function(aTopic) {
|
||||
this._notifyObservers(aTopic);
|
||||
}, this);
|
||||
},
|
||||
get displayName() this._preferredAccount && this._preferredAccount.displayName ||
|
||||
this._srvAlias || this._name,
|
||||
get buddyIconFilename() this._preferredAccount.buddyIconFileName,
|
||||
_statusType: 0,
|
||||
get statusType() this._statusType,
|
||||
get online() this.statusType > Ci.imIStatusInfo.STATUS_OFFLINE,
|
||||
get available() this.statusType == Ci.imIStatusInfo.STATUS_AVAILABLE,
|
||||
get idle() this.statusType == Ci.imIStatusInfo.STATUS_IDLE,
|
||||
get mobile() this.statusType == Ci.imIStatusInfo.STATUS_MOBILE,
|
||||
_statusText: "",
|
||||
get statusText() this._statusText,
|
||||
_availabilityDetails: 0,
|
||||
get availabilityDetails() this._availabilityDetails,
|
||||
get canSendMessage() this._preferredAccount.canSendMessage,
|
||||
//XXX should we list the accounts in the tooltip?
|
||||
getTooltipInfo: function() this._preferredAccount.getTooltipInfo(),
|
||||
createConversation: function() this._preferredAccount.createConversation(),
|
||||
|
||||
addObserver: function(aObserver) {
|
||||
if (this._observers.indexOf(aObserver) == -1)
|
||||
this._observers.push(aObserver);
|
||||
},
|
||||
removeObserver: function(aObserver) {
|
||||
let index = this._observers.indexOf(aObserver);
|
||||
if (index != -1)
|
||||
this._observers.splice(index, 1);
|
||||
},
|
||||
// internal calls + calls from add-ons
|
||||
notifyObservers: function(aSubject, aTopic, aData) {
|
||||
for each (let observer in this._observers)
|
||||
observer.observe(aSubject, aTopic, aData);
|
||||
this._contact._observe(aSubject, aTopic, aData);
|
||||
},
|
||||
_notifyObservers: function(aTopic, aData) {
|
||||
this.notifyObservers(this, "buddy-" + aTopic, aData);
|
||||
},
|
||||
|
||||
// This is called by the imIAccountBuddy implementations.
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
// Forward the notification.
|
||||
this.notifyObservers(aSubject, aTopic, aData);
|
||||
|
||||
switch (aTopic) {
|
||||
case "account-buddy-availability-changed":
|
||||
this._updatePreferredAccount(aSubject);
|
||||
break;
|
||||
case "account-buddy-status-changed":
|
||||
if (this._isPreferredAccount(aSubject))
|
||||
this._updateStatus();
|
||||
break;
|
||||
case "account-buddy-display-name-changed":
|
||||
if (this._isPreferredAccount(aSubject)) {
|
||||
this._srvAlias =
|
||||
this.displayName != this.userName ? this.displayName : "";
|
||||
let statement =
|
||||
DBConn.createStatement("UPDATE buddies SET srv_alias = :srvAlias " +
|
||||
"WHERE id = :buddyId");
|
||||
statement.params.buddyId = this.id;
|
||||
statement.params.srvAlias = this._srvAlias;
|
||||
statement.executeAsync();
|
||||
this._notifyObservers("display-name-changed", aData);
|
||||
}
|
||||
break;
|
||||
case "account-buddy-added":
|
||||
if (this._accounts.length == 0) {
|
||||
// Add the new account in the empty buddy instance.
|
||||
// The TagsById hack is to bypass the xpconnect wrapper.
|
||||
this._addAccount(aSubject, TagsById[aSubject.tag.id]);
|
||||
this._notifyObservers("added");
|
||||
}
|
||||
else {
|
||||
this._accounts.push(aSubject);
|
||||
this.contact._moved(null, aSubject.tag);
|
||||
this._updatePreferredAccount(aSubject);
|
||||
}
|
||||
break;
|
||||
case "account-buddy-removed":
|
||||
if (this._accounts.length == 1) {
|
||||
let statement =
|
||||
DBConn.createStatement("DELETE FROM buddies WHERE id = :id");
|
||||
statement.params.id = this.id;
|
||||
statement.execute();
|
||||
|
||||
this._notifyObservers("removed");
|
||||
|
||||
delete BuddiesById[this._id];
|
||||
this.destroy();
|
||||
}
|
||||
else {
|
||||
this._accounts = this._accounts.filter(function (ab) {
|
||||
return (ab.account.numericId != aSubject.account.numericId ||
|
||||
ab.tag.id != aSubject.tag.id);
|
||||
});
|
||||
if (this._preferredAccount.account.numericId == aSubject.account.numericId &&
|
||||
this._preferredAccount.tag.id == aSubject.tag.id) {
|
||||
this._preferredAccount = null;
|
||||
this._updatePreferredAccount();
|
||||
}
|
||||
this.contact._moved(aSubject.tag);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
getInterfaces: function(countRef) {
|
||||
var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imIBuddy];
|
||||
countRef.value = interfaces.length;
|
||||
return interfaces;
|
||||
},
|
||||
getHelperForLanguage: function(language) null,
|
||||
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
|
||||
flags: 0,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.imIBuddy, Ci.nsIClassInfo])
|
||||
};
|
||||
|
||||
|
||||
function ContactsService() { }
|
||||
ContactsService.prototype = {
|
||||
initContacts: function() {
|
||||
var statement = DBConn.createStatement("SELECT id, name FROM tags");
|
||||
while (statement.executeStep())
|
||||
Tags.push(new Tag(statement.getInt32(0), statement.getUTF8String(1)));
|
||||
|
||||
statement = DBConn.createStatement("SELECT id, alias FROM contacts");
|
||||
while (statement.executeStep())
|
||||
new Contact(statement.getInt32(0), statement.getUTF8String(1));
|
||||
|
||||
statement =
|
||||
DBConn.createStatement("SELECT contact_id, tag_id FROM contact_tag");
|
||||
while (statement.executeStep()) {
|
||||
let contact = ContactsById[statement.getInt32(0)];
|
||||
let tag = TagsById(statement.getInt32(1));
|
||||
contact._tags.push(tag);
|
||||
tag._addContact(contact);
|
||||
}
|
||||
|
||||
statement = DBConn.createStatement("SELECT id, key, name, srv_alias, contact_id FROM buddies");
|
||||
while (statement.executeStep())
|
||||
new Buddy(statement.getInt32(0), statement.getUTF8String(1),
|
||||
statement.getUTF8String(2), statement.getUTF8String(3),
|
||||
statement.getInt32(4));
|
||||
// FIXME is there a way to enforce that all AccountBuddies of a Buddy have the same protocol?
|
||||
|
||||
statement = DBConn.createStatement("SELECT account_id, buddy_id, tag_id FROM account_buddy");
|
||||
while (statement.executeStep()) {
|
||||
let account = getAccountById(statement.getInt32(0));
|
||||
let buddy = BuddiesById[statement.getInt32(1)];
|
||||
let tag = TagsById[statement.getInt32(2)];
|
||||
try {
|
||||
let ab = account.loadBuddy(buddy, tag);
|
||||
if (ab)
|
||||
buddy._addAccount(ab, tag);
|
||||
} catch (e) {
|
||||
// FIXME ab shouldn't be NULL (once purpleAccount is finished)
|
||||
// It currently doesn't work write with unknown protocols.
|
||||
Components.utils.reportError(e);
|
||||
dump(e + "\n");
|
||||
}
|
||||
}
|
||||
},
|
||||
unInitContacts: function() {
|
||||
AccountsById = { };
|
||||
Tags = [];
|
||||
TagsById = { };
|
||||
// Avoid shutdown leaks caused by references to native components
|
||||
// implementing imIAccountBuddy.
|
||||
for each (let buddy in BuddiesById)
|
||||
buddy.destroy();
|
||||
BuddiesById = { };
|
||||
ContactsById = { };
|
||||
},
|
||||
|
||||
getContactById: function(aId) ContactsById[aId],
|
||||
getBuddyById: function(aId) BuddiesById[aId],
|
||||
getBuddyByNameAndProtocol: function (aNormalizedName, aPrpl) {
|
||||
let statement =
|
||||
DBConn.createStatement("SELECT b.id FROM buddies b " +
|
||||
"JOIN account_buddy ab ON buddy_id = b.id " +
|
||||
"JOIN accounts a ON account_id = a.id " +
|
||||
"WHERE b.key = :buddyName and a.prpl = :prplId");
|
||||
statement.params.buddyName = aNormalizedName;
|
||||
statement.params.prplId = aPrpl.id;
|
||||
if (!statement.executeStep())
|
||||
return null;
|
||||
return BuddiesById[statement.row.id];
|
||||
},
|
||||
|
||||
accountBuddyAdded: function(aAccountBuddy) {
|
||||
let account = aAccountBuddy.account;
|
||||
let normalizedName = aAccountBuddy.normalizedName;
|
||||
let buddy = this.getBuddyByNameAndProtocol(normalizedName, account.protocol);
|
||||
if (!buddy) {
|
||||
let statement =
|
||||
DBConn.createStatement("INSERT INTO buddies " +
|
||||
"(key, name, srv_alias, position) " +
|
||||
"VALUES(:key, :name, :srvAlias, 0)");
|
||||
let name = aAccountBuddy.userName;
|
||||
let srvAlias = aAccountBuddy.serverAlias;
|
||||
statement.params.key = normalizedName;
|
||||
statement.params.name = name;
|
||||
statement.params.srvAlias = srvAlias;
|
||||
statement.execute();
|
||||
buddy =
|
||||
new Buddy(DBConn.lastInsertRowID, normalizedName, name, srvAlias, 0);
|
||||
}
|
||||
|
||||
// Initialize the 'buddy' field of the imIAccountBuddy instance.
|
||||
aAccountBuddy.buddy = buddy;
|
||||
|
||||
// Store the new account buddy.
|
||||
let statement =
|
||||
DBConn.createStatement("INSERT INTO account_buddy " +
|
||||
"(account_id, buddy_id, tag_id) " +
|
||||
"VALUES(:accountId, :buddyId, :tagId)");
|
||||
statement.params.accountId = account.numericId;
|
||||
statement.params.buddyId = buddy.id;
|
||||
statement.params.tagId = aAccountBuddy.tag.id;
|
||||
statement.execute();
|
||||
|
||||
// Fire the notifications.
|
||||
buddy.observe(aAccountBuddy, "account-buddy-added");
|
||||
},
|
||||
accountBuddyRemoved: function(aAccountBuddy) {
|
||||
let buddy = aAccountBuddy.buddy;
|
||||
let statement =
|
||||
DBConn.createStatement("DELETE FROM account_buddy " +
|
||||
"WHERE account_id = :accountId AND " +
|
||||
"buddy_id = :buddyId AND " +
|
||||
"tag_id = :tagId");
|
||||
statement.params.accountId = aAccountBuddy.account.numericId;
|
||||
statement.params.buddyId = buddy.id;
|
||||
statement.params.tagId = aAccountBuddy.tag.id;
|
||||
statement.execute();
|
||||
|
||||
buddy.observe(aAccountBuddy, "account-buddy-removed");
|
||||
},
|
||||
|
||||
accountBuddyMoved: function(aAccountBuddy, aOldTag, aNewTag) {
|
||||
let buddy = aAccountBuddy.buddy;
|
||||
let statement =
|
||||
DBConn.createStatement("UPDATE account_buddy " +
|
||||
"SET tag_id = :newTagId " +
|
||||
"WHERE account_id = :accountId AND " +
|
||||
"buddy_id = :buddyId AND " +
|
||||
"tag_id = :oldTagId");
|
||||
statement.params.accountId = aAccountBuddy.account.numericId;
|
||||
statement.params.buddyId = buddy.id;
|
||||
statement.params.oldTagId = aOldTag.id;
|
||||
statement.params.newTagId = aNewTag.id;
|
||||
statement.execute();
|
||||
|
||||
buddy.observe(aAccountBuddy, "account-buddy-moved");
|
||||
ContactsById[buddy.contact.id]._moved(aOldTag, aNewTag);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.imIContactsService]),
|
||||
classDescription: "Contacts",
|
||||
classID: Components.ID("{8c3725dd-ee26-489d-8135-736015af8c7f}"),
|
||||
contractID: "@instantbird.org/purple/contacts-service;1"
|
||||
};
|
||||
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([ContactsService,
|
||||
TagsService]);
|
|
@ -0,0 +1,4 @@
|
|||
component {8c3725dd-ee26-489d-8135-736015af8c7f} imContacts.js
|
||||
contract @instantbird.org/purple/contacts-service;1 {8c3725dd-ee26-489d-8135-736015af8c7f}
|
||||
component {1fa92237-4303-4384-b8ac-4e65b50810a5} imContacts.js
|
||||
contract @instantbird.org/purple/tags-service;1 {1fa92237-4303-4384-b8ac-4e65b50810a5}
|
|
@ -41,6 +41,7 @@ var EXPORTED_SYMBOLS = [
|
|||
"nsSimpleEnumerator",
|
||||
"EmptyEnumerator",
|
||||
"GenericAccountPrototype",
|
||||
"GenericAccountBuddyPrototype",
|
||||
"GenericConversationPrototype",
|
||||
"GenericProtocolPrototype",
|
||||
"ForwardProtocolPrototype",
|
||||
|
@ -133,8 +134,14 @@ const GenericAccountPrototype = {
|
|||
cancelReconnection: function() this._base.cancelReconnection(),
|
||||
createConversation: function(aName) this._base.createConversation(aName),
|
||||
addBuddy: function(aTag, aName) this._base.addBuddy(aTag, aName),
|
||||
loadBuddy: function(aBuddy, aName, aAlias, aServerAlias, aTag)
|
||||
this._base.loadBuddy(aBuddy, aName, aAlias, aServerAlias, aTag),
|
||||
loadBuddy: function(aBuddy, aTag) {
|
||||
try {
|
||||
return new AccountBuddy(this, aBuddy, aTag) ;
|
||||
} catch (x) {
|
||||
dump(x + "\n");
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getChatRoomFields: function() this._base.getChatRoomFields(),
|
||||
getChatRoomDefaultFieldValues: function(aDefaultChatName)
|
||||
this._base.getChatRoomDefaultFieldValues(aDefaultChatName),
|
||||
|
@ -196,6 +203,133 @@ const GenericAccountPrototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.purpleIAccount, Ci.nsIClassInfo])
|
||||
};
|
||||
|
||||
|
||||
var GenericAccountBuddyPrototype = {
|
||||
_init: function(aAccount, aBuddy, aTag) {
|
||||
this._tag = aTag;
|
||||
this._account = aAccount;
|
||||
this._buddy = aBuddy;
|
||||
},
|
||||
|
||||
get account() this._account,
|
||||
set buddy(aBuddy) {
|
||||
if (this._buddy)
|
||||
throw Components.results.NS_ERROR_ALREADY_INITIALIZED;
|
||||
this._buddy = aBuddy;
|
||||
},
|
||||
get buddy() this._buddy,
|
||||
get tag() this._tag,
|
||||
set tag(aNewTag) {
|
||||
let oldTag = this._tag;
|
||||
this._tag = aNewTag;
|
||||
Components.classes["@instantbird.org/purple/contacts-service;1"]
|
||||
.getService(Ci.imIContactsService)
|
||||
.accountBuddyMoved(this, oldTag, aNewTag);
|
||||
},
|
||||
|
||||
_notifyObservers: function(aTopic, aData) {
|
||||
this._buddy.observe(this, "account-buddy-" + aTopic, aData);
|
||||
},
|
||||
|
||||
get userName() this._buddy.userName, // FIXME
|
||||
get normalizedName() this._buddy.normalizedName, //FIXME
|
||||
_serverAlias: "",
|
||||
get serverAlias() this._serverAlias,
|
||||
set serverAlias(aNewAlias) {
|
||||
let old = this.displayName;
|
||||
this._serverAlias = aNewAlias;
|
||||
this._notifyObservers("display-name-changed", old);
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
Components.classes["@instantbird.org/purple/contacts-service;1"]
|
||||
.getService(Ci.imIContactsService)
|
||||
.accountBuddyRemoved(this);
|
||||
},
|
||||
|
||||
// imIStatusInfo implementation
|
||||
get displayName() this.serverAlias || this.userName,
|
||||
_buddyIconFileName: "",
|
||||
get buddyIconFilename() this._buddyIconFileName,
|
||||
set buddyIconFilename(aNewFileName) {
|
||||
this._buddyIconFileName = aNewFileName;
|
||||
this._notifyObservers("icon-changed");
|
||||
},
|
||||
_statusType: 0,
|
||||
get statusType() this._statusType,
|
||||
get online() this._statusType > Ci.imIStatusInfo.STATUS_OFFLINE,
|
||||
get available() this._statusType == Ci.imIStatusInfo.STATUS_AVAILABLE,
|
||||
get idle() this._statusType == Ci.imIStatusInfo.STATUS_IDLE,
|
||||
get mobile() this._statusType == Ci.imIStatusInfo.STATUS_MOBILE,
|
||||
_statusText: "",
|
||||
get statusText() this._statusText,
|
||||
|
||||
// This is for use by the protocol plugin, it's not exposed in the
|
||||
// imIStatusInfo interface.
|
||||
// All parameters are optional and will be ignored if they are null
|
||||
// or undefined.
|
||||
setStatus: function(aStatusType, aStatusText, aAvailabilityDetails) {
|
||||
// Ignore omitted parameters.
|
||||
if (aStatusType === undefined || aStatusType === null)
|
||||
aStatusType = this._statusType;
|
||||
if (aStatusText === undefined || aStatusText === null)
|
||||
aStatusText = this._statusText;
|
||||
if (aAvailabilityDetails === undefined || aAvailabilityDetails === null)
|
||||
aAvailabilityDetails = this._availabilityDetails;
|
||||
|
||||
// Decide which notifications should be fired.
|
||||
let notifications = [];
|
||||
if (this._statusType != aStatusType ||
|
||||
this._availabilityDetails != aAvailabilityDetails)
|
||||
notifications.push("availability-changed");
|
||||
if (this._statusType != aStatusType ||
|
||||
this._statusText != aStatusText) {
|
||||
notifications.push("status-changed");
|
||||
if (this.online && aStatusType <= Ci.imIStatusInfo.STATUS_OFFLINE)
|
||||
notifications.push("signed-off");
|
||||
if (!this.online && aStatusType > Ci.imIStatusInfo.STATUS_OFFLINE)
|
||||
notifications.push("signed-on");
|
||||
}
|
||||
|
||||
// Actually change the stored status.
|
||||
[this._statusType, this._statusText, this._availabilityDetails] =
|
||||
[aStatusType, aStatusText, aAvailabilityDetails];
|
||||
|
||||
// Fire the notifications.
|
||||
notifications.forEach(function(aTopic) {
|
||||
this._notifyObservers(aTopic);
|
||||
}, this);
|
||||
},
|
||||
|
||||
_availabilityDetails: 0,
|
||||
get availabilityDetails() this._availabilityDetails,
|
||||
|
||||
get canSendMessage() this.online /*|| this.account.canSendOfflineMessage(this) */,
|
||||
|
||||
getTooltipInfo: function() {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
createConversation: function() {
|
||||
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
|
||||
},
|
||||
|
||||
getInterfaces: function(countRef) {
|
||||
var interfaces = [Ci.nsIClassInfo, Ci.nsISupports, Ci.imIAccountBuddy];
|
||||
countRef.value = interfaces.length;
|
||||
return interfaces;
|
||||
},
|
||||
getHelperForLanguage: function(language) null,
|
||||
implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
|
||||
flags: 0,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.imIAccountBuddy, Ci.nsIClassInfo])
|
||||
};
|
||||
|
||||
function AccountBuddy(aAccount, aBuddy, aTag) {
|
||||
this._init(aAccount, aBuddy, aTag);
|
||||
}
|
||||
AccountBuddy.prototype = GenericAccountBuddyPrototype;
|
||||
|
||||
|
||||
function Message(aWho, aMessage, aObject)
|
||||
{
|
||||
this.id = ++Message.prototype._lastId;
|
||||
|
@ -372,6 +506,7 @@ const ForwardProtocolPrototype = {
|
|||
let proto = this;
|
||||
let account = {
|
||||
_base: this.base.getAccount(aKey, aName),
|
||||
loadBuddy: function(aBuddy, aTag) this._base.loadBuddy(aBuddy, aTag),
|
||||
get normalizedName() this._base.normalizedName,
|
||||
get protocol() proto
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче