mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-27 10:57:34 +09:00
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
This commit is contained in:
parent
23e0d82436
commit
e400f4130a
1564 changed files with 510348 additions and 0 deletions
72
mailnews/addrbook/content/abAddressBookNameDialog.js
Normal file
72
mailnews/addrbook/content/abAddressBookNameDialog.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
|
||||
var gOkButton;
|
||||
var gNameInput;
|
||||
var gDirectory = null;
|
||||
|
||||
var kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
|
||||
var kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
|
||||
var kAllDirectoryRoot = "moz-abdirectory://";
|
||||
var kPABDirectory = 2; // defined in nsDirPrefs.h
|
||||
|
||||
function abNameOnLoad()
|
||||
{
|
||||
// Get the document elements.
|
||||
gOkButton = document.documentElement.getButton('accept');
|
||||
gNameInput = document.getElementById('name');
|
||||
|
||||
// look in arguments[0] for parameters to see if we have a directory or not
|
||||
if ("arguments" in window && window.arguments[0] &&
|
||||
"selectedDirectory" in window.arguments[0]) {
|
||||
gDirectory = window.arguments[0].selectedDirectory;
|
||||
gNameInput.value = gDirectory.dirName;
|
||||
}
|
||||
|
||||
// Work out the window title (if we have a directory specified, then it's a
|
||||
// rename).
|
||||
var bundle = document.getElementById("bundle_addressBook");
|
||||
|
||||
if (gDirectory) {
|
||||
let oldListName = gDirectory.dirName;
|
||||
document.title = bundle.getFormattedString("addressBookTitleEdit", [oldListName]);
|
||||
} else {
|
||||
document.title = bundle.getString("addressBookTitleNew");
|
||||
}
|
||||
|
||||
if (gDirectory &&
|
||||
(gDirectory.URI == kCollectedAddressbookURI ||
|
||||
gDirectory.URI == kPersonalAddressbookURI ||
|
||||
gDirectory.URI == kAllDirectoryRoot + "?")) {
|
||||
// Address book name is not editable, therefore disable the field and
|
||||
// only have an ok button that doesn't do anything.
|
||||
gNameInput.readOnly = true;
|
||||
document.documentElement.buttons = "accept";
|
||||
document.documentElement.removeAttribute("ondialogaccept");
|
||||
} else {
|
||||
gNameInput.focus();
|
||||
abNameDoOkEnabling();
|
||||
}
|
||||
}
|
||||
|
||||
function abNameOKButton()
|
||||
{
|
||||
var newName = gNameInput.value.trim();
|
||||
|
||||
// Either create a new directory or update an existing one depending on what
|
||||
// we were given when we started.
|
||||
if (gDirectory)
|
||||
gDirectory.dirName = newName;
|
||||
else
|
||||
MailServices.ab.newAddressBook(newName, "", kPABDirectory);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function abNameDoOkEnabling()
|
||||
{
|
||||
gOkButton.disabled = gNameInput.value.trim() == "";
|
||||
}
|
||||
26
mailnews/addrbook/content/abAddressBookNameDialog.xul
Normal file
26
mailnews/addrbook/content/abAddressBookNameDialog.xul
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/dialogs.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abAddressBookNameDialog.dtd">
|
||||
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
style="width: 36em;"
|
||||
onload="abNameOnLoad();"
|
||||
ondialogaccept="return abNameOKButton();">
|
||||
|
||||
<stringbundleset id="stringbundleset">
|
||||
<stringbundle id="bundle_addressBook"
|
||||
src="chrome://messenger/locale/addressbook/addressBook.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
<script type="application/javascript" src="chrome://messenger/content/addressbook/abAddressBookNameDialog.js"/>
|
||||
|
||||
<hbox align="center">
|
||||
<label control="name" value="&name.label;" accesskey="&name.accesskey;"/>
|
||||
<textbox id="name" oninput="abNameDoOkEnabling();" flex="1"/>
|
||||
</hbox>
|
||||
</dialog>
|
||||
424
mailnews/addrbook/content/abDragDrop.js
Normal file
424
mailnews/addrbook/content/abDragDrop.js
Normal file
|
|
@ -0,0 +1,424 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
||||
|
||||
// Returns the load context for the current window
|
||||
function getLoadContext() {
|
||||
return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebNavigation)
|
||||
.QueryInterface(Components.interfaces.nsILoadContext);
|
||||
}
|
||||
|
||||
var abFlavorDataProvider = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIFlavorDataProvider]),
|
||||
|
||||
getFlavorData: function(aTransferable, aFlavor, aData, aDataLen)
|
||||
{
|
||||
if (aFlavor == "application/x-moz-file-promise")
|
||||
{
|
||||
var primitive = {};
|
||||
aTransferable.getTransferData("text/vcard", primitive, {});
|
||||
var vCard = primitive.value.QueryInterface(Components.interfaces.nsISupportsString).data;
|
||||
aTransferable.getTransferData("application/x-moz-file-promise-dest-filename", primitive, {});
|
||||
var leafName = primitive.value.QueryInterface(Components.interfaces.nsISupportsString).data;
|
||||
aTransferable.getTransferData("application/x-moz-file-promise-dir", primitive, {});
|
||||
var localFile = primitive.value.QueryInterface(Components.interfaces.nsIFile).clone();
|
||||
localFile.append(leafName);
|
||||
|
||||
var ofStream = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
ofStream.init(localFile, -1, -1, 0);
|
||||
var converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].createInstance(Components.interfaces.nsIConverterOutputStream);
|
||||
converter.init(ofStream, null, 0, 0);
|
||||
converter.writeString(vCard);
|
||||
converter.close();
|
||||
|
||||
aData.value = localFile;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var abResultsPaneObserver = {
|
||||
onDragStart: function (aEvent, aXferData, aDragAction)
|
||||
{
|
||||
var selectedRows = GetSelectedRows();
|
||||
|
||||
if (!selectedRows)
|
||||
return;
|
||||
|
||||
var selectedAddresses = GetSelectedAddresses();
|
||||
|
||||
aXferData.data = new TransferData();
|
||||
aXferData.data.addDataForFlavour("moz/abcard", selectedRows);
|
||||
aXferData.data.addDataForFlavour("text/x-moz-address", selectedAddresses);
|
||||
aXferData.data.addDataForFlavour("text/unicode", selectedAddresses);
|
||||
|
||||
let srcDirectory = getSelectedDirectory();
|
||||
// The default allowable actions are copy, move and link, so we need
|
||||
// to restrict them here.
|
||||
if (!srcDirectory.readOnly)
|
||||
// Only allow copy & move from read-write directories.
|
||||
aDragAction.action = Components.interfaces.
|
||||
nsIDragService.DRAGDROP_ACTION_COPY |
|
||||
Components.interfaces.
|
||||
nsIDragService.DRAGDROP_ACTION_MOVE;
|
||||
else
|
||||
// Only allow copy from read-only directories.
|
||||
aDragAction.action = Components.interfaces.
|
||||
nsIDragService.DRAGDROP_ACTION_COPY;
|
||||
|
||||
var card = GetSelectedCard();
|
||||
if (card && card.displayName) {
|
||||
let vCard = card.translateTo("vcard");
|
||||
aXferData.data.addDataForFlavour("text/vcard", decodeURIComponent(vCard));
|
||||
aXferData.data.addDataForFlavour("application/x-moz-file-promise-dest-filename", card.displayName + ".vcf");
|
||||
aXferData.data.addDataForFlavour("application/x-moz-file-promise-url", "data:text/vcard," + vCard);
|
||||
aXferData.data.addDataForFlavour("application/x-moz-file-promise", abFlavorDataProvider);
|
||||
}
|
||||
},
|
||||
|
||||
onDrop: function (aEvent, aXferData, aDragSession)
|
||||
{
|
||||
},
|
||||
|
||||
onDragExit: function (aEvent, aDragSession)
|
||||
{
|
||||
},
|
||||
|
||||
onDragOver: function (aEvent, aFlavour, aDragSession)
|
||||
{
|
||||
},
|
||||
|
||||
getSupportedFlavours: function ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var dragService = Components.classes["@mozilla.org/widget/dragservice;1"]
|
||||
.getService(Components.interfaces.nsIDragService);
|
||||
|
||||
var abDirTreeObserver = {
|
||||
/**
|
||||
* canDrop - determine if the tree will accept the dropping of a item
|
||||
* onto it.
|
||||
*
|
||||
* Note 1: We don't allow duplicate mailing list names, therefore copy
|
||||
* is not allowed for mailing lists.
|
||||
* Note 2: Mailing lists currently really need a card in the parent
|
||||
* address book, therefore only moving to an address book is allowed.
|
||||
*
|
||||
* The possibilities:
|
||||
*
|
||||
* anything -> same place = Not allowed
|
||||
* anything -> read only directory = Not allowed
|
||||
* mailing list -> mailing list = Not allowed
|
||||
* (we currently do not support recursive lists)
|
||||
* address book card -> different address book = MOVE or COPY
|
||||
* address book card -> mailing list = COPY only
|
||||
* (cards currently have to exist outside list for list to work correctly)
|
||||
* mailing list -> different address book = MOVE only
|
||||
* (lists currently need to have unique names)
|
||||
* card in mailing list -> parent mailing list = Not allowed
|
||||
* card in mailing list -> other mailing list = MOVE or COPY
|
||||
* card in mailing list -> other address book = MOVE or COPY
|
||||
* read only directory item -> anywhere = COPY only
|
||||
*/
|
||||
canDrop: function(index, orientation, dataTransfer)
|
||||
{
|
||||
if (orientation != Components.interfaces.nsITreeView.DROP_ON)
|
||||
return false;
|
||||
if (!dataTransfer.types.includes("moz/abcard")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
|
||||
|
||||
let srcURI = getSelectedDirectoryURI();
|
||||
|
||||
// We cannot allow copy/move to "All Address Books".
|
||||
if (targetURI == kAllDirectoryRoot + "?")
|
||||
return false;
|
||||
|
||||
// The same place case
|
||||
if (targetURI == srcURI)
|
||||
return false;
|
||||
|
||||
// determine if we dragging from a mailing list on a directory x to the parent (directory x).
|
||||
// if so, don't allow the drop
|
||||
if (srcURI.startsWith(targetURI))
|
||||
return false;
|
||||
|
||||
// check if we can write to the target directory
|
||||
// e.g. LDAP is readonly currently
|
||||
var targetDirectory = GetDirectoryFromURI(targetURI);
|
||||
|
||||
if (targetDirectory.readOnly)
|
||||
return false;
|
||||
|
||||
var dragSession = dragService.getCurrentSession();
|
||||
if (!dragSession)
|
||||
return false;
|
||||
|
||||
// XXX Due to bug 373125/bug 349044 we can't specify a default action,
|
||||
// so we default to move and this means that the user would have to press
|
||||
// ctrl to copy which most users don't realise.
|
||||
//
|
||||
// If target directory is a mailing list, then only allow copies.
|
||||
// if (targetDirectory.isMailList &&
|
||||
// dragSession.dragAction != Components.interfaces.
|
||||
// nsIDragService.DRAGDROP_ACTION_COPY)
|
||||
//return false;
|
||||
|
||||
var srcDirectory = GetDirectoryFromURI(srcURI);
|
||||
|
||||
// Only allow copy from read-only directories.
|
||||
if (srcDirectory.readOnly &&
|
||||
dragSession.dragAction != Components.interfaces.
|
||||
nsIDragService.DRAGDROP_ACTION_COPY)
|
||||
return false;
|
||||
|
||||
// Go through the cards checking to see if one of them is a mailing list
|
||||
// (if we are attempting a copy) - we can't copy mailing lists as
|
||||
// that would give us duplicate names which isn't allowed at the
|
||||
// moment.
|
||||
var draggingMailList = false;
|
||||
|
||||
// The data contains the a string of "selected rows", eg.: "1,2".
|
||||
var rows = dataTransfer.getData("moz/abcard").split(",").map(j => parseInt(j, 10));
|
||||
|
||||
for (var j = 0; j < rows.length; j++)
|
||||
{
|
||||
if (gAbView.getCardFromRow(rows[j]).isMailList)
|
||||
{
|
||||
draggingMailList = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The rest of the cases - allow cards for copy or move, but only allow
|
||||
// move of mailing lists if we're not going into another mailing list.
|
||||
if (draggingMailList &&
|
||||
(targetDirectory.isMailList ||
|
||||
dragSession.dragAction == Components.interfaces.
|
||||
nsIDragService.DRAGDROP_ACTION_COPY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dragSession.canDrop = true;
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* onDrop - we don't need to check again for correctness as the
|
||||
* tree view calls canDrop just before calling onDrop.
|
||||
*
|
||||
*/
|
||||
onDrop: function(index, orientation, dataTransfer)
|
||||
{
|
||||
var dragSession = dragService.getCurrentSession();
|
||||
if (!dragSession)
|
||||
return;
|
||||
if (!dataTransfer.types.includes("moz/abcard")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetURI = gDirectoryTreeView.getDirectoryAtIndex(index).URI;
|
||||
let srcURI = getSelectedDirectoryURI();
|
||||
|
||||
// The data contains the a string of "selected rows", eg.: "1,2".
|
||||
var rows = dataTransfer.getData("moz/abcard").split(",").map(j => parseInt(j, 10));
|
||||
var numrows = rows.length;
|
||||
|
||||
var result;
|
||||
// needToCopyCard is used for whether or not we should be creating
|
||||
// copies of the cards in a mailing list in a different address book
|
||||
// - it's not for if we are moving or not.
|
||||
var needToCopyCard = true;
|
||||
if (srcURI.length > targetURI.length) {
|
||||
result = srcURI.split(targetURI);
|
||||
if (result[0] != srcURI) {
|
||||
// src directory is a mailing list on target directory, no need to copy card
|
||||
needToCopyCard = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = targetURI.split(srcURI);
|
||||
if (result[0] != targetURI) {
|
||||
// target directory is a mailing list on src directory, no need to copy card
|
||||
needToCopyCard = false;
|
||||
}
|
||||
}
|
||||
|
||||
// if we still think we have to copy the card,
|
||||
// check if srcURI and targetURI are mailing lists on same directory
|
||||
// if so, we don't have to copy the card
|
||||
if (needToCopyCard) {
|
||||
var targetParentURI = GetParentDirectoryFromMailingListURI(targetURI);
|
||||
if (targetParentURI && (targetParentURI == GetParentDirectoryFromMailingListURI(srcURI)))
|
||||
needToCopyCard = false;
|
||||
}
|
||||
|
||||
var directory = GetDirectoryFromURI(targetURI);
|
||||
|
||||
// Only move if we are not transferring to a mail list
|
||||
var actionIsMoving = (dragSession.dragAction & dragSession.DRAGDROP_ACTION_MOVE) && !directory.isMailList;
|
||||
|
||||
let cardsToCopy = [];
|
||||
for (let j = 0; j < numrows; j++) {
|
||||
cardsToCopy.push(gAbView.getCardFromRow(rows[j]));
|
||||
}
|
||||
for (let card of cardsToCopy) {
|
||||
if (card.isMailList) {
|
||||
// This check ensures we haven't slipped through by mistake
|
||||
if (needToCopyCard && actionIsMoving) {
|
||||
directory.addMailList(GetDirectoryFromURI(card.mailListURI));
|
||||
}
|
||||
} else {
|
||||
let srcDirectory = null;
|
||||
if (srcURI == (kAllDirectoryRoot + "?") && actionIsMoving) {
|
||||
let dirId = card.directoryId.substring(0, card.directoryId.indexOf("&"));
|
||||
srcDirectory = MailServices.ab.getDirectoryFromId(dirId);
|
||||
}
|
||||
|
||||
directory.dropCard(card, needToCopyCard);
|
||||
|
||||
// This is true only if srcURI is "All ABs" and action is moving.
|
||||
if (srcDirectory) {
|
||||
let cardArray =
|
||||
Components.classes["@mozilla.org/array;1"]
|
||||
.createInstance(Components.interfaces.nsIMutableArray);
|
||||
cardArray.appendElement(card, false);
|
||||
srcDirectory.deleteCards(cardArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cardsTransferredText;
|
||||
|
||||
// If we are moving, but not moving to a directory, then delete the
|
||||
// selected cards and display the appropriate text
|
||||
if (actionIsMoving && srcURI != (kAllDirectoryRoot + "?")) {
|
||||
// If we have moved the cards, then delete them as well.
|
||||
gAbView.deleteSelectedCards();
|
||||
}
|
||||
|
||||
if (actionIsMoving) {
|
||||
cardsTransferredText = PluralForm.get(numrows,
|
||||
gAddressBookBundle.getFormattedString("contactsMoved", [numrows]));
|
||||
} else {
|
||||
cardsTransferredText = PluralForm.get(numrows,
|
||||
gAddressBookBundle.getFormattedString("contactsCopied", [numrows]));
|
||||
}
|
||||
|
||||
if (srcURI == kAllDirectoryRoot + "?") {
|
||||
SetAbView(srcURI);
|
||||
}
|
||||
|
||||
document.getElementById("statusText").label = cardsTransferredText;
|
||||
},
|
||||
|
||||
onToggleOpenState: function()
|
||||
{
|
||||
},
|
||||
|
||||
onCycleHeader: function(colID, elt)
|
||||
{
|
||||
},
|
||||
|
||||
onCycleCell: function(row, colID)
|
||||
{
|
||||
},
|
||||
|
||||
onSelectionChanged: function()
|
||||
{
|
||||
},
|
||||
|
||||
onPerformAction: function(action)
|
||||
{
|
||||
},
|
||||
|
||||
onPerformActionOnRow: function(action, row)
|
||||
{
|
||||
},
|
||||
|
||||
onPerformActionOnCell: function(action, row, colID)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
function DragAddressOverTargetControl(event)
|
||||
{
|
||||
var dragSession = gDragService.getCurrentSession();
|
||||
|
||||
if (!dragSession.isDataFlavorSupported("text/x-moz-address"))
|
||||
return;
|
||||
|
||||
var trans = Components.classes["@mozilla.org/widget/transferable;1"]
|
||||
.createInstance(Components.interfaces.nsITransferable);
|
||||
trans.init(getLoadContext());
|
||||
trans.addDataFlavor("text/x-moz-address");
|
||||
|
||||
var canDrop = true;
|
||||
|
||||
for ( var i = 0; i < dragSession.numDropItems; ++i )
|
||||
{
|
||||
dragSession.getData ( trans, i );
|
||||
var dataObj = new Object();
|
||||
var bestFlavor = new Object();
|
||||
var len = new Object();
|
||||
try
|
||||
{
|
||||
trans.getAnyTransferData ( bestFlavor, dataObj, len );
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
canDrop = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dragSession.canDrop = canDrop;
|
||||
}
|
||||
|
||||
function DropAddressOverTargetControl(event)
|
||||
{
|
||||
var dragSession = gDragService.getCurrentSession();
|
||||
|
||||
var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
|
||||
trans.addDataFlavor("text/x-moz-address");
|
||||
|
||||
for ( var i = 0; i < dragSession.numDropItems; ++i )
|
||||
{
|
||||
dragSession.getData ( trans, i );
|
||||
var dataObj = new Object();
|
||||
var bestFlavor = new Object();
|
||||
var len = new Object();
|
||||
|
||||
// Ensure we catch any empty data that may have slipped through
|
||||
try
|
||||
{
|
||||
trans.getAnyTransferData ( bestFlavor, dataObj, len);
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( dataObj )
|
||||
dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
|
||||
if ( !dataObj )
|
||||
continue;
|
||||
|
||||
// pull the address out of the data object
|
||||
var address = dataObj.data.substring(0, len.value);
|
||||
if (!address)
|
||||
continue;
|
||||
|
||||
DropRecipient(address);
|
||||
}
|
||||
}
|
||||
17
mailnews/addrbook/content/abEditCardDialog.xul
Normal file
17
mailnews/addrbook/content/abEditCardDialog.xul
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
|
||||
|
||||
<?xul-overlay href="chrome://messenger/content/addressbook/abCardOverlay.xul"?>
|
||||
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="abcardWindow"
|
||||
onload="OnLoadEditCard()"
|
||||
ondialogaccept="return EditCardOKButton();">
|
||||
|
||||
<stringbundleset id="stringbundleset"/>
|
||||
<vbox id="editcard"/>
|
||||
</dialog>
|
||||
613
mailnews/addrbook/content/abMailListDialog.js
Normal file
613
mailnews/addrbook/content/abMailListDialog.js
Normal file
|
|
@ -0,0 +1,613 @@
|
|||
/* 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/. */
|
||||
|
||||
top.MAX_RECIPIENTS = 1;
|
||||
var inputElementType = "";
|
||||
|
||||
var gListCard;
|
||||
var gEditList;
|
||||
var oldListName = "";
|
||||
var gLoadListeners = [];
|
||||
var gSaveListeners = [];
|
||||
|
||||
try
|
||||
{
|
||||
var gDragService = Components.classes["@mozilla.org/widget/dragservice;1"]
|
||||
.getService(Components.interfaces.nsIDragService);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
}
|
||||
|
||||
// Returns the load context for the current window
|
||||
function getLoadContext() {
|
||||
return window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebNavigation)
|
||||
.QueryInterface(Components.interfaces.nsILoadContext);
|
||||
}
|
||||
|
||||
function awHandleKeyPress(element, event)
|
||||
{
|
||||
// allow dialog to close on enter if focused textbox has no value
|
||||
if (element.value != "" && event.keyCode == KeyEvent.DOM_VK_RETURN) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function mailingListExists(listname)
|
||||
{
|
||||
if (MailServices.ab.mailListNameExists(listname))
|
||||
{
|
||||
Services.prompt.alert(window,
|
||||
gAddressBookBundle.getString("mailListNameExistsTitle"),
|
||||
gAddressBookBundle.getString("mailListNameExistsMessage"));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function GetListValue(mailList, doAdd)
|
||||
{
|
||||
var listname = document.getElementById("ListName").value.trim();
|
||||
|
||||
if (listname.length == 0)
|
||||
{
|
||||
var alertText = gAddressBookBundle.getString("emptyListName");
|
||||
alert(alertText);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
var canonicalNewListName = listname.toLowerCase();
|
||||
var canonicalOldListName = oldListName.toLowerCase();
|
||||
if (doAdd)
|
||||
{
|
||||
if (mailingListExists(canonicalNewListName))
|
||||
return false;
|
||||
}
|
||||
else if (canonicalOldListName != canonicalNewListName)
|
||||
{
|
||||
if (mailingListExists(canonicalNewListName))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mailList.isMailList = true;
|
||||
mailList.dirName = listname;
|
||||
mailList.listNickName = document.getElementById('ListNickName').value;
|
||||
mailList.description = document.getElementById('ListDescription').value;
|
||||
|
||||
var oldTotal = mailList.addressLists.length;
|
||||
var i = 1;
|
||||
var pos = 0;
|
||||
var inputField, fieldValue, cardproperty;
|
||||
while ((inputField = awGetInputElement(i)))
|
||||
{
|
||||
|
||||
fieldValue = inputField.value;
|
||||
|
||||
if (doAdd || (!doAdd && pos >= oldTotal))
|
||||
cardproperty = Components.classes["@mozilla.org/addressbook/cardproperty;1"].createInstance();
|
||||
else
|
||||
cardproperty = mailList.addressLists.queryElementAt(pos, Components.interfaces.nsIAbCard);
|
||||
|
||||
if (fieldValue == "")
|
||||
{
|
||||
if (!doAdd && cardproperty)
|
||||
try
|
||||
{
|
||||
mailList.addressLists.removeElementAt(pos);
|
||||
--oldTotal;
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
// Ignore attempting to remove an item
|
||||
// at a position greater than the number
|
||||
// of elements in the addressLists attribute
|
||||
}
|
||||
}
|
||||
else if (cardproperty)
|
||||
{
|
||||
cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
|
||||
if (cardproperty)
|
||||
{
|
||||
let addrObjects = MailServices.headerParser
|
||||
.makeFromDisplayAddress(fieldValue, {});
|
||||
for (let j = 0; j < addrObjects.length; j++)
|
||||
{
|
||||
if (j > 0)
|
||||
{
|
||||
cardproperty = Components.classes["@mozilla.org/addressbook/cardproperty;1"].createInstance();
|
||||
cardproperty = cardproperty.QueryInterface(Components.interfaces.nsIAbCard);
|
||||
}
|
||||
cardproperty.primaryEmail = addrObjects[j].email;
|
||||
cardproperty.displayName = addrObjects[j].name || addrObjects[j].email;
|
||||
|
||||
if (doAdd || (doAdd == false && pos >= oldTotal))
|
||||
mailList.addressLists.appendElement(cardproperty, false);
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
--i;
|
||||
|
||||
if (doAdd == false && i < oldTotal)
|
||||
{
|
||||
for (var j = i; j < oldTotal; j++)
|
||||
mailList.addressLists.removeElementAt(j);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function MailListOKButton()
|
||||
{
|
||||
var popup = document.getElementById('abPopup');
|
||||
if (popup)
|
||||
{
|
||||
var uri = popup.getAttribute('value');
|
||||
|
||||
// FIX ME - hack to avoid crashing if no ab selected because of blank option bug from template
|
||||
// should be able to just remove this if we are not seeing blank lines in the ab popup
|
||||
if (!uri)
|
||||
return false; // don't close window
|
||||
// -----
|
||||
|
||||
//Add mailing list to database
|
||||
var mailList = Components.classes["@mozilla.org/addressbook/directoryproperty;1"].createInstance();
|
||||
mailList = mailList.QueryInterface(Components.interfaces.nsIAbDirectory);
|
||||
|
||||
if (GetListValue(mailList, true))
|
||||
{
|
||||
var parentDirectory = GetDirectoryFromURI(uri);
|
||||
mailList = parentDirectory.addMailList(mailList);
|
||||
NotifySaveListeners(mailList);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return true; // close the window
|
||||
}
|
||||
|
||||
function OnLoadNewMailList()
|
||||
{
|
||||
var selectedAB = null;
|
||||
|
||||
InitCommonJS();
|
||||
|
||||
if ("arguments" in window && window.arguments[0])
|
||||
{
|
||||
var abURI = window.arguments[0].selectedAB;
|
||||
if (abURI && abURI != kAllDirectoryRoot + "?") {
|
||||
var directory = GetDirectoryFromURI(abURI);
|
||||
if (directory.isMailList) {
|
||||
var parentURI = GetParentDirectoryFromMailingListURI(abURI);
|
||||
if (parentURI) {
|
||||
selectedAB = parentURI;
|
||||
}
|
||||
}
|
||||
else if (directory.readOnly) {
|
||||
selectedAB = kPersonalAddressbookURI;
|
||||
}
|
||||
else {
|
||||
selectedAB = abURI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedAB)
|
||||
selectedAB = kPersonalAddressbookURI;
|
||||
|
||||
// set popup with address book names
|
||||
var abPopup = document.getElementById('abPopup');
|
||||
abPopup.value = selectedAB;
|
||||
|
||||
AppendNewRowAndSetFocus();
|
||||
awFitDummyRows(1);
|
||||
|
||||
document.addEventListener("keypress", awDocumentKeyPress, true);
|
||||
|
||||
// focus on first name
|
||||
var listName = document.getElementById('ListName');
|
||||
if (listName)
|
||||
setTimeout( function(firstTextBox) { firstTextBox.focus(); }, 0, listName );
|
||||
|
||||
NotifyLoadListeners(directory);
|
||||
}
|
||||
|
||||
function EditListOKButton()
|
||||
{
|
||||
//edit mailing list in database
|
||||
if (GetListValue(gEditList, false))
|
||||
{
|
||||
if (gListCard) {
|
||||
// modify the list card (for the results pane) from the mailing list
|
||||
gListCard.displayName = gEditList.dirName;
|
||||
gListCard.lastName = gEditList.dirName;
|
||||
gListCard.setProperty("NickName", gEditList.listNickName);
|
||||
gListCard.setProperty("Notes", gEditList.description);
|
||||
}
|
||||
|
||||
NotifySaveListeners(gEditList);
|
||||
gEditList.editMailListToDatabase(gListCard);
|
||||
|
||||
window.arguments[0].refresh = true;
|
||||
return true; // close the window
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function OnLoadEditList()
|
||||
{
|
||||
InitCommonJS();
|
||||
|
||||
gListCard = window.arguments[0].abCard;
|
||||
var listUri = window.arguments[0].listURI;
|
||||
|
||||
gEditList = GetDirectoryFromURI(listUri);
|
||||
|
||||
document.getElementById('ListName').value = gEditList.dirName;
|
||||
document.getElementById('ListNickName').value = gEditList.listNickName;
|
||||
document.getElementById('ListDescription').value = gEditList.description;
|
||||
oldListName = gEditList.dirName;
|
||||
|
||||
document.title = gAddressBookBundle.getFormattedString("mailingListTitleEdit", [oldListName]);
|
||||
|
||||
if (gEditList.addressLists)
|
||||
{
|
||||
let total = gEditList.addressLists.length;
|
||||
if (total)
|
||||
{
|
||||
let listbox = document.getElementById('addressingWidget');
|
||||
let newListBoxNode = listbox.cloneNode(false);
|
||||
let templateNode = listbox.querySelector("listitem");
|
||||
|
||||
top.MAX_RECIPIENTS = 0;
|
||||
for (let i = 0; i < total; i++)
|
||||
{
|
||||
let card = gEditList.addressLists.queryElementAt(i, Components.interfaces.nsIAbCard);
|
||||
let address = MailServices.headerParser.makeMailboxObject(
|
||||
card.displayName, card.primaryEmail).toString();
|
||||
SetInputValue(address, newListBoxNode, templateNode);
|
||||
}
|
||||
listbox.parentNode.replaceChild(newListBoxNode, listbox);
|
||||
}
|
||||
}
|
||||
|
||||
// Is this directory read-only? If so, we now need to set all the fields to
|
||||
// read-only.
|
||||
if (gEditList.readOnly) {
|
||||
const kMailListFields = [ 'ListName', 'ListNickName', 'ListDescription' ];
|
||||
|
||||
for (let i = 0; i < kMailListFields.length; ++i)
|
||||
document.getElementById(kMailListFields[i]).readOnly = true;
|
||||
|
||||
document.documentElement.buttons = "accept";
|
||||
document.documentElement.removeAttribute("ondialogaccept");
|
||||
|
||||
// Getting a sane read-only implementation for the addressing widget would
|
||||
// basically need a separate dialog. Given I'm not sure about the future of
|
||||
// the mailing list dialog in its current state, let's just disable it
|
||||
// completely.
|
||||
document.getElementById("addressingWidget").disabled = true;
|
||||
}
|
||||
|
||||
document.addEventListener("keypress", awDocumentKeyPress, true);
|
||||
|
||||
// workaround for bug 118337 - for mailing lists that have more rows than fits inside
|
||||
// the display, the value of the textbox inside the new row isn't inherited into the input -
|
||||
// the first row then appears to be duplicated at the end although it is actually empty.
|
||||
// see awAppendNewRow which copies first row and clears it
|
||||
setTimeout(AppendLastRow, 0);
|
||||
NotifyLoadListeners(gEditList);
|
||||
}
|
||||
|
||||
function AppendLastRow()
|
||||
{
|
||||
AppendNewRowAndSetFocus();
|
||||
awFitDummyRows(1);
|
||||
|
||||
// focus on first name
|
||||
var listName = document.getElementById('ListName');
|
||||
if (listName)
|
||||
listName.focus();
|
||||
}
|
||||
|
||||
function AppendNewRowAndSetFocus()
|
||||
{
|
||||
var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
if (lastInput && lastInput.value)
|
||||
awAppendNewRow(true);
|
||||
else
|
||||
awSetFocus(top.MAX_RECIPIENTS, lastInput);
|
||||
}
|
||||
|
||||
function SetInputValue(inputValue, parentNode, templateNode)
|
||||
{
|
||||
top.MAX_RECIPIENTS++;
|
||||
|
||||
var newNode = templateNode.cloneNode(true);
|
||||
parentNode.appendChild(newNode); // we need to insert the new node before we set the value of the select element!
|
||||
|
||||
var input = newNode.getElementsByTagName(awInputElementName());
|
||||
if (input && input.length == 1)
|
||||
{
|
||||
//We need to set the value using both setAttribute and .value else we will
|
||||
// lose the content when the field is not visible. See bug 37435
|
||||
input[0].setAttribute("value", inputValue);
|
||||
input[0].value = inputValue;
|
||||
input[0].setAttribute("id", "addressCol1#" + top.MAX_RECIPIENTS);
|
||||
}
|
||||
}
|
||||
|
||||
function awNotAnEmptyArea(event)
|
||||
{
|
||||
//This is temporary until i figure out how to ensure to always having an empty space after the last row
|
||||
|
||||
var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
if (lastInput && lastInput.value)
|
||||
awAppendNewRow(false);
|
||||
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
function awClickEmptySpace(target, setFocus)
|
||||
{
|
||||
if (target == null ||
|
||||
(target.localName != "listboxbody" &&
|
||||
target.localName != "listcell" &&
|
||||
target.localName != "listitem"))
|
||||
return;
|
||||
|
||||
var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
|
||||
if (lastInput && lastInput.value)
|
||||
awAppendNewRow(setFocus);
|
||||
else
|
||||
if (setFocus)
|
||||
awSetFocus(top.MAX_RECIPIENTS, lastInput);
|
||||
}
|
||||
|
||||
function awReturnHit(inputElement)
|
||||
{
|
||||
var row = awGetRowByInputElement(inputElement);
|
||||
if (inputElement.value)
|
||||
{
|
||||
var nextInput = awGetInputElement(row+1);
|
||||
if (!nextInput)
|
||||
awAppendNewRow(true);
|
||||
else
|
||||
awSetFocus(row+1, nextInput);
|
||||
}
|
||||
}
|
||||
|
||||
function awDeleteRow(rowToDelete)
|
||||
{
|
||||
/* When we delete a row, we must reset the id of others row in order to not break the sequence */
|
||||
var maxRecipients = top.MAX_RECIPIENTS;
|
||||
awRemoveRow(rowToDelete);
|
||||
|
||||
var numberOfCols = awGetNumberOfCols();
|
||||
for (var row = rowToDelete + 1; row <= maxRecipients; row ++)
|
||||
for (var col = 1; col <= numberOfCols; col++)
|
||||
awGetElementByCol(row, col).setAttribute("id", "addressCol" + (col) + "#" + (row-1));
|
||||
|
||||
awTestRowSequence();
|
||||
}
|
||||
|
||||
function awInputChanged(inputElement)
|
||||
{
|
||||
// AutoCompleteAddress(inputElement);
|
||||
|
||||
//Do we need to add a new row?
|
||||
var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
if (lastInput && lastInput.value && !top.doNotCreateANewRow)
|
||||
awAppendNewRow(false);
|
||||
top.doNotCreateANewRow = false;
|
||||
}
|
||||
|
||||
function awInputElementName()
|
||||
{
|
||||
if (inputElementType == "")
|
||||
inputElementType = document.getElementById("addressCol1#1").localName;
|
||||
return inputElementType;
|
||||
}
|
||||
|
||||
function awAppendNewRow(setFocus)
|
||||
{
|
||||
var body = document.getElementById("addressingWidget");
|
||||
var listitem1 = awGetListItem(1);
|
||||
|
||||
if (body && listitem1)
|
||||
{
|
||||
var nextDummy = awGetNextDummyRow();
|
||||
var newNode = listitem1.cloneNode(true);
|
||||
if (nextDummy)
|
||||
body.replaceChild(newNode, nextDummy);
|
||||
else
|
||||
body.appendChild(newNode);
|
||||
|
||||
top.MAX_RECIPIENTS++;
|
||||
|
||||
var input = newNode.getElementsByTagName(awInputElementName());
|
||||
if (input && input.length == 1)
|
||||
{
|
||||
input[0].setAttribute("value", "");
|
||||
input[0].setAttribute("id", "addressCol1#" + top.MAX_RECIPIENTS);
|
||||
|
||||
if (input[0].getAttribute('focused') != '')
|
||||
input[0].removeAttribute('focused');
|
||||
}
|
||||
// focus on new input widget
|
||||
if (setFocus && input )
|
||||
awSetFocus(top.MAX_RECIPIENTS, input[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// functions for accessing the elements in the addressing widget
|
||||
|
||||
function awGetInputElement(row)
|
||||
{
|
||||
return document.getElementById("addressCol1#" + row);
|
||||
}
|
||||
|
||||
|
||||
function _awSetFocus()
|
||||
{
|
||||
var listbox = document.getElementById('addressingWidget');
|
||||
try
|
||||
{
|
||||
var theNewRow = awGetListItem(top.awRow);
|
||||
|
||||
listbox.ensureElementIsVisible(theNewRow);
|
||||
top.awInputElement.focus();
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
top.awFocusRetry ++;
|
||||
if (top.awFocusRetry < 8)
|
||||
{
|
||||
dump("_awSetFocus failed, try it again...\n");
|
||||
setTimeout(_awSetFocus, 0);
|
||||
}
|
||||
else
|
||||
dump("_awSetFocus failed, forget about it!\n");
|
||||
}
|
||||
}
|
||||
|
||||
function awTabFromRecipient(element, event)
|
||||
{
|
||||
//If we are the last element in the listbox, we don't want to create a new row.
|
||||
if (element == awGetInputElement(top.MAX_RECIPIENTS))
|
||||
top.doNotCreateANewRow = true;
|
||||
}
|
||||
|
||||
function DragOverAddressListTree(event)
|
||||
{
|
||||
var validFlavor = false;
|
||||
var dragSession = gDragService.getCurrentSession();
|
||||
|
||||
// XXX add support for other flavors here
|
||||
if (dragSession.isDataFlavorSupported("text/x-moz-address")) {
|
||||
dragSession.canDrop = true;
|
||||
}
|
||||
}
|
||||
|
||||
function DropOnAddressListTree(event)
|
||||
{
|
||||
let dragSession = gDragService.getCurrentSession();
|
||||
let trans;
|
||||
|
||||
try {
|
||||
trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
|
||||
trans.init(getLoadContext());
|
||||
trans.addDataFlavor("text/x-moz-address");
|
||||
}
|
||||
catch (ex) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < dragSession.numDropItems; ++i)
|
||||
{
|
||||
dragSession.getData(trans, i);
|
||||
let dataObj = new Object();
|
||||
let bestFlavor = new Object();
|
||||
let len = new Object();
|
||||
trans.getAnyTransferData(bestFlavor, dataObj, len);
|
||||
if (dataObj)
|
||||
dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsString);
|
||||
if (!dataObj)
|
||||
continue;
|
||||
|
||||
// pull the URL out of the data object
|
||||
let address = dataObj.data.substring(0, len.value);
|
||||
if (!address)
|
||||
continue;
|
||||
|
||||
DropListAddress(event.target, address);
|
||||
}
|
||||
}
|
||||
|
||||
function DropListAddress(target, address)
|
||||
{
|
||||
// Set focus on a new available, visible row.
|
||||
awClickEmptySpace(target, true);
|
||||
if (top.MAX_RECIPIENTS == 0)
|
||||
top.MAX_RECIPIENTS = 1;
|
||||
|
||||
// Break apart the MIME-ready header address into individual addressees to
|
||||
// add to the dialog.
|
||||
let addresses = {}, names = {}, fullNames = {};
|
||||
MailServices.headerParser.parseHeadersWithArray(address, addresses, names,
|
||||
fullNames);
|
||||
for (let full of fullNames.value)
|
||||
{
|
||||
let lastInput = awGetInputElement(top.MAX_RECIPIENTS);
|
||||
lastInput.value = full;
|
||||
awAppendNewRow(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allows extensions to register a listener function for
|
||||
* when a mailing list is loaded. The listener function
|
||||
* should take two parameters - the first being the
|
||||
* mailing list being loaded, the second one being the
|
||||
* current window document.
|
||||
*/
|
||||
function RegisterLoadListener(aListener)
|
||||
{
|
||||
gLoadListeners.push(aListener);
|
||||
}
|
||||
|
||||
/* Allows extensions to unload a load listener function.
|
||||
*/
|
||||
function UnregisterLoadListener(aListener)
|
||||
{
|
||||
var fIndex = gLoadListeners.indexOf(aListener);
|
||||
if (fIndex != -1)
|
||||
gLoadListeners.splice(fIndex, 1);
|
||||
}
|
||||
|
||||
/* Allows extensions to register a listener function for
|
||||
* when a mailing list is saved. Like a load listener,
|
||||
* the save listener should take two parameters: the first
|
||||
* being a copy of the mailing list that is being saved,
|
||||
* and the second being the current window document.
|
||||
*/
|
||||
function RegisterSaveListener(aListener)
|
||||
{
|
||||
gSaveListeners.push(aListener);
|
||||
}
|
||||
|
||||
/* Allows extensions to unload a save listener function.
|
||||
*/
|
||||
function UnregisterSaveListener(aListener)
|
||||
{
|
||||
var fIndex = gSaveListeners.indexOf(aListener);
|
||||
if (fIndex != -1)
|
||||
gSaveListeners.splice(fIndex, 1);
|
||||
}
|
||||
|
||||
/* Notifies all load listeners.
|
||||
*/
|
||||
function NotifyLoadListeners(aMailingList)
|
||||
{
|
||||
for (let i = 0; i < gLoadListeners.length; i++)
|
||||
gLoadListeners[i](aMailingList, document);
|
||||
}
|
||||
|
||||
/* Notifies all save listeners.
|
||||
*/
|
||||
function NotifySaveListeners(aMailingList)
|
||||
{
|
||||
for (let i = 0; i < gSaveListeners.length; i++)
|
||||
gSaveListeners[i](aMailingList, document);
|
||||
}
|
||||
|
||||
33
mailnews/addrbook/content/abNewCardDialog.xul
Normal file
33
mailnews/addrbook/content/abNewCardDialog.xul
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
|
||||
|
||||
<?xul-overlay href="chrome://messenger/content/addressbook/abCardOverlay.xul"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://messenger/locale/addressbook/abNewCardDialog.dtd">
|
||||
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="abcardWindow"
|
||||
onload="OnLoadNewCard()"
|
||||
ondialogaccept="return NewCardOKButton();">
|
||||
|
||||
<stringbundleset id="stringbundleset"/>
|
||||
|
||||
<hbox align="center">
|
||||
|
||||
<label id="abPopupLabel" control="abPopup" value="&chooseAddressBook.label;" accesskey="&chooseAddressBook.accesskey;"/>
|
||||
|
||||
<menulist id="abPopup">
|
||||
<menupopup id="abPopup-menupopup" class="addrbooksPopup" writable="true"/>
|
||||
</menulist>
|
||||
|
||||
</hbox>
|
||||
|
||||
<spacer style="height:1em"/>
|
||||
|
||||
<vbox id="editcard"/>
|
||||
|
||||
</dialog>
|
||||
502
mailnews/addrbook/content/abResultsPane.js
Normal file
502
mailnews/addrbook/content/abResultsPane.js
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Use of items in this file require:
|
||||
*
|
||||
* getSelectedDirectoryURI()
|
||||
* returns the URI of the selected directory
|
||||
* AbResultsPaneDoubleClick(card)
|
||||
* Is called when the results pane is double-clicked, with the clicked card.
|
||||
* AbEditCard(card)
|
||||
* Is called when a card is to be edited, with the card as the parameter.
|
||||
*
|
||||
* The following function is only required if ResultsPaneController is used:
|
||||
*
|
||||
* goSetMenuValue()
|
||||
* Core function in globalOverlay.js
|
||||
*/
|
||||
|
||||
// List/card selections in the results pane.
|
||||
var kNothingSelected = 0;
|
||||
var kListsAndCards = 1;
|
||||
var kMultipleListsOnly = 2;
|
||||
var kSingleListOnly = 3;
|
||||
var kCardsOnly = 4;
|
||||
|
||||
// Global Variables
|
||||
|
||||
// gAbView holds an object with an nsIAbView interface
|
||||
var gAbView = null;
|
||||
// Holds a reference to the "abResultsTree" document element. Initially
|
||||
// set up by SetAbView.
|
||||
var gAbResultsTree = null;
|
||||
|
||||
function SetAbView(aURI)
|
||||
{
|
||||
// If we don't have a URI, just clear the view and leave everything else
|
||||
// alone.
|
||||
if (!aURI) {
|
||||
gAbView.clearView();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we do have a URI, we want to allow updating the review even if the
|
||||
// URI is the same, as the search results may be different.
|
||||
|
||||
var sortColumn = kDefaultSortColumn;
|
||||
var sortDirection = kDefaultAscending;
|
||||
|
||||
if (!gAbResultsTree) {
|
||||
gAbResultsTree = document.getElementById("abResultsTree");
|
||||
gAbResultsTree.controllers.appendController(ResultsPaneController);
|
||||
}
|
||||
|
||||
if (gAbView) {
|
||||
sortColumn = gAbView.sortColumn;
|
||||
sortDirection = gAbView.sortDirection;
|
||||
}
|
||||
else {
|
||||
if (gAbResultsTree.hasAttribute("sortCol"))
|
||||
sortColumn = gAbResultsTree.getAttribute("sortCol");
|
||||
var sortColumnNode = document.getElementById(sortColumn);
|
||||
if (sortColumnNode && sortColumnNode.hasAttribute("sortDirection"))
|
||||
sortDirection = sortColumnNode.getAttribute("sortDirection");
|
||||
}
|
||||
|
||||
var directory = GetDirectoryFromURI(aURI);
|
||||
|
||||
if (!gAbView)
|
||||
gAbView = Components.classes["@mozilla.org/addressbook/abview;1"]
|
||||
.createInstance(Components.interfaces.nsIAbView);
|
||||
|
||||
var actualSortColumn = gAbView.setView(directory, GetAbViewListener(),
|
||||
sortColumn, sortDirection);
|
||||
|
||||
gAbResultsTree.treeBoxObject.view =
|
||||
gAbView.QueryInterface(Components.interfaces.nsITreeView);
|
||||
|
||||
UpdateSortIndicators(actualSortColumn, sortDirection);
|
||||
|
||||
// If the selected address book is LDAP and the search box is empty,
|
||||
// inform the user of the empty results pane.
|
||||
let abResultsTree = document.getElementById("abResultsTree");
|
||||
let cardViewOuterBox = document.getElementById("CardViewOuterBox");
|
||||
let blankResultsPaneMessageBox = document.getElementById("blankResultsPaneMessageBox");
|
||||
if (aURI.startsWith("moz-abldapdirectory://") && !aURI.includes("?")) {
|
||||
if (abResultsTree)
|
||||
abResultsTree.hidden = true;
|
||||
if (cardViewOuterBox)
|
||||
cardViewOuterBox.hidden = true;
|
||||
if (blankResultsPaneMessageBox)
|
||||
blankResultsPaneMessageBox.hidden = false;
|
||||
} else {
|
||||
if (abResultsTree)
|
||||
abResultsTree.hidden = false;
|
||||
if (cardViewOuterBox)
|
||||
cardViewOuterBox.hidden = false;
|
||||
if (blankResultsPaneMessageBox)
|
||||
blankResultsPaneMessageBox.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
function CloseAbView()
|
||||
{
|
||||
if (gAbView)
|
||||
gAbView.clearView();
|
||||
}
|
||||
|
||||
function GetOneOrMoreCardsSelected()
|
||||
{
|
||||
return (gAbView && (gAbView.selection.getRangeCount() > 0));
|
||||
}
|
||||
|
||||
function GetSelectedAddresses()
|
||||
{
|
||||
return GetAddressesForCards(GetSelectedAbCards());
|
||||
}
|
||||
|
||||
function GetNumSelectedCards()
|
||||
{
|
||||
try {
|
||||
return gAbView.selection.count;
|
||||
}
|
||||
catch (ex) {
|
||||
}
|
||||
|
||||
// if something went wrong, return 0 for the count.
|
||||
return 0;
|
||||
}
|
||||
|
||||
function GetSelectedCardTypes()
|
||||
{
|
||||
var cards = GetSelectedAbCards();
|
||||
if (!cards) {
|
||||
Components.utils.reportError("ERROR: GetSelectedCardTypes: |cards| is null.");
|
||||
return kNothingSelected; // no view
|
||||
}
|
||||
var count = cards.length;
|
||||
if (count == 0)
|
||||
return kNothingSelected; // nothing selected
|
||||
|
||||
var mailingListCnt = 0;
|
||||
var cardCnt = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
// We can assume no values from GetSelectedAbCards will be null.
|
||||
if (cards[i].isMailList)
|
||||
mailingListCnt++;
|
||||
else
|
||||
cardCnt++;
|
||||
}
|
||||
|
||||
return (mailingListCnt == 0) ? kCardsOnly :
|
||||
(cardCnt > 0) ? kListsAndCards :
|
||||
(mailingListCnt == 1) ? kSingleListOnly :
|
||||
kMultipleListsOnly;
|
||||
}
|
||||
|
||||
// NOTE, will return -1 if more than one card selected, or no cards selected.
|
||||
function GetSelectedCardIndex()
|
||||
{
|
||||
if (!gAbView)
|
||||
return -1;
|
||||
|
||||
var treeSelection = gAbView.selection;
|
||||
if (treeSelection.getRangeCount() == 1) {
|
||||
var start = new Object;
|
||||
var end = new Object;
|
||||
treeSelection.getRangeAt(0, start, end);
|
||||
if (start.value == end.value)
|
||||
return start.value;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// NOTE, returns the card if exactly one card is selected, null otherwise
|
||||
function GetSelectedCard()
|
||||
{
|
||||
var index = GetSelectedCardIndex();
|
||||
return (index == -1) ? null : gAbView.getCardFromRow(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a (possibly empty) list of cards
|
||||
*
|
||||
* It pushes only non-null/empty element, if any, into the returned list.
|
||||
*/
|
||||
function GetSelectedAbCards()
|
||||
{
|
||||
var abView = gAbView;
|
||||
|
||||
// if sidebar is open, and addressbook panel is open and focused,
|
||||
// then use the ab view from sidebar (gCurFrame is from sidebarOverlay.js)
|
||||
if (document.getElementById("sidebar-box")) {
|
||||
const abPanelUrl =
|
||||
"chrome://messenger/content/addressbook/addressbook-panel.xul";
|
||||
if (gCurFrame &&
|
||||
gCurFrame.getAttribute("src") == abPanelUrl &&
|
||||
document.commandDispatcher.focusedWindow == gCurFrame.contentDocument.defaultView)
|
||||
abView = gCurFrame.contentDocument.defaultView.gAbView;
|
||||
}
|
||||
|
||||
if (!abView)
|
||||
return [];
|
||||
|
||||
let cards = [];
|
||||
var count = abView.selection.getRangeCount();
|
||||
var current = 0;
|
||||
for (let i = 0; i < count; ++i) {
|
||||
let start = {};
|
||||
let end = {};
|
||||
|
||||
abView.selection.getRangeAt(i, start, end);
|
||||
|
||||
for (let j = start.value; j <= end.value; ++j) {
|
||||
// avoid inserting null element into the list. GetRangeAt() may be buggy.
|
||||
let tmp = abView.getCardFromRow(j);
|
||||
if (tmp) {
|
||||
cards.push(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cards;
|
||||
}
|
||||
|
||||
// XXX todo
|
||||
// an optimization might be to make this return
|
||||
// the selected ranges, which would be faster
|
||||
// when the user does large selections, but for now, let's keep it simple.
|
||||
function GetSelectedRows()
|
||||
{
|
||||
var selectedRows = "";
|
||||
|
||||
if (!gAbView)
|
||||
return selectedRows;
|
||||
|
||||
var rangeCount = gAbView.selection.getRangeCount();
|
||||
for (let i = 0; i < rangeCount; ++i) {
|
||||
var start = new Object;
|
||||
var end = new Object;
|
||||
gAbView.selection.getRangeAt(i, start, end);
|
||||
for (let j = start.value;j <= end.value; ++j) {
|
||||
if (selectedRows)
|
||||
selectedRows += ",";
|
||||
selectedRows += j;
|
||||
}
|
||||
}
|
||||
|
||||
return selectedRows;
|
||||
}
|
||||
|
||||
function AbSwapFirstNameLastName()
|
||||
{
|
||||
if (gAbView)
|
||||
gAbView.swapFirstNameLastName();
|
||||
}
|
||||
|
||||
function AbEditSelectedCard()
|
||||
{
|
||||
AbEditCard(GetSelectedCard());
|
||||
}
|
||||
|
||||
function AbResultsPaneOnClick(event)
|
||||
{
|
||||
// we only care about button 0 (left click) events
|
||||
if (event.button != 0) return;
|
||||
|
||||
// all we need to worry about here is double clicks
|
||||
// and column header clicks.
|
||||
//
|
||||
// we get in here for clicks on the "treecol" (headers)
|
||||
// and the "scrollbarbutton" (scrollbar buttons)
|
||||
// we don't want those events to cause a "double click"
|
||||
|
||||
var t = event.originalTarget;
|
||||
|
||||
if (t.localName == "treecol") {
|
||||
var sortDirection;
|
||||
var currentDirection = t.getAttribute("sortDirection");
|
||||
|
||||
// Revert the sort order. If none is set, use Ascending.
|
||||
sortDirection = currentDirection == kDefaultAscending ?
|
||||
kDefaultDescending : kDefaultAscending;
|
||||
|
||||
SortAndUpdateIndicators(t.id, sortDirection);
|
||||
}
|
||||
else if (t.localName == "treechildren") {
|
||||
// figure out what row the click was in
|
||||
var row = gAbResultsTree.treeBoxObject.getRowAt(event.clientX,
|
||||
event.clientY);
|
||||
if (row == -1)
|
||||
return;
|
||||
|
||||
if (event.detail == 2)
|
||||
AbResultsPaneDoubleClick(gAbView.getCardFromRow(row));
|
||||
}
|
||||
}
|
||||
|
||||
function AbSortAscending()
|
||||
{
|
||||
var sortColumn = gAbResultsTree.getAttribute("sortCol");
|
||||
SortAndUpdateIndicators(sortColumn, kDefaultAscending);
|
||||
}
|
||||
|
||||
function AbSortDescending()
|
||||
{
|
||||
var sortColumn = gAbResultsTree.getAttribute("sortCol");
|
||||
SortAndUpdateIndicators(sortColumn, kDefaultDescending);
|
||||
}
|
||||
|
||||
function SortResultPane(sortColumn)
|
||||
{
|
||||
var sortDirection = kDefaultAscending;
|
||||
if (gAbView)
|
||||
sortDirection = gAbView.sortDirection;
|
||||
|
||||
SortAndUpdateIndicators(sortColumn, sortDirection);
|
||||
}
|
||||
|
||||
function SortAndUpdateIndicators(sortColumn, sortDirection)
|
||||
{
|
||||
UpdateSortIndicators(sortColumn, sortDirection);
|
||||
|
||||
if (gAbView)
|
||||
gAbView.sortBy(sortColumn, sortDirection);
|
||||
}
|
||||
|
||||
function UpdateSortIndicators(colID, sortDirection)
|
||||
{
|
||||
var sortedColumn = null;
|
||||
|
||||
// set the sort indicator on the column we are sorted by
|
||||
if (colID) {
|
||||
sortedColumn = document.getElementById(colID);
|
||||
if (sortedColumn) {
|
||||
sortedColumn.setAttribute("sortDirection",sortDirection);
|
||||
gAbResultsTree.setAttribute("sortCol", colID);
|
||||
}
|
||||
}
|
||||
|
||||
// remove the sort indicator from all the columns
|
||||
// except the one we are sorted by
|
||||
var currCol = gAbResultsTree.firstChild.firstChild;
|
||||
while (currCol) {
|
||||
if (currCol != sortedColumn && currCol.localName == "treecol")
|
||||
currCol.removeAttribute("sortDirection");
|
||||
currCol = currCol.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
function InvalidateResultsPane()
|
||||
{
|
||||
if (gAbResultsTree)
|
||||
gAbResultsTree.treeBoxObject.invalidate();
|
||||
}
|
||||
|
||||
// Controller object for Results Pane
|
||||
var ResultsPaneController =
|
||||
{
|
||||
supportsCommand: function(command)
|
||||
{
|
||||
switch (command) {
|
||||
case "cmd_selectAll":
|
||||
case "cmd_delete":
|
||||
case "button_delete":
|
||||
case "cmd_properties":
|
||||
case "cmd_newlist":
|
||||
case "cmd_newCard":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
isCommandEnabled: function(command)
|
||||
{
|
||||
switch (command) {
|
||||
case "cmd_selectAll":
|
||||
return true;
|
||||
case "cmd_delete":
|
||||
case "button_delete":
|
||||
var numSelected;
|
||||
var enabled = false;
|
||||
if (gAbView && gAbView.selection) {
|
||||
if (gAbView.directory)
|
||||
enabled = !gAbView.directory.readOnly;
|
||||
numSelected = gAbView.selection.count;
|
||||
}
|
||||
else
|
||||
numSelected = 0;
|
||||
|
||||
// fix me, don't update on isCommandEnabled
|
||||
if (command == "cmd_delete") {
|
||||
switch (GetSelectedCardTypes()) {
|
||||
case kSingleListOnly:
|
||||
goSetMenuValue(command, "valueList");
|
||||
break;
|
||||
case kMultipleListsOnly:
|
||||
goSetMenuValue(command, "valueLists");
|
||||
break;
|
||||
case kListsAndCards:
|
||||
goSetMenuValue(command, "valueItems");
|
||||
break;
|
||||
case kCardsOnly:
|
||||
default:
|
||||
if (numSelected < 2)
|
||||
goSetMenuValue(command, "valueCard");
|
||||
else
|
||||
goSetMenuValue(command, "valueCards");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (enabled && (numSelected > 0));
|
||||
case "cmd_properties":
|
||||
// Temporary fix for SeaMonkey (see bug 1318852).
|
||||
// goSetLabelAccesskeyTooltiptext() is only defined in mail/.
|
||||
// This will be removed in due course and therefore the
|
||||
// block wasn't indented.
|
||||
if (typeof goSetLabelAccesskeyTooltiptext == "function") {
|
||||
let labelAttr = "valueGeneric";
|
||||
let accKeyAttr = "valueGenericAccessKey";
|
||||
let tooltipTextAttr = "valueGenericTooltipText";
|
||||
switch (GetSelectedCardTypes()) {
|
||||
// Set cmd_properties UI according to the type of the selected item(s),
|
||||
// even with multiple selections for which cmd_properties is
|
||||
// not yet available and hence disabled.
|
||||
case kMultipleListsOnly:
|
||||
case kSingleListOnly:
|
||||
labelAttr = "valueMailingList";
|
||||
accKeyAttr = "valueMailingListAccessKey";
|
||||
tooltipTextAttr = "valueMailingListTooltipText";
|
||||
break;
|
||||
case kCardsOnly:
|
||||
labelAttr = "valueContact";
|
||||
accKeyAttr = "valueContactAccessKey";
|
||||
tooltipTextAttr = "valueContactTooltipText";
|
||||
break;
|
||||
case kListsAndCards:
|
||||
default:
|
||||
//use generic set of attributes declared above
|
||||
break;
|
||||
}
|
||||
// This code is shared between main AB and composition's contacts sidebar.
|
||||
// Note that in composition, there's no cmd_properties-button (yet);
|
||||
// the resulting dump() should be ignored.
|
||||
goSetLabelAccesskeyTooltiptext("cmd_properties-button", null, null,
|
||||
tooltipTextAttr);
|
||||
goSetLabelAccesskeyTooltiptext("cmd_properties-contextMenu",
|
||||
labelAttr, accKeyAttr);
|
||||
goSetLabelAccesskeyTooltiptext("cmd_properties-menu",
|
||||
labelAttr, accKeyAttr);
|
||||
}
|
||||
// While "Edit Contact" dialogue is still modal (bug 115904, bug 135126),
|
||||
// only enable "Properties" button for single selection; then fix bug 119999.
|
||||
return (GetNumSelectedCards() == 1);
|
||||
case "cmd_newlist":
|
||||
case "cmd_newCard":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
doCommand: function(command)
|
||||
{
|
||||
switch (command) {
|
||||
case "cmd_selectAll":
|
||||
if (gAbView)
|
||||
gAbView.selectAll();
|
||||
break;
|
||||
case "cmd_delete":
|
||||
case "button_delete":
|
||||
AbDelete();
|
||||
break;
|
||||
case "cmd_properties":
|
||||
AbEditSelectedCard();
|
||||
break;
|
||||
case "cmd_newlist":
|
||||
AbNewList();
|
||||
break;
|
||||
case "cmd_newCard":
|
||||
AbNewCard();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onEvent: function(event)
|
||||
{
|
||||
// on blur events set the menu item texts back to the normal values
|
||||
if (event == "blur")
|
||||
goSetMenuValue("cmd_delete", "valueDefault");
|
||||
}
|
||||
};
|
||||
|
||||
function SelectFirstCard()
|
||||
{
|
||||
if (gAbView && gAbView.selection && (gAbView.selection.count > 0))
|
||||
gAbView.selection.select(0);
|
||||
}
|
||||
90
mailnews/addrbook/content/abResultsPaneOverlay.xul
Normal file
90
mailnews/addrbook/content/abResultsPaneOverlay.xul
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/addressbook/abResultsPane.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://messenger/locale/addressbook/abResultsPaneOverlay.dtd">
|
||||
|
||||
<overlay
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript" src="chrome://messenger/content/addressbook/abResultsPane.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/nsDragAndDrop.js"/>
|
||||
<script type="application/javascript" src="chrome://messenger/content/addressbook/abDragDrop.js"/>
|
||||
|
||||
<tree id="abResultsTree" flex="1" enableColumnDrag="true" class="plain focusring"
|
||||
onclick="AbResultsPaneOnClick(event);"
|
||||
onselect="this.view.selectionChanged(); document.commandDispatcher.updateCommands('addrbook-select');"
|
||||
sortCol="GeneratedName"
|
||||
persist="sortCol height">
|
||||
|
||||
<treecols id="abResultsTreeCols">
|
||||
<!-- these column ids must match up to the mork column names, except for GeneratedName, see nsIAddrDatabase.idl -->
|
||||
<treecol id="GeneratedName"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&GeneratedName.label;" primary="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="PrimaryEmail"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&PrimaryEmail.label;"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="_AimScreenName"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&ScreenName.label;"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="Company"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&Company.label;"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="NickName"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&NickName.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="SecondEmail"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&SecondEmail.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="Department"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&Department.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="JobTitle"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&JobTitle.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="CellularNumber"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&CellularNumber.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="PagerNumber"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&PagerNumber.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="FaxNumber"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&FaxNumber.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="HomePhone"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&HomePhone.label;" hidden="true"/>
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="WorkPhone"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&WorkPhone.label;"/>
|
||||
|
||||
<!-- LOCALIZATION NOTE: _PhoneticName may be enabled for Japanese builds. -->
|
||||
<!--
|
||||
<splitter class="tree-splitter"/>
|
||||
<treecol id="_PhoneticName"
|
||||
persist="hidden ordinal width sortDirection" flex="1"
|
||||
label="&_PhoneticName.label;" hidden="true"/>
|
||||
-->
|
||||
|
||||
</treecols>
|
||||
<treechildren ondragstart="nsDragAndDrop.startDrag(event, abResultsPaneObserver);"/>
|
||||
</tree>
|
||||
|
||||
</overlay>
|
||||
|
||||
439
mailnews/addrbook/content/addrbookWidgets.xml
Normal file
439
mailnews/addrbook/content/addrbookWidgets.xml
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<bindings id="addrbookBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="addrbooks-menupopup"
|
||||
extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
<implementation implements="nsIAbListener">
|
||||
<!-- A cache of nsIAbDirectory objects. -->
|
||||
<field name="_directories">[]</field>
|
||||
|
||||
<!-- Represents the nsIAbDirectory attribute used as the value of the
|
||||
parent menulist. Defaults to URI but can be e.g. dirPrefId -->
|
||||
<field name="_value">this.getAttribute("value") || "URI"</field>
|
||||
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
// Init the address book cache.
|
||||
const nsIAbDirectory = Components.interfaces.nsIAbDirectory;
|
||||
let directories = MailServices.ab.directories;
|
||||
while (directories && directories.hasMoreElements()) {
|
||||
var ab = directories.getNext();
|
||||
if (ab instanceof nsIAbDirectory && this._matches(ab))
|
||||
this._directories.push(ab);
|
||||
}
|
||||
|
||||
this._directories.sort(this._compare);
|
||||
|
||||
// Now create menuitems for all displayed directories.
|
||||
var menulist = this.parentNode;
|
||||
var value = this._value;
|
||||
this._directories.forEach(function (ab) {
|
||||
menulist.appendItem(ab.dirName, ab[value]);
|
||||
});
|
||||
if (this.hasAttribute("none")) {
|
||||
// Create a dummy menuitem representing no selection.
|
||||
this._directories.unshift(null);
|
||||
menulist.insertItemAt(0, this.getAttribute("none"), "");
|
||||
}
|
||||
|
||||
// Attempt to select the persisted or otherwise first directory.
|
||||
menulist.value = menulist.value;
|
||||
if (!menulist.selectedItem && this.hasChildNodes())
|
||||
menulist.selectedIndex = 0;
|
||||
|
||||
const nsIAbListener = Components.interfaces.nsIAbListener;
|
||||
// Add a listener so we can update correctly if the list should change
|
||||
MailServices.ab.addAddressBookListener(this,
|
||||
nsIAbListener.itemAdded |
|
||||
nsIAbListener.directoryRemoved |
|
||||
nsIAbListener.itemChanged);
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<destructor>
|
||||
<![CDATA[
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
MailServices.ab.removeAddressBookListener(this);
|
||||
|
||||
// Empty out anything in the list.
|
||||
while (this.hasChildNodes())
|
||||
this.lastChild.remove();
|
||||
]]>
|
||||
</destructor>
|
||||
|
||||
<!-- nsIAbListener methods -->
|
||||
<method name="onItemAdded">
|
||||
<parameter name="aParentDir"/>
|
||||
<parameter name="aItem"/>
|
||||
<body><![CDATA[
|
||||
// Are we interested in this new directory?
|
||||
if (aItem instanceof Components.interfaces.nsIAbDirectory &&
|
||||
!aItem.isMailList && this._matches(aItem)) {
|
||||
this._directories.push(aItem);
|
||||
this._directories.sort(this._compare);
|
||||
// Insert the new menuitem at the position to which it was sorted.
|
||||
this.parentNode.insertItemAt(this._directories.indexOf(aItem),
|
||||
aItem.dirName, aItem[this._value]);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onItemRemoved">
|
||||
<parameter name="aParentDir"/>
|
||||
<parameter name="aItem"/>
|
||||
<body><![CDATA[
|
||||
if (aItem instanceof Components.interfaces.nsIAbDirectory &&
|
||||
!aItem.isMailList) {
|
||||
// Find the item in the list to remove
|
||||
// We can't use indexOf here because we need loose equality
|
||||
for (var index = this._directories.length; --index >= 0; )
|
||||
if (this._directories[index] == aItem)
|
||||
break;
|
||||
if (index != -1)
|
||||
// Are we removing the selected directory?
|
||||
if (this.parentNode.selectedItem ==
|
||||
this.removeChild(this.childNodes[index]))
|
||||
// If so, try to select the first directory, if available.
|
||||
if (this.hasChildNodes())
|
||||
this.firstChild.doCommand();
|
||||
else
|
||||
this.parentNode.selectedItem = null;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="onItemPropertyChanged">
|
||||
<parameter name="aItem"/>
|
||||
<parameter name="aProperty"/>
|
||||
<parameter name="aOldValue"/>
|
||||
<parameter name="aNewValue"/>
|
||||
<body><![CDATA[
|
||||
if (aItem instanceof Components.interfaces.nsIAbDirectory &&
|
||||
!aItem.isMailList) {
|
||||
// Find the item in the list to rename.
|
||||
// We can't use indexOf here because we need loose equality
|
||||
for (var oldIndex = this._directories.length; --oldIndex >= 0; )
|
||||
if (this._directories[oldIndex] == aItem)
|
||||
break;
|
||||
if (oldIndex != -1) {
|
||||
// Cache the matching item so that we can use indexOf next time.
|
||||
aItem = this._directories[oldIndex];
|
||||
var child = this.childNodes[oldIndex];
|
||||
child.label = aItem.dirName;
|
||||
this._directories.sort(this._compare);
|
||||
// Reorder the menuitems if renaming changed the directory index.
|
||||
var newIndex = this._directories.indexOf(aItem);
|
||||
if (newIndex < oldIndex)
|
||||
this.insertBefore(child, this.childNodes[newIndex]);
|
||||
else if (newIndex > oldIndex)
|
||||
this.insertBefore(child, this.childNodes[newIndex].nextSibling);
|
||||
}
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- Private methods -->
|
||||
<!-- Tests to see whether this directory should display in the list. -->
|
||||
<method name="_matches">
|
||||
<parameter name="ab"/>
|
||||
<body><![CDATA[
|
||||
// This condition is used for instance when creating cards
|
||||
if (this.getAttribute("writable") == "true" && ab.readOnly)
|
||||
return false;
|
||||
|
||||
// This condition is used for instance when creating mailing lists
|
||||
if (this.getAttribute("supportsmaillists") == "true" &&
|
||||
!ab.supportsMailingLists)
|
||||
return false;
|
||||
|
||||
return this.getAttribute(ab.isRemote ? "localonly" : "remoteonly") != "true";
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!-- Used to sort directories in order -->
|
||||
<method name="_compare">
|
||||
<parameter name="a"/>
|
||||
<parameter name="b"/>
|
||||
<body><![CDATA[
|
||||
// Null at the very top.
|
||||
if (!a)
|
||||
return -1;
|
||||
|
||||
if (!b)
|
||||
return 1;
|
||||
|
||||
// Personal at the top.
|
||||
const kPersonalAddressbookURI = "moz-abmdbdirectory://abook.mab";
|
||||
if (a.URI == kPersonalAddressbookURI)
|
||||
return -1;
|
||||
|
||||
if (b.URI == kPersonalAddressbookURI)
|
||||
return 1;
|
||||
|
||||
// Collected at the bottom.
|
||||
const kCollectedAddressbookURI = "moz-abmdbdirectory://history.mab";
|
||||
if (a.URI == kCollectedAddressbookURI)
|
||||
return 1;
|
||||
|
||||
if (b.URI == kCollectedAddressbookURI)
|
||||
return -1;
|
||||
|
||||
// Sort books of the same type by name.
|
||||
if (a.dirType == b.dirType)
|
||||
return a.dirName.localeCompare(b.dirName);
|
||||
|
||||
// If one of the dirTypes is PAB and the other is something else,
|
||||
// then the other will go below the one of type PAB.
|
||||
const PABDirectory = 2;
|
||||
if (a.dirType == PABDirectory)
|
||||
return -1;
|
||||
|
||||
if (b.dirType == PABDirectory)
|
||||
return 1;
|
||||
|
||||
// Sort anything else by the dir type.
|
||||
return a.dirType - b.dirType;
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="map-list"
|
||||
extends="chrome://global/content/bindings/popup.xml#popup">
|
||||
<implementation>
|
||||
<property name="mapURL" readonly="true">
|
||||
<getter><![CDATA[
|
||||
return this._createMapItURL();
|
||||
]]></getter>
|
||||
</property>
|
||||
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this._setWidgetDisabled(true);
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<!--
|
||||
Initializes the necessary address data from an addressbook card.
|
||||
@param aCard A nsIAbCard to get the address data from.
|
||||
@param aAddrPrefix A prefix of the card properties to use. Use "Home" or "Work".
|
||||
-->
|
||||
<method name="initMapAddressFromCard">
|
||||
<parameter name="aCard"/>
|
||||
<parameter name="aAddrPrefix"/>
|
||||
<body><![CDATA[
|
||||
let mapItURLFormat = this._getMapURLPref(0);
|
||||
let doNotShowMap = !mapItURLFormat || !aAddrPrefix || !aCard;
|
||||
this._setWidgetDisabled(doNotShowMap);
|
||||
if (doNotShowMap)
|
||||
return;
|
||||
|
||||
this.setAttribute("map_address1", aCard.getProperty(aAddrPrefix + "Address"));
|
||||
this.setAttribute("map_address2", aCard.getProperty(aAddrPrefix + "Address2"));
|
||||
this.setAttribute("map_city" , aCard.getProperty(aAddrPrefix + "City"));
|
||||
this.setAttribute("map_state" , aCard.getProperty(aAddrPrefix + "State"));
|
||||
this.setAttribute("map_zip" , aCard.getProperty(aAddrPrefix + "ZipCode"));
|
||||
this.setAttribute("map_country" , aCard.getProperty(aAddrPrefix + "Country"));
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Initializes the necessary address data from passed in values.
|
||||
-->
|
||||
<method name="initMapAddress">
|
||||
<parameter name="aAddr1"/>
|
||||
<parameter name="aAddr2"/>
|
||||
<parameter name="aCity"/>
|
||||
<parameter name="aState"/>
|
||||
<parameter name="aZip"/>
|
||||
<parameter name="aCountry"/>
|
||||
<body><![CDATA[
|
||||
let mapItURLFormat = this._getMapURLPref(0);
|
||||
let doNotShowMap = !mapItURLFormat || !(aAddr1 + aAddr2 + aCity + aState + aZip + aCountry);
|
||||
this._setWidgetDisabled(doNotShowMap);
|
||||
if (doNotShowMap)
|
||||
return;
|
||||
|
||||
this.setAttribute("map_address1", aAddr1);
|
||||
this.setAttribute("map_address2", aAddr2);
|
||||
this.setAttribute("map_city" , aCity);
|
||||
this.setAttribute("map_state" , aState);
|
||||
this.setAttribute("map_zip" , aZip);
|
||||
this.setAttribute("map_country" , aCountry);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Sets the disabled/enabled state of the parent widget (e.g. a button).
|
||||
-->
|
||||
<method name="_setWidgetDisabled">
|
||||
<parameter name="aDisabled"/>
|
||||
<body><![CDATA[
|
||||
this.parentNode.disabled = aDisabled;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Returns the Map service URL from localized pref. Returns null if there
|
||||
is none at the given index.
|
||||
@param aIndex The index of the service to return. 0 is the default service.
|
||||
-->
|
||||
<method name="_getMapURLPref">
|
||||
<parameter name="aIndex"/>
|
||||
<body><![CDATA[
|
||||
let url = null;
|
||||
if (!aIndex) {
|
||||
url = Services.prefs.getComplexValue("mail.addr_book.mapit_url.format",
|
||||
Components.interfaces.nsIPrefLocalizedString).data;
|
||||
} else {
|
||||
try {
|
||||
url = Services.prefs.getComplexValue("mail.addr_book.mapit_url." + aIndex + ".format",
|
||||
Components.interfaces.nsIPrefLocalizedString).data;
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
return url;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Builds menuitem elements representing map services defined in prefs
|
||||
and attaches them to the specified button.
|
||||
-->
|
||||
<method name="_listMapServices">
|
||||
<body><![CDATA[
|
||||
let index = 1;
|
||||
let itemFound = true;
|
||||
let defaultFound = false;
|
||||
const kUserIndex = 100;
|
||||
let aMapList = this;
|
||||
while (aMapList.hasChildNodes()) {
|
||||
aMapList.lastChild.remove();
|
||||
}
|
||||
|
||||
let defaultUrl = this._getMapURLPref(0);
|
||||
|
||||
// Creates the menuitem with supplied data.
|
||||
function addMapService(aUrl, aName) {
|
||||
let item = document.createElement("menuitem");
|
||||
item.setAttribute("url", aUrl);
|
||||
item.setAttribute("label", aName);
|
||||
item.setAttribute("type", "radio");
|
||||
item.setAttribute("name", "mapit_service");
|
||||
if (aUrl == defaultUrl)
|
||||
item.setAttribute("checked", "true");
|
||||
aMapList.appendChild(item);
|
||||
}
|
||||
|
||||
// Generates a useful generic name by cutting out only the host address.
|
||||
function generateName(aUrl) {
|
||||
return new URL(aUrl).hostname;
|
||||
}
|
||||
|
||||
// Add all defined map services as menuitems.
|
||||
while (itemFound) {
|
||||
let urlName;
|
||||
let urlTemplate = this._getMapURLPref(index);
|
||||
if (!urlTemplate) {
|
||||
itemFound = false;
|
||||
} else {
|
||||
// Name is not mandatory, generate one if not found.
|
||||
try {
|
||||
urlName = Services.prefs.getComplexValue("mail.addr_book.mapit_url." + index + ".name",
|
||||
Components.interfaces.nsIPrefLocalizedString).data;
|
||||
} catch (e) {
|
||||
urlName = generateName(urlTemplate);
|
||||
}
|
||||
}
|
||||
if (itemFound) {
|
||||
addMapService(urlTemplate, urlName);
|
||||
index++;
|
||||
if (urlTemplate == defaultUrl)
|
||||
defaultFound = true;
|
||||
} else if (index < kUserIndex) {
|
||||
// After iterating the base region provided urls, check for user defined ones.
|
||||
index = kUserIndex;
|
||||
itemFound = true;
|
||||
}
|
||||
}
|
||||
if (!defaultFound) {
|
||||
// If user had put a customized map URL into mail.addr_book.mapit_url.format
|
||||
// preserve it as a new map service named with the URL.
|
||||
// 'index' now points to the first unused entry in prefs.
|
||||
let defaultName = generateName(defaultUrl);
|
||||
addMapService(defaultUrl, defaultName);
|
||||
Services.prefs.setCharPref("mail.addr_book.mapit_url." + index + ".format",
|
||||
defaultUrl);
|
||||
Services.prefs.setCharPref("mail.addr_book.mapit_url." + index + ".name",
|
||||
defaultName);
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Save user selected mapping service.
|
||||
@param aItem The chosen menuitem with map service.
|
||||
-->
|
||||
<method name="_chooseMapService">
|
||||
<parameter name="aItem"/>
|
||||
<body><![CDATA[
|
||||
// Save selected URL as the default.
|
||||
let defaultUrl = Components.classes["@mozilla.org/pref-localizedstring;1"]
|
||||
.createInstance(Components.interfaces.nsIPrefLocalizedString);
|
||||
defaultUrl.data = aItem.getAttribute("url");
|
||||
Services.prefs.setComplexValue("mail.addr_book.mapit_url.format",
|
||||
Components.interfaces.nsIPrefLocalizedString, defaultUrl);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Generate map URL in the href attribute.
|
||||
-->
|
||||
<method name="_createMapItURL">
|
||||
<body><![CDATA[
|
||||
let urlFormat = this._getMapURLPref(0);
|
||||
if (!urlFormat)
|
||||
return null;
|
||||
|
||||
let address1 = this.getAttribute("map_address1");
|
||||
let address2 = this.getAttribute("map_address2");
|
||||
let city = this.getAttribute("map_city");
|
||||
let state = this.getAttribute("map_state");
|
||||
let zip = this.getAttribute("map_zip");
|
||||
let country = this.getAttribute("map_country");
|
||||
|
||||
urlFormat = urlFormat.replace("@A1", encodeURIComponent(address1));
|
||||
urlFormat = urlFormat.replace("@A2", encodeURIComponent(address2));
|
||||
urlFormat = urlFormat.replace("@CI", encodeURIComponent(city));
|
||||
urlFormat = urlFormat.replace("@ST", encodeURIComponent(state));
|
||||
urlFormat = urlFormat.replace("@ZI", encodeURIComponent(zip));
|
||||
urlFormat = urlFormat.replace("@CO", encodeURIComponent(country));
|
||||
|
||||
return urlFormat;
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<handler event="command">
|
||||
<![CDATA[
|
||||
this._chooseMapService(event.target);
|
||||
event.stopPropagation();
|
||||
]]>
|
||||
</handler>
|
||||
<handler event="popupshowing">
|
||||
<![CDATA[
|
||||
this._listMapServices();
|
||||
]]>
|
||||
</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
</bindings>
|
||||
94
mailnews/addrbook/content/print.css
Normal file
94
mailnews/addrbook/content/print.css
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* 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/. */
|
||||
|
||||
directory {
|
||||
display: block;
|
||||
}
|
||||
|
||||
section {
|
||||
display:block;
|
||||
margin: 15px 20px;
|
||||
}
|
||||
|
||||
sectiontitle {
|
||||
display:block;
|
||||
font-weight:bold;
|
||||
font-family:verdana;
|
||||
font-size:small;
|
||||
}
|
||||
|
||||
labelrow {
|
||||
display:block;
|
||||
}
|
||||
|
||||
label {
|
||||
display:inline;
|
||||
font-family:verdana;
|
||||
font-size:small;
|
||||
}
|
||||
|
||||
GeneratedName {
|
||||
display:block;
|
||||
font-weight:bold;
|
||||
font-family:verdana;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 3px;
|
||||
margin-inline-end: 10px;
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
|
||||
FirstName, LastName,
|
||||
HomeAddress, HomeAddress2, HomeCountry,
|
||||
WorkAddress, WorkAddress2, WorkCountry,
|
||||
JobTitle, Department, Company, Notes {
|
||||
display: block;
|
||||
font-family: verdana;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
DisplayName, NickName,
|
||||
WorkPhone, HomePhone, FaxNumber, PagerNumber, CellularNumber,
|
||||
HomeCity, HomeState, HomeZipCode,
|
||||
WorkCity, WorkState, WorkZipCode,
|
||||
Custom1, Custom2, Custom3, Custom4 {
|
||||
display: inline;
|
||||
font-family: verdana;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
PrimaryEmail, SecondEmail,
|
||||
WebPage1, WebPage2 {
|
||||
display: block;
|
||||
font-family: verdana;
|
||||
font-size: small;
|
||||
text-decoration: underline;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
separator {
|
||||
display: block;
|
||||
border-bottom: 1px solid #000000;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-inline-end: 0px;
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
|
||||
table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
tr {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
td {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
PreferMailFormat {
|
||||
display: none;
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue