717 строки
26 KiB
JavaScript
717 строки
26 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
Cu.import("resource:///modules/imServices.jsm");
|
|
Cu.import("resource://gre/modules/DownloadUtils.jsm");
|
|
|
|
// This is the list of notifications that the account manager window observes
|
|
const events = [
|
|
"prpl-quit",
|
|
"account-list-updated",
|
|
"account-added",
|
|
"account-updated",
|
|
"account-removed",
|
|
"account-connected",
|
|
"account-connecting",
|
|
"account-disconnected",
|
|
"account-disconnecting",
|
|
"account-connect-progress",
|
|
"account-connect-error",
|
|
"autologin-processed",
|
|
"status-changed",
|
|
"network:offline-status-changed"
|
|
];
|
|
|
|
var gAccountManager = {
|
|
// Sets the delay after connect() or disconnect() during which
|
|
// it is impossible to perform disconnect() and connect()
|
|
_disabledDelay: 500,
|
|
disableTimerID: 0,
|
|
_connectedLabelInterval: 0,
|
|
load: function am_load() {
|
|
// Wait until the password service is ready before offering anything.
|
|
Services.logins.initializationPromise.then(() => {
|
|
this.accountList = document.getElementById("accountlist");
|
|
let defaultID;
|
|
for (let acc in this.getAccounts()) {
|
|
var elt = document.createElement("richlistitem");
|
|
this.accountList.appendChild(elt);
|
|
elt.build(acc);
|
|
if (!defaultID && acc.firstConnectionState == acc.FIRST_CONNECTION_CRASHED)
|
|
defaultID = acc.id;
|
|
}
|
|
for each (let event in events)
|
|
Services.obs.addObserver(this, event, false);
|
|
if (!this.accountList.getRowCount())
|
|
// This is horrible, but it works. Otherwise (at least on mac)
|
|
// the wizard is not centered relatively to the account manager
|
|
setTimeout(function() { gAccountManager.new(); }, 0);
|
|
else {
|
|
// we have accounts, show the list
|
|
document.getElementById("accountsDesk").selectedIndex = 1;
|
|
|
|
// ensure an account is selected
|
|
if (defaultID)
|
|
this.selectAccount(defaultID);
|
|
else
|
|
this.accountList.selectedIndex = 0;
|
|
}
|
|
|
|
this.setAutoLoginNotification();
|
|
|
|
this.accountList.addEventListener("keypress", this.onKeyPress, true);
|
|
window.addEventListener("unload", this.unload.bind(this));
|
|
this._connectedLabelInterval = setInterval(this.updateConnectedLabels, 60000);
|
|
}, () => {
|
|
this.close();
|
|
});
|
|
},
|
|
unload: function am_unload() {
|
|
clearInterval(this._connectedLabelInterval);
|
|
for each (let event in events)
|
|
Services.obs.removeObserver(this, event);
|
|
},
|
|
_updateAccountList: function am__updateAccountList() {
|
|
let accountList = this.accountList;
|
|
let i = 0;
|
|
for (let acc in this.getAccounts()) {
|
|
let oldItem = accountList.getItemAtIndex(i);
|
|
if (oldItem.id != acc.id) {
|
|
let accElt = document.getElementById(acc.id);
|
|
accountList.insertBefore(accElt, oldItem);
|
|
accElt.restoreItems();
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (accountList.itemCount == 0) {
|
|
// Focus the "New Account" button if there are no accounts left.
|
|
document.getElementById("newaccount").focus();
|
|
// Return early, otherwise we'll run into an 'undefined property' strict
|
|
// warning when trying to focus the buttons. Fixes bug 408.
|
|
return;
|
|
}
|
|
|
|
// The selected item is still selected
|
|
accountList.selectedItem.buttons.setFocus();
|
|
accountList.ensureSelectedElementIsVisible();
|
|
|
|
// We need to refresh the disabled menu items
|
|
this.disableCommandItems();
|
|
},
|
|
observe: function am_observe(aObject, aTopic, aData) {
|
|
if (aTopic == "prpl-quit") {
|
|
// libpurple is being uninitialized. We don't need the account
|
|
// manager window anymore, close it.
|
|
this.close();
|
|
return;
|
|
}
|
|
else if (aTopic == "autologin-processed") {
|
|
var notification = document.getElementById("accountsNotificationBox")
|
|
.getNotificationWithValue("autoLoginStatus");
|
|
if (notification)
|
|
notification.close();
|
|
return;
|
|
}
|
|
else if (aTopic == "network:offline-status-changed") {
|
|
this.setOffline(aData == "offline");
|
|
return;
|
|
}
|
|
else if (aTopic == "status-changed") {
|
|
this.setOffline(aObject.statusType == Ci.imIStatusInfo.STATUS_OFFLINE);
|
|
return;
|
|
}
|
|
else if (aTopic == "account-list-updated") {
|
|
this._updateAccountList();
|
|
return;
|
|
}
|
|
|
|
// The following notification handlers need an account.
|
|
aObject.QueryInterface(Ci.imIAccount);
|
|
|
|
if (aTopic == "account-added") {
|
|
document.getElementById("accountsDesk").selectedIndex = 1;
|
|
var elt = document.createElement("richlistitem");
|
|
this.accountList.appendChild(elt);
|
|
elt.build(aObject);
|
|
if (this.accountList.getRowCount() == 1)
|
|
this.accountList.selectedIndex = 0;
|
|
}
|
|
else if (aTopic == "account-removed") {
|
|
var elt = document.getElementById(aObject.id);
|
|
elt.destroy();
|
|
if (!elt.selected) {
|
|
elt.remove();
|
|
return;
|
|
}
|
|
// The currently selected element is removed,
|
|
// ensure another element gets selected (if the list is not empty)
|
|
var selectedIndex = this.accountList.selectedIndex;
|
|
// Prevent errors if the timer is active and the account deleted
|
|
clearTimeout(this.disableTimerID);
|
|
this.disableTimerID = 0;
|
|
elt.remove();
|
|
var count = this.accountList.getRowCount();
|
|
if (!count) {
|
|
document.getElementById("accountsDesk").selectedIndex = 0;
|
|
return;
|
|
}
|
|
if (selectedIndex == count)
|
|
--selectedIndex;
|
|
this.accountList.selectedIndex = selectedIndex;
|
|
}
|
|
else if (aTopic == "account-updated") {
|
|
document.getElementById(aObject.id).build(aObject);
|
|
this.disableCommandItems();
|
|
}
|
|
else if (aTopic == "account-connect-progress")
|
|
document.getElementById(aObject.id).updateConnectionState();
|
|
else if (aTopic == "account-connect-error")
|
|
document.getElementById(aObject.id).updateConnectionError();
|
|
else {
|
|
const stateEvents = {
|
|
"account-connected": "connected",
|
|
"account-connecting": "connecting",
|
|
"account-disconnected": "disconnected",
|
|
"account-disconnecting": "disconnecting"
|
|
};
|
|
if (aTopic in stateEvents) {
|
|
let elt = document.getElementById(aObject.id);
|
|
if (!elt)
|
|
return; // probably disconnecting a removed account.
|
|
|
|
if (aTopic == "account-connecting") {
|
|
elt.removeAttribute("error");
|
|
elt.updateConnectionState();
|
|
}
|
|
else {
|
|
if (aTopic == "account-connected")
|
|
elt.refreshConnectedLabel();
|
|
}
|
|
|
|
elt.setAttribute("state", stateEvents[aTopic]);
|
|
}
|
|
}
|
|
},
|
|
cancelReconnection: function am_cancelReconnection() {
|
|
this.accountList.selectedItem.cancelReconnection();
|
|
},
|
|
connect: function am_connect() {
|
|
let account = this.accountList.selectedItem.account;
|
|
if (account.disconnected) {
|
|
this.temporarilyDisableButtons();
|
|
account.connect();
|
|
}
|
|
},
|
|
disconnect: function am_disconnect() {
|
|
let account = this.accountList.selectedItem.account;
|
|
if (account.connected || account.connecting) {
|
|
this.temporarilyDisableButtons();
|
|
account.disconnect();
|
|
}
|
|
},
|
|
addException: function am_addException() {
|
|
let account = this.accountList.selectedItem.account;
|
|
let prplAccount = account.prplAccount;
|
|
if (!account.disconnected || !prplAccount.connectionTarget)
|
|
return;
|
|
|
|
// Open the Gecko SSL exception dialog.
|
|
let params = {
|
|
exceptionAdded: false,
|
|
sslStatus: prplAccount.sslStatus,
|
|
prefetchCert: true,
|
|
location: prplAccount.connectionTarget
|
|
};
|
|
window.openDialog("chrome://pippki/content/exceptionDialog.xul", "",
|
|
"chrome,centerscreen,modal", params);
|
|
// Reconnect the account if an exception was added.
|
|
if (params.exceptionAdded)
|
|
account.connect();
|
|
},
|
|
copyDebugLog: function am_copyDebugLog() {
|
|
let account = this.accountList.selectedItem.account;
|
|
let text = account.getDebugMessages().map(function(dbgMsg) {
|
|
let m = dbgMsg.message;
|
|
const dateServ = Cc["@mozilla.org/intl/scriptabledateformat;1"]
|
|
.getService(Ci.nsIScriptableDateFormat);
|
|
let time = new Date(m.timeStamp);
|
|
time = dateServ.FormatDateTime("", dateServ.dateFormatShort,
|
|
dateServ.timeFormatSeconds,
|
|
time.getFullYear(), time.getMonth() + 1,
|
|
time.getDate(), time.getHours(),
|
|
time.getMinutes(), time.getSeconds());
|
|
let level = dbgMsg.logLevel;
|
|
if (!level)
|
|
return "(" + m.errorMessage + ")";
|
|
if (level == dbgMsg.LEVEL_ERROR)
|
|
level = "ERROR";
|
|
else if (level == dbgMsg.LEVEL_WARNING)
|
|
level = "WARN.";
|
|
else if (level == dbgMsg.LEVEL_LOG)
|
|
level = "LOG ";
|
|
else
|
|
level = "DEBUG"
|
|
return "[" + time + "] " + level + " (@ " + m.sourceLine +
|
|
" " + m.sourceName + ":" + m.lineNumber + ")\n" +
|
|
m.errorMessage;
|
|
}).join("\n");
|
|
Cc["@mozilla.org/widget/clipboardhelper;1"]
|
|
.getService(Ci.nsIClipboardHelper).copyString(text);
|
|
},
|
|
showDebugLog: function am_showDebugLog() {
|
|
if (!("Core" in window))
|
|
Cu.import("resource:///modules/ibCore.jsm");
|
|
Core.showDebugLog(this.accountList.selectedItem.account.id);
|
|
},
|
|
updateConnectedLabels: function am_updateConnectedLabels() {
|
|
for (let i = 0; i < gAccountManager.accountList.itemCount; ++i) {
|
|
let item = gAccountManager.accountList.getItemAtIndex(i);
|
|
if (item.account.connected)
|
|
item.refreshConnectedLabel();
|
|
}
|
|
},
|
|
/* This function disables the connect/disconnect buttons for
|
|
* `this._disabledDelay` ms before calling disableCommandItems to restore
|
|
* the state of the buttons.
|
|
*/
|
|
temporarilyDisableButtons: function am_temporarilyDisableButtons() {
|
|
document.getElementById("cmd_disconnect").setAttribute("disabled", "true");
|
|
document.getElementById("cmd_connect").setAttribute("disabled", "true");
|
|
clearTimeout(this.disableTimerID);
|
|
this.accountList.focus();
|
|
this.disableTimerID = setTimeout(function(aItem) {
|
|
gAccountManager.disableTimerID = 0;
|
|
gAccountManager.disableCommandItems();
|
|
aItem.buttons.setFocus();
|
|
}, this._disabledDelay, this.accountList.selectedItem);
|
|
},
|
|
|
|
delete: function am_delete() {
|
|
var selectedItem = this.accountList.selectedItem;
|
|
if (!selectedItem)
|
|
return;
|
|
|
|
var showPrompt =
|
|
Services.prefs.getBoolPref("messenger.accounts.promptOnDelete");
|
|
if (showPrompt) {
|
|
var prompts = Services.prompt;
|
|
var bundle = document.getElementById("accountsBundle");
|
|
var promptTitle = bundle.getString("account.deletePrompt.title");
|
|
var promptMessage = bundle.getString("account.deletePrompt.message");
|
|
var promptCheckbox = bundle.getString("account.deletePrompt.checkbox");
|
|
var deleteButton = bundle.getString("account.deletePrompt.button");
|
|
|
|
var checkbox = {};
|
|
var flags = prompts.BUTTON_TITLE_IS_STRING * prompts.BUTTON_POS_0 +
|
|
prompts.BUTTON_TITLE_CANCEL * prompts.BUTTON_POS_1 +
|
|
prompts.BUTTON_POS_1_DEFAULT;
|
|
if (prompts.confirmEx(window, promptTitle, promptMessage, flags,
|
|
deleteButton, null, null, promptCheckbox, checkbox))
|
|
return;
|
|
|
|
if (checkbox.value)
|
|
Services.prefs.setBoolPref("messenger.accounts.promptOnDelete", false);
|
|
}
|
|
|
|
Services.accounts.deleteAccount(selectedItem.id);
|
|
},
|
|
new: function am_new() {
|
|
this.openDialog("chrome://instantbird/content/accountWizard.xul");
|
|
},
|
|
edit: function am_edit() {
|
|
this.openDialog("chrome://instantbird/content/account.xul",
|
|
this.accountList.selectedItem.account);
|
|
},
|
|
autologin: function am_autologin() {
|
|
var elt = this.accountList.selectedItem;
|
|
elt.autoLogin = !elt.autoLogin;
|
|
},
|
|
close: function am_close() {
|
|
// If a modal dialog is opened, we can't close this window now
|
|
if (this.modalDialog)
|
|
setTimeout(function() { window.close();}, 0);
|
|
else
|
|
window.close();
|
|
},
|
|
|
|
/* This function disables or enables the currently selected button and
|
|
the corresponding context menu item */
|
|
disableCommandItems: function am_disableCommandItems() {
|
|
let accountList = this.accountList;
|
|
let selectedItem = accountList.selectedItem;
|
|
// When opening the account manager, if accounts have errors, we
|
|
// can be called during build(), before any item is selected.
|
|
// In this case, just return early.
|
|
if (!selectedItem)
|
|
return;
|
|
|
|
// If the timer that disables the button (for a short time) already exists,
|
|
// we don't want to interfere and set the button as enabled.
|
|
if (this.disableTimerID)
|
|
return;
|
|
|
|
let account = selectedItem.account;
|
|
let isCommandDisabled =
|
|
(this.isOffline ||
|
|
(account.disconnected &&
|
|
account.connectionErrorReason == Ci.imIAccount.ERROR_UNKNOWN_PRPL));
|
|
let disabledItems = {
|
|
connect: isCommandDisabled,
|
|
disconnect: isCommandDisabled,
|
|
moveup: accountList.selectedIndex == 0,
|
|
movedown: accountList.selectedIndex == accountList.itemCount - 1
|
|
};
|
|
for each (let [name, state] in Iterator(disabledItems)) {
|
|
let elt = document.getElementById("cmd_" + name);
|
|
if (state)
|
|
elt.setAttribute("disabled", "true");
|
|
else
|
|
elt.removeAttribute("disabled");
|
|
}
|
|
},
|
|
onContextMenuShowing: function am_onContextMenuShowing() {
|
|
let targetElt = document.popupNode;
|
|
let isAccount = targetElt instanceof Ci.nsIDOMXULSelectControlItemElement;
|
|
document.getElementById("contextAccountsItems").hidden = !isAccount;
|
|
if (isAccount) {
|
|
let account = targetElt.account;
|
|
let hiddenItems = {
|
|
connect: !account.disconnected,
|
|
disconnect: account.disconnected || account.disconnecting,
|
|
cancelReconnection: !targetElt.hasAttribute("reconnectPending"),
|
|
accountsItemsSeparator: account.disconnecting
|
|
};
|
|
for (let name in hiddenItems)
|
|
document.getElementById("context_" + name).hidden = hiddenItems[name];
|
|
}
|
|
},
|
|
|
|
selectAccount: function am_selectAccount(aAccountId) {
|
|
this.accountList.selectedItem = document.getElementById(aAccountId);
|
|
},
|
|
onAccountSelect: function am_onAccountSelect() {
|
|
clearTimeout(this.disableTimerID);
|
|
this.disableTimerID = 0;
|
|
this.disableCommandItems();
|
|
let selectedItem = this.accountList.selectedItem;
|
|
selectedItem.clientHeight; // force synchronous binding attachment
|
|
selectedItem.buttons.setFocus();
|
|
// Without the following, the account manager is scrolled to the first
|
|
// (dis)connect button when being opened.
|
|
this.accountList.ensureSelectedElementIsVisible();
|
|
},
|
|
|
|
onKeyPress: function am_onKeyPress(event) {
|
|
if (!this.selectedItem)
|
|
return;
|
|
|
|
if (event.shiftKey &&
|
|
(event.keyCode == event.DOM_VK_DOWN || event.keyCode == event.DOM_VK_UP)) {
|
|
let offset = event.keyCode == event.DOM_VK_DOWN ? 1 : -1;
|
|
gAccountManager.moveCurrentItem(offset);
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
|
|
// As we stop propagation, the default action applies to the richlistbox
|
|
// so that the selected account is changed with this default action
|
|
if (event.keyCode == event.DOM_VK_DOWN) {
|
|
if (this.selectedIndex < this.itemCount - 1)
|
|
this.ensureIndexIsVisible(this.selectedIndex + 1);
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
if (event.keyCode == event.DOM_VK_UP) {
|
|
if (this.selectedIndex > 0)
|
|
this.ensureIndexIsVisible(this.selectedIndex - 1);
|
|
event.stopPropagation();
|
|
return;
|
|
}
|
|
|
|
if (event.keyCode == event.DOM_VK_RETURN) {
|
|
let target = event.originalTarget;
|
|
if (target.localName != "checkbox" &&
|
|
(target.localName != "button" ||
|
|
/^(dis)?connect$/.test(target.getAttribute("anonid"))))
|
|
this.selectedItem.buttons.proceedDefaultAction();
|
|
return;
|
|
}
|
|
|
|
if (event.keyCode == event.DOM_VK_DELETE)
|
|
document.getElementById("cmd_delete").doCommand();
|
|
},
|
|
|
|
moveCurrentItem: function am_moveCurrentItem(aOffset) {
|
|
let accountList = this.accountList;
|
|
if (!aOffset || !accountList.selectedItem)
|
|
return;
|
|
|
|
// Create the new preference value from the richlistbox list
|
|
let items = accountList.children;
|
|
let selectedID = accountList.selectedItem.id;
|
|
let array = [];
|
|
for (let i in items)
|
|
if (items[i].id != selectedID)
|
|
array.push(items[i].id);
|
|
|
|
let newIndex = accountList.selectedIndex + aOffset;
|
|
if (newIndex < 0)
|
|
newIndex = 0;
|
|
else if (newIndex >= accountList.itemCount)
|
|
newIndex = accountList.itemCount - 1;
|
|
array.splice(newIndex, 0, selectedID);
|
|
|
|
Services.prefs.setCharPref("messenger.accounts", array.join(","));
|
|
},
|
|
|
|
getAccounts: function am_getAccounts() {
|
|
let accounts = Services.accounts.getAccounts();
|
|
while (accounts.hasMoreElements())
|
|
yield accounts.getNext();
|
|
},
|
|
|
|
openDialog: function am_openDialog(aUrl, aArgs) {
|
|
this.modalDialog = true;
|
|
window.openDialog(aUrl, "", "chrome,modal,titlebar,centerscreen", aArgs);
|
|
this.modalDialog = false;
|
|
},
|
|
setAutoLoginNotification: function am_setAutoLoginNotification() {
|
|
var as = Services.accounts;
|
|
var autoLoginStatus = as.autoLoginStatus;
|
|
let isOffline = false;
|
|
let crashCount = 0;
|
|
for (let acc in this.getAccounts())
|
|
if (acc.autoLogin && acc.firstConnectionState == acc.FIRST_CONNECTION_CRASHED)
|
|
++crashCount;
|
|
|
|
if (autoLoginStatus == as.AUTOLOGIN_ENABLED && crashCount == 0) {
|
|
let status = Services.core.globalUserStatus.statusType;
|
|
this.setOffline(isOffline || status == Ci.imIStatusInfo.STATUS_OFFLINE);
|
|
return;
|
|
}
|
|
|
|
var bundle = document.getElementById("accountsBundle");
|
|
var box = document.getElementById("accountsNotificationBox");
|
|
var priority = box.PRIORITY_INFO_HIGH;
|
|
var connectNowButton = {
|
|
accessKey: bundle.getString("accountsManager.notification.button.accessKey"),
|
|
callback: this.processAutoLogin,
|
|
label: bundle.getString("accountsManager.notification.button.label")
|
|
};
|
|
var label;
|
|
|
|
switch (autoLoginStatus) {
|
|
case as.AUTOLOGIN_USER_DISABLED:
|
|
label = bundle.getString("accountsManager.notification.userDisabled.label");
|
|
break;
|
|
|
|
case as.AUTOLOGIN_SAFE_MODE:
|
|
label = bundle.getString("accountsManager.notification.safeMode.label");
|
|
break;
|
|
|
|
case as.AUTOLOGIN_START_OFFLINE:
|
|
label = bundle.getString("accountsManager.notification.startOffline.label");
|
|
isOffline = true;
|
|
break;
|
|
|
|
case as.AUTOLOGIN_CRASH:
|
|
label = bundle.getString("accountsManager.notification.crash.label");
|
|
priority = box.PRIORITY_WARNING_MEDIUM;
|
|
break;
|
|
|
|
/* One or more accounts made the application crash during their connection.
|
|
If none, this function has already returned */
|
|
case as.AUTOLOGIN_ENABLED:
|
|
if (!("PluralForm" in window))
|
|
Cu.import("resource://gre/modules/PluralForm.jsm");
|
|
label = bundle.getString("accountsManager.notification.singleCrash.label");
|
|
label = PluralForm.get(crashCount, label).replace("#1", crashCount);
|
|
priority = box.PRIORITY_WARNING_MEDIUM;
|
|
connectNowButton.callback = this.processCrashedAccountsLogin;
|
|
break;
|
|
|
|
default:
|
|
label = bundle.getString("accountsManager.notification.other.label");
|
|
}
|
|
let status = Services.core.globalUserStatus.statusType;
|
|
this.setOffline(isOffline || status == Ci.imIStatusInfo.STATUS_OFFLINE);
|
|
|
|
box.appendNotification(label, "autologinStatus", null, priority, [connectNowButton]);
|
|
},
|
|
processAutoLogin: function am_processAutoLogin() {
|
|
var ioService = Services.io;
|
|
if (ioService.offline) {
|
|
ioService.manageOfflineStatus = false;
|
|
ioService.offline = false;
|
|
}
|
|
|
|
Services.accounts.processAutoLogin();
|
|
|
|
gAccountManager.accountList.selectedItem.buttons.setFocus();
|
|
},
|
|
processCrashedAccountsLogin: function am_processCrashedAccountsLogin() {
|
|
for (let acc in gAccountManager.getAccounts())
|
|
if (acc.disconnected && acc.autoLogin &&
|
|
acc.firstConnectionState == acc.FIRST_CONNECTION_CRASHED)
|
|
acc.connect();
|
|
|
|
let notification = document.getElementById("accountsNotificationBox")
|
|
.getNotificationWithValue("autoLoginStatus");
|
|
if (notification)
|
|
notification.close();
|
|
|
|
gAccountManager.accountList.selectedItem.buttons.setFocus();
|
|
},
|
|
setOffline: function am_setOffline(aState) {
|
|
this.isOffline = aState;
|
|
if (aState)
|
|
this.accountList.setAttribute("offline", "true");
|
|
else
|
|
this.accountList.removeAttribute("offline");
|
|
this.disableCommandItems();
|
|
}
|
|
};
|
|
|
|
|
|
let gAMDragAndDrop = {
|
|
ACCOUNT_MIME_TYPE: "application/x-moz-richlistitem",
|
|
// Size of the scroll zone on the top and on the bottom of the account list
|
|
MAGIC_SCROLL_HEIGHT: 20,
|
|
|
|
// A preference already exists to define scroll speed, let's use it.
|
|
get SCROLL_SPEED() {
|
|
delete this.SCROLL_SPEED;
|
|
try {
|
|
this.SCROLL_SPEED =
|
|
Services.prefs.getIntPref("toolkit.scrollbox.scrollIncrement");
|
|
}
|
|
catch (e) {
|
|
this.SCROLL_SPEED = 20;
|
|
}
|
|
return this.SCROLL_SPEED;
|
|
},
|
|
|
|
onDragStart: function amdnd_onDragStart(aEvent, aTransferData, aAction) {
|
|
let accountElement = aEvent.explicitOriginalTarget;
|
|
// This stops the dragging session.
|
|
if (!(accountElement instanceof Ci.nsIDOMXULSelectControlItemElement))
|
|
throw "Element is not draggable!";
|
|
if (gAccountManager.accountList.itemCount == 1)
|
|
throw "Can't drag while there is only one account!";
|
|
|
|
// Transferdata is never used, but we need to transfer something.
|
|
aTransferData.data = new TransferData();
|
|
aTransferData.data.addDataForFlavour(this.ACCOUNT_MIME_TYPE, accountElement);
|
|
},
|
|
|
|
onDragOver: function amdnd_onDragOver(aEvent, aFlavour, aSession) {
|
|
let accountElement = aEvent.explicitOriginalTarget;
|
|
// We are dragging over the account manager, consider it is the same as
|
|
// the last element.
|
|
if (accountElement == gAccountManager.accountList)
|
|
accountElement = gAccountManager.accountList.lastChild;
|
|
|
|
// Auto scroll the account list if we are dragging at the top/bottom
|
|
this.checkForMagicScroll(aEvent.clientY);
|
|
|
|
// The hovered element has changed, change the border too
|
|
if (("_accountElement" in this) && this._accountElement != accountElement)
|
|
this.cleanBorders();
|
|
|
|
if (!aSession.canDrop) {
|
|
aEvent.dataTransfer.dropEffect = "none";
|
|
return;
|
|
}
|
|
aEvent.dataTransfer.dropEffect = "move";
|
|
|
|
if (aEvent.clientY < accountElement.getBoundingClientRect().top +
|
|
accountElement.clientHeight / 2) {
|
|
// we don't want the previous item to show its default bottom-border
|
|
let previousItem = accountElement.previousSibling;
|
|
if (previousItem)
|
|
previousItem.style.borderBottom = "none";
|
|
accountElement.setAttribute("dragover", "up");
|
|
}
|
|
else {
|
|
if (("_accountElement" in this) &&
|
|
this._accountElement == accountElement &&
|
|
accountElement.getAttribute("dragover") == "up")
|
|
this.cleanBorders();
|
|
accountElement.setAttribute("dragover", "down");
|
|
}
|
|
|
|
this._accountElement = accountElement;
|
|
},
|
|
|
|
cleanBorders: function amdnd_cleanBorders(aIsEnd) {
|
|
if (!this._accountElement)
|
|
return;
|
|
|
|
this._accountElement.removeAttribute("dragover");
|
|
// reset the border of the previous element
|
|
let previousItem = this._accountElement.previousSibling;
|
|
if (previousItem) {
|
|
if (aIsEnd && !previousItem.style.borderBottom && previousItem.previousSibling)
|
|
previousItem = previousItem.previousSibling;
|
|
previousItem.style.borderBottom = "";
|
|
}
|
|
|
|
if (aIsEnd)
|
|
delete this._accountElement;
|
|
},
|
|
|
|
canDrop: function amdnd_canDrop(aEvent, aSession) {
|
|
let accountElement = aEvent.explicitOriginalTarget;
|
|
if (accountElement == gAccountManager.accountList)
|
|
accountElement = gAccountManager.accountList.lastChild;
|
|
return (accountElement != gAccountManager.accountList.selectedItem);
|
|
},
|
|
|
|
checkForMagicScroll: function amdnd_checkForMagicScroll(aClientY) {
|
|
let accountList = gAccountManager.accountList;
|
|
let listSize = accountList.getBoundingClientRect();
|
|
let direction = 1;
|
|
if (aClientY < listSize.top + this.MAGIC_SCROLL_HEIGHT)
|
|
direction = -1;
|
|
else if (aClientY < listSize.bottom - this.MAGIC_SCROLL_HEIGHT)
|
|
// We are not on a scroll zone
|
|
return;
|
|
|
|
accountList._scrollbox.scrollTop += direction * this.SCROLL_SPEED;
|
|
},
|
|
|
|
onDrop: function amdnd_onDrop(aEvent, aTransferData, aSession) {
|
|
let accountElement = aEvent.explicitOriginalTarget;
|
|
if (accountElement == gAccountManager.accountList)
|
|
accountElement = gAccountManager.accountList.lastChild;
|
|
|
|
if (!aSession.canDrop)
|
|
return;
|
|
|
|
// compute the destination
|
|
let accountList = gAccountManager.accountList;
|
|
let offset = accountList.getIndexOfItem(accountElement) -
|
|
accountList.selectedIndex;
|
|
let isDroppingAbove =
|
|
aEvent.clientY < accountElement.getBoundingClientRect().top +
|
|
accountElement.clientHeight / 2;
|
|
if (offset > 0)
|
|
offset -= isDroppingAbove;
|
|
else
|
|
offset += !isDroppingAbove;
|
|
gAccountManager.moveCurrentItem(offset);
|
|
},
|
|
|
|
getSupportedFlavours: function amdnd_getSupportedFlavours() {
|
|
var flavours = new FlavourSet();
|
|
flavours.appendFlavour(this.ACCOUNT_MIME_TYPE,
|
|
"nsIDOMXULSelectControlItemElement");
|
|
return flavours;
|
|
}
|
|
};
|