mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
033f7a0696
r/sr=hewitt, sspitzer
1050 lines
39 KiB
JavaScript
1050 lines
39 KiB
JavaScript
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
* March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*/
|
|
|
|
/* This is where functions related to displaying the headers for a selected message in the
|
|
message pane live. */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
// Warning: if you go to modify any of these JS routines please get a code review from either
|
|
// hangas@netscape.com or mscott@netscape.com. It's critical that the code in here for displaying
|
|
// the message headers for a selected message remain as fast as possible. In particular,
|
|
// right now, we only introduce one reflow per message. i.e. if you click on a message in the thread
|
|
// pane, we batch up all the changes for displaying the header pane (to, cc, attachements button, etc.)
|
|
// and we make a single pass to display them. It's critical that we maintain this one reflow per message
|
|
// view in the message header pane.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//NOTE: gMessengerBundle must be defined and set or this Overlay won't work
|
|
|
|
var msgHeaderParserContractID = "@mozilla.org/messenger/headerparser;1";
|
|
var abAddressCollectorContractID = "@mozilla.org/addressbook/services/addressCollecter;1";
|
|
|
|
// gGeneratedViewAllHeaderInfo --> we clear this every time we start to display a new message.
|
|
// the view all header popup will set it when we first generate a view of all the headers. this is
|
|
// just so it won't try to regenerate all the information every time the user clicks on the popup.
|
|
var gGeneratedViewAllHeaderInfo = false;
|
|
var gViewAllHeaders = false;
|
|
var gNumAddressesToShow = 3;
|
|
var gShowUserAgent = false;
|
|
var gCollectIncoming = false;
|
|
var gCollectNewsgroup = false;
|
|
var gCollapsedHeaderViewMode = false;
|
|
var gBuildAttachmentsForCurrentMsg = false;
|
|
var gBuildAttachmentPopupForCurrentMsg = true;
|
|
|
|
var msgHeaderParser = Components.classes[msgHeaderParserContractID].getService(Components.interfaces.nsIMsgHeaderParser);
|
|
var abAddressCollector = Components.classes[abAddressCollectorContractID].getService(Components.interfaces.nsIAbAddressCollecter);
|
|
|
|
// All these variables are introduced to keep track of insertion and deletion of the toggle button either
|
|
// as the first node in the list of emails or the last node.
|
|
var numOfEmailsInEnumerator;
|
|
|
|
// var used to determine whether to show the toggle button at the
|
|
// beginning or at the end of a list of emails in to/cc fields.
|
|
var gNumOfEmailsToShowToggleButtonInFront = 15;
|
|
|
|
// For every possible "view" in the message pane, you need to define the header names you want to
|
|
// see in that view. In addition, include information describing how you want that header field to be
|
|
// presented. i.e. if it's an email address field, if you want a toggle inserted on the node in case
|
|
// of multiple email addresses, etc. We'll then use this static table to dynamically generate header view entries
|
|
// which manipulate the UI.
|
|
// When you add a header to one of these view lists you can specify the following properties:
|
|
// name: the name of the header. i.e. "to", "subject". This must be in lower case and the name of the
|
|
// header is used to help dynamically generate ids for objects in the document. (REQUIRED)
|
|
// useToggle: true if the values for this header are multiple email addresses and you want a
|
|
// a toggle icon to show a short vs. long list (DEFAULT: false)
|
|
// useShortView: (only works on some fields like From). If the field has a long presentation and a
|
|
// short presentation we'll use the short one. i.e. if you are showing the From field and you
|
|
// set this to true, we can show just "John Doe" instead of "John Doe <jdoe@netscape.net>".
|
|
// (DEFAULT: false)
|
|
//
|
|
// outputFunction: this is a method which takes a headerEntry (see the definition below) and a header value
|
|
// This allows you to provide your own methods for actually determining how the header value
|
|
// is displayed. (DEFAULT: updateHeaderValue which just sets the header value on the text node)
|
|
|
|
// Our first view is the collapsed view. This is very light weight view of the data. We only show a couple
|
|
// fields.
|
|
var gCollapsedHeaderList = [ {name:"subject", outputFunction:updateHeaderValueInTextNode},
|
|
{name:"from", useShortView:true, outputFunction:OutputEmailAddresses},
|
|
{name:"date", outputFunction:updateHeaderValueInTextNode}];
|
|
|
|
// We also have an expanded header view. This shows many of your more common (and useful) headers.
|
|
var gExpandedHeaderList = [ {name:"subject"},
|
|
{name:"from", outputFunction:OutputEmailAddresses},
|
|
{name:"reply-to", isEmailAddress:true, outputFunction:OutputEmailAddresses},
|
|
{name:"date"},
|
|
{name:"to", useToggle:true, outputFunction:OutputEmailAddresses},
|
|
{name:"cc", useToggle:true, outputFunction:OutputEmailAddresses},
|
|
{name:"bcc", useToggle:true, outputFunction:OutputEmailAddresses},
|
|
{name:"newsgroups", outputFunction:OutputNewsgroups},
|
|
{name:"followup-to", outputFunction:OutputNewsgroups} ];
|
|
|
|
// Now, for each view the message pane can generate, we need a global table of headerEntries. These
|
|
// header entry objects are generated dynamically based on the static date in the header lists (see above)
|
|
// and elements we find in the DOM based on properties in the header lists.
|
|
var gCollapsedHeaderView = {};
|
|
var gExpandedHeaderView = {};
|
|
|
|
// currentHeaderData --> this is an array of header name and value pairs for the currently displayed message.
|
|
// it's purely a data object and has no view information. View information is contained in the view objects.
|
|
// for a given entry in this array you can ask for:
|
|
// .headerName ---> name of the header (i.e. 'to'). Always stored in lower case
|
|
// .headerValue --> value of the header "johndoe@netscape.net"
|
|
var currentHeaderData = {};
|
|
|
|
// For the currently displayed message, we store all the attachment data. When displaying a particular
|
|
// view, it's up to the view layer to extract this attachment data and turn it into something useful.
|
|
// For a given entry in the attachments list, you can ask for the following properties:
|
|
// .contentType --> the content type of the attachment
|
|
// url --> an imap, or mailbox url which can be used to fetch the message
|
|
// uri --> an RDF URI which refers to the message containig the attachment
|
|
// notDownloaded --> boolean flag stating whether the attachment is downloaded or not.
|
|
var currentAttachments = new Array();
|
|
|
|
// createHeaderEntry --> our constructor method which creates a header Entry
|
|
// based on an entry in one of the header lists. A header entry is different from a header list.
|
|
// a header list just describes how you want a particular header to be presented. The header entry
|
|
// actually has knowledge about the DOM and the actual DOM elements associated with the header.
|
|
// prefix --> the name of the view (i.e. "collapsed", "expanded")
|
|
// headerListInfo --> entry from a header list.
|
|
function createHeaderEntry(prefix, headerListInfo)
|
|
{
|
|
var partialIDName = prefix + headerListInfo.name;
|
|
//dump('enclosingBox id = ' + partialIDName + 'Box' + '\n');
|
|
this.enclosingBox = document.getElementById(partialIDName + 'Box');
|
|
//dump(this.enclosingBox + \'n');
|
|
this.textNode = document.getElementById(partialIDName + 'Value');
|
|
this.isValid = false;
|
|
|
|
if ("useToggle" in headerListInfo)
|
|
{
|
|
this.useToggle = headerListInfo.useToggle;
|
|
if (this.useToggle) // find the toggle icon in the document
|
|
{
|
|
this.toggleIcon = document.getElementById(partialIDName + 'ToggleIcon');
|
|
this.longTextNode = document.getElementById(partialIDName + 'Value' + 'Long');
|
|
}
|
|
}
|
|
else
|
|
this.useToggle = false;
|
|
|
|
if ("useShortView" in headerListInfo)
|
|
this.useShortView = headerListInfo.useShortView;
|
|
else
|
|
this.useShortView = false;
|
|
|
|
if ("outputFunction" in headerListInfo)
|
|
this.outputFunction = headerListInfo.outputFunction;
|
|
else
|
|
this.outputFunction = updateHeaderValue;
|
|
}
|
|
|
|
function initializeHeaderViewTables()
|
|
{
|
|
// iterate over each header in our header list arrays and create header entries
|
|
// for each one. These header entries are then stored in the appropriate header table
|
|
for (var index = 0; index < gCollapsedHeaderList.length; index++)
|
|
{
|
|
gCollapsedHeaderView[gCollapsedHeaderList[index].name] =
|
|
new createHeaderEntry('collapsed', gCollapsedHeaderList[index]);
|
|
}
|
|
|
|
for (index = 0; index < gExpandedHeaderList.length; index++)
|
|
{
|
|
var headerName = gExpandedHeaderList[index].name;
|
|
gExpandedHeaderView[headerName] = new createHeaderEntry('expanded', gExpandedHeaderList[index]);
|
|
}
|
|
|
|
if (gShowUserAgent)
|
|
{
|
|
var userAgentEntry = {name:"user-agent", outputFunction:updateHeaderValueInTextNode};
|
|
gExpandedHeaderView[userAgentEntry.name] = new createHeaderEntry('expanded', userAgentEntry);
|
|
}
|
|
|
|
}
|
|
|
|
function OnLoadMsgHeaderPane()
|
|
{
|
|
// HACK...force our XBL bindings file to be load before we try to create our first xbl widget....
|
|
// otherwise we have problems.
|
|
|
|
document.loadBindingDocument('chrome://messenger/content/mailWidgets.xml');
|
|
|
|
// load any preferences that at are global with regards to
|
|
// displaying a message...
|
|
gNumAddressesToShow = pref.GetIntPref("mailnews.max_header_display_length");
|
|
gCollectIncoming = pref.GetBoolPref("mail.collect_email_address_incoming");
|
|
gCollectNewsgroup = pref.GetBoolPref("mail.collect_email_address_newsgroup");
|
|
gShowUserAgent = pref.GetBoolPref("mailnews.headers.showUserAgent");
|
|
initializeHeaderViewTables();
|
|
|
|
var toggleHeaderView = document.getElementById("msgHeaderView");
|
|
var initialCollapsedSetting = toggleHeaderView.getAttribute("state");
|
|
if (initialCollapsedSetting == "true")
|
|
gCollapsedHeaderViewMode = true;
|
|
}
|
|
|
|
// The messageHeaderSink is the class that gets notified of a message's headers as we display the message
|
|
// through our mime converter.
|
|
|
|
var messageHeaderSink = {
|
|
onStartHeaders: function()
|
|
{
|
|
// every time we start to redisplay a message, check the view all headers pref....
|
|
var showAllHeadersPref = pref.GetIntPref("mail.show_headers");
|
|
if (showAllHeadersPref == 2)
|
|
gViewAllHeaders = true;
|
|
else
|
|
gViewAllHeaders = false;
|
|
|
|
ClearCurrentHeaders();
|
|
gGeneratedViewAllHeaderInfo = false;
|
|
gBuildAttachmentsForCurrentMsg = false;
|
|
gBuildAttachmentPopupForCurrentMsg = true;
|
|
ClearAttachmentTreeList();
|
|
ClearEditMessageButton();
|
|
},
|
|
|
|
onEndHeaders: function()
|
|
{
|
|
// WARNING: This is the ONLY routine inside of the message Header Sink that should
|
|
// trigger a reflow!
|
|
CheckNotify();
|
|
|
|
ClearHeaderView(gCollapsedHeaderView);
|
|
ClearHeaderView(gExpandedHeaderView);
|
|
|
|
EnsureSubjectValue(); // make sure there is a subject even if it's empty so we'll show the subject and the twisty
|
|
|
|
ShowMessageHeaderPane();
|
|
UpdateMessageHeaders();
|
|
if (gIsEditableMsgFolder)
|
|
ShowEditMessageButton();
|
|
},
|
|
|
|
handleHeader: function(headerName, headerValue, dontCollectAddress)
|
|
{
|
|
// WARNING: if you are modifying this function, make sure you do not do *ANY*
|
|
// dom manipulations which would trigger a reflow. This method gets called
|
|
// for every rfc822 header in the message being displayed. If you introduce a reflow
|
|
// you'll be introducing a reflow for every time we are told about a header. And msgs have
|
|
// a lot of headers. Don't do it =)....Move your code into OnEndHeaders which is only called
|
|
// once per message view.
|
|
|
|
// for consistancy sake, let's force all header names to be lower case so
|
|
// we don't have to worry about looking for: Cc and CC, etc.
|
|
var lowerCaseHeaderName = headerName.toLowerCase();
|
|
var foo = new Object;
|
|
foo.headerValue = headerValue;
|
|
foo.headerName = headerName;
|
|
|
|
// some times, you can have multiple To or cc lines....in this case, we want to APPEND
|
|
// these headers into one.
|
|
if ( (lowerCaseHeaderName == 'to' || lowerCaseHeaderName == 'cc') && ( lowerCaseHeaderName in currentHeaderData))
|
|
{
|
|
currentHeaderData[lowerCaseHeaderName].headerValue = currentHeaderData[lowerCaseHeaderName].headerValue + ',' + foo.headerValue;
|
|
}
|
|
else
|
|
currentHeaderData[lowerCaseHeaderName] = foo;
|
|
|
|
if (lowerCaseHeaderName == "from")
|
|
{
|
|
if (headerValue && abAddressCollector &&
|
|
((gCollectIncoming && !dontCollectAddress) || (gCollectNewsgroup && dontCollectAddress)))
|
|
abAddressCollector.collectUnicodeAddress(headerValue);
|
|
}
|
|
|
|
},
|
|
|
|
handleAttachment: function(contentType, url, displayName, uri, notDownloaded)
|
|
{
|
|
currentAttachments.push (new createNewAttachmentInfo(contentType, url, displayName, uri, notDownloaded));
|
|
// if we have an attachment, set the MSG_FLAG_ATTACH flag on the hdr
|
|
// this will cause the "message with attachment" icon to show up
|
|
// in the thread pane
|
|
// we only need to do this on the first attachment
|
|
var numAttachments = currentAttachments.length;
|
|
if (numAttachments == 1) {
|
|
try {
|
|
// convert the uri into a hdr
|
|
var hdr = messenger.messageServiceFromURI(uri).messageURIToMsgHdr(uri);
|
|
hdr.markHasAttachments(true);
|
|
}
|
|
catch (ex) {
|
|
dump("ex = " + ex + "\n");
|
|
}
|
|
}
|
|
},
|
|
|
|
onEndAllAttachments: function()
|
|
{
|
|
// AddSaveAllAttachmentsMenu();
|
|
if (gCollapsedHeaderViewMode)
|
|
displayAttachmentsForCollapsedView();
|
|
else
|
|
displayAttachmentsForExpandedView();
|
|
}
|
|
};
|
|
|
|
function EnsureSubjectValue()
|
|
{
|
|
if (!('subject' in currentHeaderData))
|
|
{
|
|
var foo = new Object;
|
|
foo.headerValue = "";
|
|
foo.headerName = 'subject';
|
|
currentHeaderData[foo.headerName] = foo;
|
|
}
|
|
}
|
|
|
|
function CheckNotify()
|
|
{
|
|
if (this.NotifyClearAddresses != undefined)
|
|
NotifyClearAddresses();
|
|
}
|
|
|
|
|
|
// flush out any local state being held by a header entry for a given
|
|
// table
|
|
function ClearHeaderView(headerTable)
|
|
{
|
|
for (index in headerTable)
|
|
{
|
|
var headerEntry = headerTable[index];
|
|
if (headerEntry.useToggle)
|
|
{
|
|
ClearEmailField(headerEntry.textNode);
|
|
if (headerEntry.longTextNode)
|
|
ClearEmailField(headerEntry.longTextNode);
|
|
}
|
|
|
|
headerEntry.valid = false;
|
|
}
|
|
}
|
|
|
|
// make sure that any valid header entry in the table is collapsed
|
|
function hideHeaderView(headerTable)
|
|
{
|
|
for (index in headerTable)
|
|
{
|
|
headerTable[index].enclosingBox.setAttribute('collapsed', true);
|
|
}
|
|
}
|
|
|
|
// make sure that any valid header entry in the table specified is
|
|
// visible
|
|
function showHeaderView(headerTable)
|
|
{
|
|
var headerEntry;
|
|
for (index in headerTable)
|
|
{
|
|
headerEntry = headerTable[index];
|
|
if (headerEntry.valid)
|
|
headerEntry.enclosingBox.removeAttribute('collapsed');
|
|
else // if the entry is invalid, always make sure it's collapsed
|
|
headerEntry.enclosingBox.setAttribute('collapsed', true);
|
|
}
|
|
}
|
|
|
|
// make sure the appropriate fields within the currently displayed view header mode
|
|
// are collapsed or visible...
|
|
function updateHeaderViews()
|
|
{
|
|
if (gCollapsedHeaderViewMode)
|
|
{
|
|
showHeaderView(gCollapsedHeaderView);
|
|
displayAttachmentsForCollapsedView();
|
|
}
|
|
else
|
|
{
|
|
showHeaderView(gExpandedHeaderView);
|
|
displayAttachmentsForExpandedView();
|
|
}
|
|
}
|
|
|
|
function ToggleHeaderView ()
|
|
{
|
|
var expandedNode = document.getElementById("expandedHeaderView");
|
|
var collapsedNode = document.getElementById("collapsedHeaderView");
|
|
var toggleHeaderView = document.getElementById("msgHeaderView");
|
|
|
|
if (gCollapsedHeaderViewMode)
|
|
{
|
|
gCollapsedHeaderViewMode = false;
|
|
// hide the current view
|
|
hideHeaderView(gCollapsedHeaderView);
|
|
// update the current view
|
|
updateHeaderViews();
|
|
|
|
// now uncollapse / collapse the right views
|
|
expandedNode.removeAttribute("collapsed");
|
|
collapsedNode.setAttribute("collapsed", "true");
|
|
}
|
|
else
|
|
{
|
|
gCollapsedHeaderViewMode = true;
|
|
// hide the current view
|
|
hideHeaderView(gExpandedHeaderView);
|
|
// update the current view
|
|
updateHeaderViews();
|
|
|
|
// now uncollapse / collapse the right views
|
|
collapsedNode.removeAttribute("collapsed");
|
|
expandedNode.setAttribute("collapsed", "true");
|
|
}
|
|
|
|
if (gCollapsedHeaderViewMode)
|
|
toggleHeaderView.setAttribute("state", "true");
|
|
else
|
|
toggleHeaderView.setAttribute("state", "false");
|
|
}
|
|
|
|
// Clear Email Field takes the passed in div and removes all the child nodes!
|
|
function ClearEmailField(parentDiv)
|
|
{
|
|
if (parentDiv)
|
|
{
|
|
while (parentDiv.childNodes.length > 0)
|
|
parentDiv.removeChild(parentDiv.childNodes[0]);
|
|
}
|
|
}
|
|
|
|
// default method for updating a header value into a header entry
|
|
function updateHeaderValue(headerEntry, headerValue)
|
|
{
|
|
headerEntry.textNode.childNodes[0].nodeValue = headerValue;
|
|
}
|
|
|
|
function updateHeaderValueInTextNode(headerEntry, headerValue)
|
|
{
|
|
headerEntry.textNode.setAttribute("value", headerValue);
|
|
}
|
|
|
|
// UpdateMessageHeaders: Iterate through all the current header data we received from mime for this message
|
|
// for each header entry table, see if we have a corresponding entry for that header. i.e. does the particular
|
|
// view care about this header value. if it does then call updateHeaderEntry
|
|
function UpdateMessageHeaders()
|
|
{
|
|
// iterate over each header we received and see if we have a matching entry in each
|
|
// header view table...
|
|
|
|
for (headerName in currentHeaderData)
|
|
{
|
|
var headerField = currentHeaderData[headerName];
|
|
var headerEntry = gExpandedHeaderView[headerName];
|
|
if (headerEntry != undefined && headerEntry)
|
|
{
|
|
headerEntry.outputFunction(headerEntry, headerField.headerValue);
|
|
headerEntry.valid = true;
|
|
}
|
|
headerEntry = gCollapsedHeaderView[headerName];
|
|
if (headerEntry != undefined && headerEntry)
|
|
{
|
|
headerEntry.outputFunction(headerEntry, headerField.headerValue);
|
|
headerEntry.valid = true;
|
|
}
|
|
}
|
|
|
|
// now update the view to make sure the right elements are visible
|
|
updateHeaderViews();
|
|
|
|
if (this.FinishEmailProcessing != undefined)
|
|
FinishEmailProcessing();
|
|
}
|
|
|
|
function ClearCurrentHeaders()
|
|
{
|
|
currentHeaderData = {};
|
|
currentAttachments = new Array();
|
|
}
|
|
|
|
function ShowMessageHeaderPane()
|
|
{
|
|
var node;
|
|
if (gCollapsedHeaderViewMode)
|
|
{
|
|
node = document.getElementById("collapsedHeaderView");
|
|
if (node)
|
|
node.removeAttribute("collapsed");
|
|
}
|
|
else
|
|
{
|
|
node = document.getElementById("expandedHeaderView");
|
|
if (node)
|
|
node.removeAttribute("collapsed");
|
|
}
|
|
|
|
/* workaround for 39655 */
|
|
if (gFolderJustSwitched)
|
|
{
|
|
var el = document.getElementById("msgHeaderView");
|
|
el.setAttribute("style", el.getAttribute("style"));
|
|
gFolderJustSwitched = false;
|
|
}
|
|
}
|
|
|
|
function HideMessageHeaderPane()
|
|
{
|
|
var node = document.getElementById("collapsedHeaderView");
|
|
if (node)
|
|
node.setAttribute("collapsed", "true");
|
|
|
|
node = document.getElementById("expandedHeaderView");
|
|
if (node)
|
|
node.setAttribute("collapsed", "true");
|
|
}
|
|
|
|
function OutputNewsgroups(headerEntry, headerValue)
|
|
{
|
|
headerValue = headerValue.replace(/,/g,", ");
|
|
updateHeaderValue(headerEntry, headerValue);
|
|
}
|
|
|
|
// OutputEmailAddresses --> knows how to take a comma separated list of email addresses,
|
|
// extracts them one by one, linkifying each email address into a mailto url.
|
|
// Then we add the link'ified email address to the parentDiv passed in.
|
|
//
|
|
// defaultParentDiv --> the div to add the link-ified email addresses into.
|
|
// emailAddresses --> comma separated list of the addresses for this header field
|
|
// includeShortLongToggle --> true if you want to include the ability to toggle between short/long
|
|
// address views for this header field. If true, then pass in a another div which is the div the long
|
|
// view will be added too...
|
|
// useShortView --> if true, we'll only generate the Name of the email address field instead of
|
|
// showing the name + the email address.
|
|
|
|
function OutputEmailAddresses(headerEntry, emailAddresses)
|
|
{
|
|
if ( !emailAddresses ) return;
|
|
|
|
if (msgHeaderParser)
|
|
{
|
|
// Count the number of email addresses being inserted into each header
|
|
// The headers could be "to", "cc", "from"
|
|
var myEnum = msgHeaderParser.ParseHeadersWithEnumerator(emailAddresses);
|
|
myEnum = myEnum.QueryInterface(Components.interfaces.nsISimpleEnumerator);
|
|
|
|
numOfEmailsInEnumerator = 0;
|
|
while (myEnum.hasMoreElements())
|
|
{
|
|
myEnum.getNext();
|
|
numOfEmailsInEnumerator++;
|
|
}
|
|
|
|
var enumerator = msgHeaderParser.ParseHeadersWithEnumerator(emailAddresses);
|
|
enumerator = enumerator.QueryInterface(Components.interfaces.nsISimpleEnumerator);
|
|
var numAddressesParsed = 0;
|
|
if (enumerator)
|
|
{
|
|
var emailAddress = {};
|
|
var name = {};
|
|
|
|
while (enumerator.hasMoreElements())
|
|
{
|
|
var headerResult = enumerator.getNext();
|
|
headerResult = enumerator.QueryInterface(Components.interfaces.nsIMsgHeaderParserResult);
|
|
|
|
// get the email and name fields
|
|
var addrValue = {};
|
|
var nameValue = {};
|
|
var fullAddress = headerResult.getAddressAndName(addrValue, nameValue);
|
|
emailAddress = addrValue.value;
|
|
name = nameValue.value;
|
|
|
|
// if we want to include short/long toggle views and we have a long view, always add it.
|
|
// if we aren't including a short/long view OR if we are and we haven't parsed enough
|
|
// addresses to reach the cutoff valve yet then add it to the default (short) div.
|
|
if (headerEntry.useToggle && headerEntry.longTextNode)
|
|
{
|
|
InsertEmailAddressUnderEnclosingBox(headerEntry, headerEntry.longTextNode, emailAddress, fullAddress, name);
|
|
}
|
|
if (!headerEntry.useToggle || (numAddressesParsed < gNumAddressesToShow))
|
|
{
|
|
InsertEmailAddressUnderEnclosingBox(headerEntry, headerEntry.textNode, emailAddress, fullAddress, name);
|
|
}
|
|
|
|
numAddressesParsed++;
|
|
}
|
|
} // if enumerator
|
|
|
|
if (headerEntry.useToggle && headerEntry.toggleIcon)
|
|
{
|
|
if (numAddressesParsed > gNumAddressesToShow) // make sure the icon is always visible if we have more than the # of addresses to show
|
|
headerEntry.toggleIcon.removeAttribute('collapsed');
|
|
else
|
|
headerEntry.toggleIcon.setAttribute('collapsed', true);
|
|
}
|
|
} // if msgheader parser
|
|
}
|
|
|
|
function updateEmailAddressNode(emailAddressNode, emailAddress, fullAddress, displayName, useShortView)
|
|
{
|
|
if (useShortView && displayName)
|
|
emailAddressNode.setAttribute("label", displayName);
|
|
else
|
|
emailAddressNode.setAttribute("label", fullAddress);
|
|
emailAddressNode.setTextAttribute("emailAddress", emailAddress);
|
|
emailAddressNode.setTextAttribute("fullAddress", fullAddress);
|
|
emailAddressNode.setTextAttribute("displayName", displayName);
|
|
|
|
if (this.AddExtraAddressProcessing != undefined)
|
|
AddExtraAddressProcessing(emailAddress, emailAddressNode);
|
|
}
|
|
|
|
/* InsertEmailAddressUnderEnclosingBox --> right now all email addresses are borderless titled buttons
|
|
with formatting to make them look like html anchors. When you click on the button,
|
|
you are prompted with a popup asking you what you want to do with the email address
|
|
useShortView --> only show the name (if present) instead of the name + email address
|
|
*/
|
|
|
|
function InsertEmailAddressUnderEnclosingBox(headerEntry, parentNode,
|
|
emailAddress, fullAddress, displayName)
|
|
{
|
|
var itemInDocument = parentNode;
|
|
// if this header uses a toggle then it can contain multiple email addresses. In this case,
|
|
// parentNode is really an HTML div. we need to create a mail-emailaddress element and insert it into
|
|
// the html div.
|
|
if ( headerEntry.useToggle)
|
|
{
|
|
var item = document.createElement("mail-emailaddress");
|
|
if (item && parentNode)
|
|
{
|
|
if (parentNode.childNodes.length >= 1)
|
|
{
|
|
var textNode = document.createElement("text");
|
|
textNode.setAttribute("value", ", ");
|
|
textNode.setAttribute("class", "emailSeparator");
|
|
parentNode.appendChild(textNode);
|
|
}
|
|
|
|
itemInDocument = parentNode.appendChild(item);
|
|
}
|
|
}
|
|
|
|
updateEmailAddressNode(itemInDocument, emailAddress, fullAddress, displayName, headerEntry.useShortView);
|
|
}
|
|
|
|
// ToggleLongShortAddresses is used to toggle between showing
|
|
// all of the addresses on a given header line vs. only the first 'n'
|
|
// where 'n' is a user controlled preference. By toggling on the more/less
|
|
// images in the header window, we'll hide / show the appropriate div for that header.
|
|
|
|
function ToggleLongShortAddresses(imageID, shortDivID, longDivID)
|
|
{
|
|
var shortNode = document.getElementById(shortDivID);
|
|
var longNode = document.getElementById(longDivID);
|
|
var imageNode = document.getElementById(imageID);
|
|
|
|
var nodeToReset;
|
|
|
|
// test to see which if short is already hidden...
|
|
if (shortNode.getAttribute("collapsed") == "true")
|
|
{
|
|
longNode.setAttribute("collapsed", true);
|
|
shortNode.removeAttribute("collapsed");
|
|
imageNode.setAttribute("class", 'showMoreAddressesButton');
|
|
nodeToReset = shortNode;
|
|
}
|
|
else
|
|
{
|
|
shortNode.setAttribute("collapsed", true);
|
|
longNode.removeAttribute("collapsed");
|
|
imageNode.setAttribute("class", 'showFewerAddressesButton');
|
|
nodeToReset = longNode;
|
|
}
|
|
}
|
|
|
|
function AddNodeToAddressBook (emailAddressNode)
|
|
{
|
|
if (emailAddressNode)
|
|
{
|
|
var primaryEmail = emailAddressNode.getAttribute("emailAddress");
|
|
var displayName = emailAddressNode.getAttribute("displayName");
|
|
window.openDialog("chrome://messenger/content/addressbook/abNewCardDialog.xul",
|
|
"",
|
|
"chrome,titlebar,resizeable=no",
|
|
{primaryEmail:primaryEmail, displayName:displayName });
|
|
}
|
|
}
|
|
|
|
// SendMailToNode takes the email address title button, extracts
|
|
// the email address we stored in there and opens a compose window
|
|
// with that address
|
|
function SendMailToNode(emailAddressNode)
|
|
{
|
|
if (emailAddressNode)
|
|
{
|
|
var emailAddress = emailAddressNode.getAttribute("emailAddress");
|
|
if (emailAddress)
|
|
messenger.OpenURL("mailto:" + emailAddress );
|
|
}
|
|
}
|
|
|
|
// CopyEmailAddress takes the email address title button, extracts
|
|
// the email address we stored in there and copies it to the clipboard
|
|
function CopyEmailAddress(emailAddressNode)
|
|
{
|
|
if (emailAddressNode)
|
|
{
|
|
var emailAddress = emailAddressNode.getAttribute("emailAddress");
|
|
|
|
var contractid = "@mozilla.org/widget/clipboardhelper;1";
|
|
var iid = Components.interfaces.nsIClipboardHelper;
|
|
var clipboard = Components.classes[contractid].getService(iid);
|
|
clipboard.copyString(emailAddress);
|
|
}
|
|
}
|
|
|
|
// createnewAttachmentInfo --> constructor method for creating new attachment object which goes into the
|
|
// data attachment array.
|
|
function createNewAttachmentInfo(contentType, url, displayName, uri, notDownloaded)
|
|
{
|
|
this.contentType = contentType;
|
|
this.url = url;
|
|
this.displayName = displayName;
|
|
this.uri = uri;
|
|
this.notDownloaded = notDownloaded;
|
|
}
|
|
|
|
function saveAttachment(contentType, url, displayName, messageUri)
|
|
{
|
|
messenger.saveAttachment(contentType, url, displayName, messageUri);
|
|
}
|
|
|
|
function openAttachment(contentType, url, displayName, messageUri)
|
|
{
|
|
messenger.openAttachment(contentType, url, displayName, messageUri);
|
|
}
|
|
|
|
function printAttachmentAttachment(contentType, url, displayName, messageUri)
|
|
{
|
|
// we haven't implemented the ability to print attachments yet...
|
|
// messenger.printAttachment(contentType, url, displayName, messageUri);
|
|
}
|
|
|
|
// this is our onclick handler for the attachment tree.
|
|
// A double click in a tree cell simulates "opening" the attachment....
|
|
function attachmentTreeClick(event)
|
|
{
|
|
if(event.detail == 2) // double click
|
|
{
|
|
var target = event.originalTarget;
|
|
item = target.parentNode.parentNode;
|
|
if (item.localName == "treeitem")
|
|
{
|
|
var commandStringSuffix = item.getAttribute("commandSuffix");
|
|
var openString = 'openAttachment' + commandStringSuffix;
|
|
eval(openString);
|
|
}
|
|
}
|
|
}
|
|
|
|
// on command handlers for the attachment tree context menu...
|
|
// commandPrefix matches one of our existing functions (openAttachment, saveAttachment, etc.) which we'll add to the command suffix
|
|
// found on the tree item....
|
|
function handleAttachmentSelection(commandPrefix)
|
|
{
|
|
// get the selected attachment...and call openAttachment on it...
|
|
var attachmentTree = document.getElementById('attachmentTree');
|
|
var selectedAttachments = attachmentTree.selectedItems;
|
|
var treeItem = selectedAttachments[0];
|
|
var commandStringSuffix = treeItem.getAttribute("commandSuffix");
|
|
var openString = commandPrefix + commandStringSuffix;
|
|
eval(openString);
|
|
}
|
|
|
|
function generateCommandSuffixForAttachment(attachment)
|
|
{
|
|
return "('" + attachment.contentType + "', '" + attachment.url + "', '" + escape(attachment.displayName) + "', '" + attachment.uri + "')";
|
|
}
|
|
|
|
function displayAttachmentsForExpandedView()
|
|
{
|
|
var numAttachments = currentAttachments.length;
|
|
if (numAttachments > 0 && !gBuildAttachmentsForCurrentMsg)
|
|
{
|
|
var attachmentList = document.getElementById('attachmentsBody');
|
|
var row, cell, item;
|
|
for (index in currentAttachments)
|
|
{
|
|
var attachment = currentAttachments[index];
|
|
// we need to create a tree item, a tree row and a tree cell to insert the attachment
|
|
// into the attachment tree..
|
|
|
|
item = document.createElement("treeitem");
|
|
row = document.createElement("treerow");
|
|
cell = document.createElement("treecell");
|
|
|
|
cell.setAttribute('class', "treecell-iconic");
|
|
cell.setAttribute("label", attachment.displayName);
|
|
item.setAttribute("commandSuffix", generateCommandSuffixForAttachment(attachment)); // set the command suffix on the tree item...
|
|
setApplicationIconForAttachment(attachment, cell);
|
|
row.appendChild(cell);
|
|
item.appendChild(row);
|
|
attachmentList.appendChild(item);
|
|
} // for each attachment
|
|
gBuildAttachmentsForCurrentMsg = true;
|
|
}
|
|
|
|
var attachmentNode = document.getElementById('expandedAttachmentBox');
|
|
if (numAttachments > 0) // make sure the attachment button is visible
|
|
attachmentNode.removeAttribute('collapsed');
|
|
else
|
|
attachmentNode.setAttribute('collapsed', true);
|
|
}
|
|
|
|
// attachment --> the attachment struct containing all the information on the attachment
|
|
// treeCell --> the tree cell currently showing the attachment.
|
|
function setApplicationIconForAttachment(attachment, treeCell)
|
|
{
|
|
// generate a moz-icon url for the attachment so we'll show a nice icon next to it.
|
|
treeCell.setAttribute('src', "moz-icon:" + "//" + attachment.displayName + "?size=16&contentType=" + attachment.contentType);
|
|
}
|
|
|
|
function displayAttachmentsForCollapsedView()
|
|
{
|
|
var numAttachments = currentAttachments.length;
|
|
var attachmentNode = document.getElementById('collapsedAttachmentBox');
|
|
if (numAttachments > 0) // make sure the attachment button is visible
|
|
{
|
|
attachmentNode.removeAttribute('hide');
|
|
}
|
|
else
|
|
{
|
|
attachmentNode.setAttribute('hide', true);
|
|
}
|
|
}
|
|
|
|
// Public method called when we create the attachments file menu
|
|
function FillAttachmentListPopup(popup)
|
|
{
|
|
// the FE sometimes call this routie TWICE...I haven't been able to figure out why yet...
|
|
// protect against it...
|
|
|
|
if (!gBuildAttachmentPopupForCurrentMsg) return;
|
|
|
|
// otherwise we need to build the attachment view...
|
|
// First clear out the old view...
|
|
ClearAttachmentMenu(popup);
|
|
|
|
|
|
for (index in currentAttachments)
|
|
{
|
|
addAttachmentToPopup(popup, currentAttachments[index]);
|
|
}
|
|
|
|
gBuildAttachmentPopupForCurrentMsg = false;
|
|
|
|
}
|
|
|
|
// Public method used to clear the file attachment menu
|
|
function ClearAttachmentMenu(popup)
|
|
{
|
|
if ( popup )
|
|
{
|
|
while ( popup.childNodes.length > 2 )
|
|
popup.removeChild(popup.childNodes[0]);
|
|
}
|
|
}
|
|
|
|
// Public method used to determine the number of attachments for the currently displayed message...
|
|
function GetNumberOfAttachmentsForDisplayedMessage()
|
|
{
|
|
return currentAttachments.length;
|
|
}
|
|
|
|
// private method used to build up a menu list of attachments
|
|
function addAttachmentToPopup(popup, attachment)
|
|
{
|
|
if (popup)
|
|
{
|
|
var item = document.createElement('menu');
|
|
if ( item )
|
|
{
|
|
// insert the item just before the separator...the separator is the 2nd to last element in the popup.
|
|
item.setAttribute('class', 'menu-iconic');
|
|
setApplicationIconForAttachment(attachment,item);
|
|
var numItemsInPopup = popup.childNodes.length;
|
|
item = popup.insertBefore(item, popup.childNodes[numItemsInPopup-2]);
|
|
item.setAttribute('label', attachment.displayName);
|
|
var oncommandPrefix = generateCommandSuffixForAttachment(attachment);
|
|
|
|
var openpopup = document.createElement('menupopup');
|
|
openpopup = item.appendChild(openpopup);
|
|
|
|
var menuitementry = document.createElement('menuitem');
|
|
menuitementry.setAttribute('oncommand', 'openAttachment' + oncommandPrefix);
|
|
menuitementry.setAttribute('label', "Open");
|
|
menuitementry = openpopup.appendChild(menuitementry);
|
|
|
|
var menuseparator = document.createElement('menuseparator');
|
|
openpopup.appendChild(menuseparator);
|
|
|
|
menuitementry = document.createElement('menuitem');
|
|
menuitementry.setAttribute('oncommand', 'saveAttachment' + oncommandPrefix);
|
|
menuitementry.setAttribute('label', "Save");
|
|
menuitementry = openpopup.appendChild(menuitementry);
|
|
} // if we created a menu item for this attachment...
|
|
} // if we have a popup
|
|
}
|
|
|
|
function SaveAllAttachments()
|
|
{
|
|
try
|
|
{
|
|
// convert our attachment data into some c++ friendly structs
|
|
var attachmentContentTypeArray = new Array();
|
|
var attachmentUrlArray = new Array();
|
|
var attachmentDisplayNameArray = new Array();
|
|
var attachmentMessageUriArray = new Array();
|
|
|
|
// populate these arrays..
|
|
for (index in currentAttachments)
|
|
{
|
|
var attachment = currentAttachments[index];
|
|
attachmentContentTypeArray[index] = attachment.contentType;
|
|
attachmentUrlArray[index] = attachment.url;
|
|
attachmentDisplayNameArray[index] = escape(attachment.displayName);
|
|
attachmentMessageUriArray[index] = attachment.uri;
|
|
}
|
|
|
|
// okay the list has been built...now call our save all attachments code...
|
|
messenger.saveAllAttachments(attachmentContentTypeArray.length,
|
|
attachmentContentTypeArray, attachmentUrlArray,
|
|
attachmentDisplayNameArray, attachmentMessageUriArray);
|
|
}
|
|
catch (ex)
|
|
{
|
|
dump ("** failed to save all attachments **\n");
|
|
}
|
|
}
|
|
|
|
function ClearAttachmentTreeList()
|
|
{
|
|
var attachmentTreebody = document.getElementById("attachmentsBody");
|
|
if ( attachmentTreebody )
|
|
{
|
|
while ( attachmentTreebody.childNodes.length )
|
|
attachmentTreebody.removeChild(attachmentTreebody.childNodes[0]);
|
|
}
|
|
}
|
|
|
|
function ShowEditMessageButton()
|
|
{
|
|
var editBox = document.getElementById("editMessageBox");
|
|
if (editBox)
|
|
editBox.removeAttribute("collapsed");
|
|
}
|
|
|
|
function ClearEditMessageButton()
|
|
{
|
|
var editBox = document.getElementById("editMessageBox");
|
|
if (editBox)
|
|
editBox.setAttribute("collapsed", "true");
|
|
}
|
|
|
|
// given a box, iterate through all of the headers and add them to
|
|
// the enclosing box.
|
|
function fillBoxWithAllHeaders(containerBox, boxPartOfPopup)
|
|
{
|
|
// clear out the old popup date if there is any...
|
|
while ( containerBox.childNodes.length )
|
|
containerBox.removeChild(containerBox.childNodes[0]);
|
|
|
|
for (header in currentHeaderData)
|
|
{
|
|
var innerBox = document.createElement('box');
|
|
innerBox.setAttribute("class", "headerBox");
|
|
innerBox.setAttribute("orient", "horizontal");
|
|
if (boxPartOfPopup)
|
|
innerBox.setAttribute("autostretch", "never");
|
|
|
|
// for each header, create a header value and header name then assign those values...
|
|
var headerValueBox = document.createElement('hbox');
|
|
headerValueBox.setAttribute("class", "headerValueBox");
|
|
|
|
var newHeaderTitle = document.createElement('text');
|
|
newHeaderTitle.setAttribute("class", "headerdisplayname");
|
|
newHeaderTitle.setAttribute("value", currentHeaderData[header].headerName + ':');
|
|
headerValueBox.appendChild(newHeaderTitle);
|
|
|
|
innerBox.appendChild(headerValueBox);
|
|
|
|
var newHeaderValue = document.createElement('html');
|
|
// make sure we are properly resized...
|
|
// if (boxPartOfPopup)
|
|
// newHeaderValue.setAttribute("width", window.innerWidth*.65);
|
|
newHeaderValue.setAttribute("class", "headerValue");
|
|
if (!boxPartOfPopup)
|
|
newHeaderValue.setAttribute("flex", "1");
|
|
innerBox.appendChild(newHeaderValue);
|
|
containerBox.appendChild(innerBox);
|
|
ProcessHeaderValue(innerBox, newHeaderValue, currentHeaderData[header], boxPartOfPopup);
|
|
}
|
|
}
|
|
|
|
// the on create handler for the view all headers popup...
|
|
function fillAllHeadersPopup(node)
|
|
{
|
|
// don't bother re-filling the popup if we've already done it for the
|
|
// currently displayed message....
|
|
if (gGeneratedViewAllHeaderInfo == true)
|
|
return true;
|
|
|
|
var containerBox = document.getElementById('allHeadersPopupContainer');
|
|
|
|
containerBox.setAttribute("class", "header-part1");
|
|
containerBox.setAttribute("align", "vertical");
|
|
containerBox.setAttribute("flex", "1");
|
|
|
|
fillBoxWithAllHeaders(containerBox, true);
|
|
gGeneratedViewAllHeaderInfo = true; // don't try to regenerate this information for the currently displayed message
|
|
return true;
|
|
}
|
|
|
|
// containingBox --> the box containing the header
|
|
// containerNode --> the div or box that we want to insert this specific header into
|
|
// header --> an entry from currentHeaderData contains .headerName and .headerValue
|
|
// boxPartOfPopup --> true if the box is part of a popup (certain functions are disabled in this scenario)
|
|
function ProcessHeaderValue(containingBox, containerNode, header, boxPartOfPopup)
|
|
{
|
|
// in the simplest case, we'll just create a text node for the header
|
|
// and append it to the container Node. for certain headers, we might want to do
|
|
// extra processing....
|
|
|
|
var headerName = header.headerName;
|
|
headerName = headerName.toLowerCase();
|
|
if (!boxPartOfPopup && (headerName == "cc" || headerName == "from" || headerName == "to" || headerName == "reply-to"))
|
|
{
|
|
OutputEmailAddresses(containingBox, containerNode, header.headerValue, false, "", "", headerName)
|
|
return;
|
|
}
|
|
|
|
var headerValue = header.headerValue;
|
|
headerValue = headerValue.replace(/\n|\r/g,"");
|
|
var textNode = document.createTextNode(headerValue);
|
|
if (headerName == "subject")
|
|
{
|
|
containerNode.setAttribute("class", "subjectvalue headerValue");
|
|
}
|
|
|
|
containerNode.appendChild(textNode);
|
|
}
|