home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 January / 01_02.iso / software / netscape62win / mail.xpi / bin / chrome / messenger.jar / content / messenger / msgHdrViewOverlay.js < prev    next >
Text File  |  2001-08-09  |  42KB  |  1,070 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  * The contents of this file are subject to the Netscape Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/NPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is Mozilla Communicator client code, released
  13.  * March 31, 1998.
  14.  * 
  15.  * The Initial Developer of the Original Code is Netscape
  16.  * Communications Corporation. Portions created by Netscape are
  17.  * Copyright (C) 1998-1999 Netscape Communications Corporation. All
  18.  * Rights Reserved.
  19.  */
  20.  
  21. /* This is where functions related to displaying the headers for a selected message in the
  22.    message pane live. */
  23.  
  24. ////////////////////////////////////////////////////////////////////////////////////
  25. // Warning: if you go to modify any of these JS routines please get a code review from either
  26. // hangas@netscape.com or mscott@netscape.com. It's critical that the code in here for displaying
  27. // the message headers for a selected message remain as fast as possible. In particular, 
  28. // right now, we only introduce one reflow per message. i.e. if you click on a message in the thread
  29. // pane, we batch up all the changes for displaying the header pane (to, cc, attachements button, etc.) 
  30. // and we make a single pass to display them. It's critical that we maintain this one reflow per message
  31. // view in the message header pane. 
  32. //
  33. ////////////////////////////////////////////////////////////////////////////////////
  34.  
  35. var msgHeaderParserContractID           = "@mozilla.org/messenger/headerparser;1";
  36. var abAddressCollectorContractID     = "@mozilla.org/addressbook/services/addressCollecter;1";
  37.  
  38. // gGeneratedViewAllHeaderInfo --> we clear this every time we start to display a new message.
  39. // the view all header popup will set it when we first generate a view of all the headers. this is 
  40. // just so it won't try to regenerate all the information every time the user clicks on the popup.
  41. var gGeneratedViewAllHeaderInfo = false; 
  42. var gViewAllHeaders = false;
  43. var gNumAddressesToShow = 3;
  44. var gShowUserAgent = false;
  45. var gCollectIncoming = false;
  46. var gCollectNewsgroup = false;
  47. var gCollapsedHeaderViewMode = false;
  48. var gBuildAttachmentsForCurrentMsg = false;
  49. var gBuildAttachmentPopupForCurrentMsg = true;
  50. var gOpenLabel;
  51. var gSaveLabel;
  52.  
  53. var msgHeaderParser = Components.classes[msgHeaderParserContractID].getService(Components.interfaces.nsIMsgHeaderParser);
  54. var abAddressCollector = Components.classes[abAddressCollectorContractID].getService(Components.interfaces.nsIAbAddressCollecter);
  55.  
  56. // All these variables are introduced to keep track of insertion and deletion of the toggle button either
  57. // as the first node in the list of emails or the last node.
  58. var numOfEmailsInEnumerator;
  59.  
  60. // var used to determine whether to show the toggle button at the
  61. // beginning or at the end of a list of emails in to/cc fields.
  62. var gNumOfEmailsToShowToggleButtonInFront = 15;
  63.  
  64. // For every possible "view" in the message pane, you need to define the header names you want to
  65. // see in that view. In addition, include information describing how you want that header field to be
  66. // presented. i.e. if it's an email address field, if you want a toggle inserted on the node in case
  67. // of multiple email addresses, etc. We'll then use this static table to dynamically generate header view entries
  68. // which manipulate the UI. 
  69. // When you add a header to one of these view lists you can specify the following properties:
  70. // name: the name of the header. i.e. "to", "subject". This must be in lower case and the name of the
  71. //       header is used to help dynamically generate ids for objects in the document. (REQUIRED)
  72. // useToggle:      true if the values for this header are multiple email addresses and you want a 
  73. //                 a toggle icon to show a short vs. long list (DEFAULT: false)
  74. // useShortView:   (only works on some fields like From). If the field has a long presentation and a
  75. //                 short presentation we'll use the short one. i.e. if you are showing the From field and you
  76. //                 set this to true, we can show just "John Doe" instead of "John Doe <jdoe@netscape.net>".
  77. //                 (DEFAULT: false)
  78. // 
  79. // outputFunction: this is a method which takes a headerEntry (see the definition below) and a header value
  80. //                 This allows you to provide your own methods for actually determining how the header value
  81. //                 is displayed. (DEFAULT: updateHeaderValue which just sets the header value on the text node)
  82.  
  83. // Our first view is the collapsed view. This is very light weight view of the data. We only show a couple
  84. // fields.
  85. var gCollapsedHeaderList = [ {name:"subject", outputFunction:updateHeaderValueInTextNode},
  86.                              {name:"from", useShortView:true, outputFunction:OutputEmailAddresses},
  87.                              {name:"date", outputFunction:updateHeaderValueInTextNode}];
  88.  
  89. // We also have an expanded header view. This shows many of your more common (and useful) headers.
  90. var gExpandedHeaderList = [ {name:"subject"}, 
  91.                             {name:"from", outputFunction:OutputEmailAddresses},
  92.                             {name:"reply-to", isEmailAddress:true, outputFunction:OutputEmailAddresses},
  93.                             {name:"date"},
  94.                             {name:"to", useToggle:true, outputFunction:OutputEmailAddresses},
  95.                             {name:"cc", useToggle:true, outputFunction:OutputEmailAddresses},
  96.                             {name:"bcc", useToggle:true, outputFunction:OutputEmailAddresses},
  97.                             {name:"newsgroups", outputFunction:OutputNewsgroups},
  98.                             {name:"followup-to", outputFunction:OutputNewsgroups} ];
  99.  
  100. // Now, for each view the message pane can generate, we need a global table of headerEntries. These
  101. // header entry objects are generated dynamically based on the static date in the header lists (see above)
  102. // and elements we find in the DOM based on properties in the header lists. 
  103. var gCollapsedHeaderView = {};
  104. var gExpandedHeaderView = {};
  105.  
  106. // currentHeaderData --> this is an array of header name and value pairs for the currently displayed message.
  107. //                       it's purely a data object and has no view information. View information is contained in the view objects.
  108. //                       for a given entry in this array you can ask for:
  109. // .headerName ---> name of the header (i.e. 'to'). Always stored in lower case
  110. // .headerValue --> value of the header "johndoe@netscape.net"
  111. var currentHeaderData = {};
  112.  
  113. // For the currently displayed message, we store all the attachment data. When displaying a particular
  114. // view, it's up to the view layer to extract this attachment data and turn it into something useful.
  115. // For a given entry in the attachments list, you can ask for the following properties:
  116. // .contentType --> the content type of the attachment
  117. // url --> an imap, or mailbox url which can be used to fetch the message
  118. // uri --> an RDF URI which refers to the message containig the attachment
  119. // notDownloaded --> boolean flag stating whether the attachment is downloaded or not.
  120. var currentAttachments = new Array();
  121.  
  122. // createHeaderEntry --> our constructor method which creates a header Entry 
  123. // based on an entry in one of the header lists. A header entry is different from a header list.
  124. // a header list just describes how you want a particular header to be presented. The header entry
  125. // actually has knowledge about the DOM and the actual DOM elements associated with the header.
  126. // prefix --> the name of the view (i.e. "collapsed", "expanded")
  127. // headerListInfo --> entry from a header list.
  128. function createHeaderEntry(prefix, headerListInfo)
  129. {
  130.   var partialIDName = prefix + headerListInfo.name;
  131.   //dump('enclosingBox id = ' + partialIDName + 'Box' + '\n');
  132.   this.enclosingBox = document.getElementById(partialIDName + 'Box');
  133.   //dump(this.enclosingBox + \'n');
  134.   this.textNode = document.getElementById(partialIDName + 'Value');
  135.   this.isValid = false;
  136.  
  137.   if ("useToggle" in headerListInfo)
  138.   {
  139.     this.useToggle = headerListInfo.useToggle;
  140.     if (this.useToggle) // find the toggle icon in the document
  141.     {
  142.       this.toggleIcon = document.getElementById(partialIDName + 'ToggleIcon');
  143.       this.longTextNode = document.getElementById(partialIDName + 'Value' + 'Long');
  144.     }
  145.   }
  146.   else
  147.    this.useToggle = false;
  148.  
  149.   if ("useShortView" in headerListInfo)
  150.     this.useShortView = headerListInfo.useShortView;
  151.   else
  152.     this.useShortView = false;
  153.  
  154.   if ("outputFunction" in headerListInfo)
  155.     this.outputFunction = headerListInfo.outputFunction;
  156.   else
  157.     this.outputFunction = updateHeaderValue;
  158. }
  159.  
  160. function initializeHeaderViewTables()
  161. {
  162.   // iterate over each header in our header list arrays and create header entries 
  163.   // for each one. These header entries are then stored in the appropriate header table
  164.   for (var index = 0; index < gCollapsedHeaderList.length; index++)
  165.   {
  166.     gCollapsedHeaderView[gCollapsedHeaderList[index].name] = 
  167.       new createHeaderEntry('collapsed', gCollapsedHeaderList[index]);
  168.   }
  169.  
  170.   for (index = 0; index < gExpandedHeaderList.length; index++)
  171.   {
  172.     var headerName = gExpandedHeaderList[index].name;
  173.     gExpandedHeaderView[headerName] = new createHeaderEntry('expanded', gExpandedHeaderList[index]);
  174.   }
  175.  
  176.   if (gShowUserAgent)
  177.   {
  178.     var userAgentEntry = {name:"user-agent", outputFunction:updateHeaderValueInTextNode};
  179.     gExpandedHeaderView[userAgentEntry.name] = new createHeaderEntry('expanded', userAgentEntry);
  180.   }
  181.  
  182. }
  183.  
  184. function OnLoadMsgHeaderPane()
  185. {
  186.   // HACK...force our XBL bindings file to be load before we try to create our first xbl widget....
  187.   // otherwise we have problems.
  188.  
  189.   document.loadBindingDocument('chrome://messenger/content/mailWidgets.xml');
  190.   
  191.   // load any preferences that at are global with regards to 
  192.   // displaying a message...
  193.   gNumAddressesToShow = pref.GetIntPref("mailnews.max_header_display_length");
  194.   gCollectIncoming = pref.GetBoolPref("mail.collect_email_address_incoming");
  195.   gCollectNewsgroup = pref.GetBoolPref("mail.collect_email_address_newsgroup");
  196.   gShowUserAgent = pref.GetBoolPref("mailnews.headers.showUserAgent");
  197.   initializeHeaderViewTables();
  198.  
  199.   var toggleHeaderView = document.getElementById("msgHeaderView");
  200.   var initialCollapsedSetting = toggleHeaderView.getAttribute("state");
  201.   if (initialCollapsedSetting == "true")
  202.     gCollapsedHeaderViewMode = true;   
  203. }
  204.  
  205. // The messageHeaderSink is the class that gets notified of a message's headers as we display the message
  206. // through our mime converter. 
  207.  
  208. var messageHeaderSink = {
  209.     onStartHeaders: function()
  210.     {
  211.       // every time we start to redisplay a message, check the view all headers pref....
  212.       var showAllHeadersPref = pref.GetIntPref("mail.show_headers");
  213.       if (showAllHeadersPref == 2)
  214.         gViewAllHeaders = true;
  215.       else
  216.         gViewAllHeaders = false;
  217.  
  218.       ClearCurrentHeaders();
  219.       gGeneratedViewAllHeaderInfo = false;
  220.       gBuildAttachmentsForCurrentMsg = false;
  221.       gBuildAttachmentPopupForCurrentMsg = true;
  222.       ClearAttachmentTreeList();
  223.       ClearEditMessageButton();
  224.     },
  225.  
  226.     onEndHeaders: function() 
  227.     {
  228.       // WARNING: This is the ONLY routine inside of the message Header Sink that should 
  229.       // trigger a reflow!
  230.       CheckNotify();
  231.       
  232.       ClearHeaderView(gCollapsedHeaderView);
  233.       ClearHeaderView(gExpandedHeaderView);
  234.  
  235.       EnsureSubjectValue(); // make sure there is a subject even if it's empty so we'll show the subject and the twisty
  236.       
  237.       ShowMessageHeaderPane();
  238.       UpdateMessageHeaders();
  239.       if (gIsEditableMsgFolder)
  240.         ShowEditMessageButton();
  241.     },
  242.  
  243.     handleHeader: function(headerName, headerValue, dontCollectAddress) 
  244.     {
  245.       // WARNING: if you are modifying this function, make sure you do not do *ANY*
  246.       // dom manipulations which would trigger a reflow. This method gets called 
  247.       // for every rfc822 header in the message being displayed. If you introduce a reflow
  248.       // you'll be introducing a reflow for every time we are told about a header. And msgs have
  249.       // a lot of headers. Don't do it =)....Move your code into OnEndHeaders which is only called
  250.       // once per message view.
  251.  
  252.       // for consistancy sake, let's force all header names to be lower case so
  253.       // we don't have to worry about looking for: Cc and CC, etc.
  254.       var lowerCaseHeaderName = headerName.toLowerCase();
  255.       var foo = new Object;
  256.       foo.headerValue = headerValue;
  257.       foo.headerName = headerName;
  258.  
  259.       // some times, you can have multiple To or cc lines....in this case, we want to APPEND 
  260.       // these headers into one. 
  261.       if ( (lowerCaseHeaderName == 'to' || lowerCaseHeaderName == 'cc') && ( lowerCaseHeaderName in currentHeaderData))
  262.       {
  263.         currentHeaderData[lowerCaseHeaderName].headerValue = currentHeaderData[lowerCaseHeaderName].headerValue + ',' + foo.headerValue;
  264.       }
  265.       else
  266.        currentHeaderData[lowerCaseHeaderName] = foo;
  267.  
  268.       if (lowerCaseHeaderName == "from")
  269.       {
  270.         if (headerValue && abAddressCollector && 
  271.             ((gCollectIncoming && !dontCollectAddress) || (gCollectNewsgroup && dontCollectAddress)))
  272.           abAddressCollector.collectUnicodeAddress(headerValue);  
  273.       }
  274.  
  275.     },
  276.  
  277.     handleAttachment: function(contentType, url, displayName, uri, notDownloaded) 
  278.     {
  279.       currentAttachments.push (new createNewAttachmentInfo(contentType, url, displayName, uri, notDownloaded));
  280.       // if we have an attachment, set the MSG_FLAG_ATTACH flag on the hdr
  281.       // this will cause the "message with attachment" icon to show up
  282.       // in the thread pane
  283.       // we only need to do this on the first attachment
  284.       var numAttachments = currentAttachments.length;
  285.       if (numAttachments == 1) {
  286.         try {
  287.           // convert the uri into a hdr
  288.           var hdr = messenger.messageServiceFromURI(uri).messageURIToMsgHdr(uri);
  289.           hdr.markHasAttachments(true);
  290.         }
  291.         catch (ex) {
  292.           dump("ex = " + ex + "\n");
  293.         }
  294.       }
  295.     },
  296.     
  297.     onEndAllAttachments: function()
  298.     {
  299.       // AddSaveAllAttachmentsMenu();
  300.       if (gCollapsedHeaderViewMode)
  301.         displayAttachmentsForCollapsedView();
  302.       else
  303.         displayAttachmentsForExpandedView();
  304.     }
  305. };
  306.  
  307. function EnsureSubjectValue()
  308. {
  309.   if (!('subject' in currentHeaderData))
  310.   {
  311.     var foo = new Object;
  312.     foo.headerValue = "";
  313.     foo.headerName = 'subject';
  314.     currentHeaderData[foo.headerName] = foo;
  315.   } 
  316. }
  317.  
  318. function CheckNotify()
  319. {
  320.   if (this.NotifyClearAddresses != undefined)
  321.     NotifyClearAddresses();
  322. }
  323.  
  324.  
  325. // flush out any local state being held by a header entry for a given
  326. // table
  327. function ClearHeaderView(headerTable)
  328. {
  329.   for (index in headerTable)
  330.   {
  331.      var headerEntry = headerTable[index];
  332.      if (headerEntry.useToggle)
  333.      {
  334.        ClearEmailField(headerEntry.textNode);
  335.        if (headerEntry.longTextNode)
  336.         ClearEmailField(headerEntry.longTextNode);        
  337.      }
  338.  
  339.      headerEntry.valid = false;
  340.   }
  341. }
  342.  
  343. // make sure that any valid header entry in the table is collapsed
  344. function hideHeaderView(headerTable)
  345. {
  346.   for (index in headerTable)
  347.   {
  348.     headerTable[index].enclosingBox.setAttribute('collapsed', true);
  349.   }
  350. }
  351.  
  352. // make sure that any valid header entry in the table specified is 
  353. // visible
  354. function showHeaderView(headerTable)
  355. {
  356.   var headerEntry;
  357.   for (index in headerTable)
  358.   {
  359.     headerEntry = headerTable[index];
  360.     if (headerEntry.valid)
  361.       headerEntry.enclosingBox.removeAttribute('collapsed');
  362.     else // if the entry is invalid, always make sure it's collapsed
  363.       headerEntry.enclosingBox.setAttribute('collapsed', true);
  364.   }
  365. }
  366.  
  367. // make sure the appropriate fields within the currently displayed view header mode
  368. // are collapsed or visible...
  369. function updateHeaderViews()
  370. {
  371.   if (gCollapsedHeaderViewMode)
  372.   {
  373.     showHeaderView(gCollapsedHeaderView);
  374.     displayAttachmentsForCollapsedView();
  375.   }
  376.   else
  377.   {
  378.     showHeaderView(gExpandedHeaderView);
  379.     displayAttachmentsForExpandedView();
  380.   }
  381. }
  382.  
  383. function ToggleHeaderView ()
  384. {
  385.   var expandedNode = document.getElementById("expandedHeaderView");
  386.   var collapsedNode = document.getElementById("collapsedHeaderView");
  387.   var toggleHeaderView = document.getElementById("msgHeaderView");
  388.  
  389.   if (gCollapsedHeaderViewMode)
  390.   {          
  391.     gCollapsedHeaderViewMode = false;
  392.     // hide the current view
  393.     hideHeaderView(gCollapsedHeaderView);
  394.     // update the current view
  395.     updateHeaderViews();
  396.     
  397.     // now uncollapse / collapse the right views
  398.     expandedNode.removeAttribute("collapsed");
  399.     collapsedNode.setAttribute("collapsed", "true");
  400.   }
  401.   else
  402.   {
  403.     gCollapsedHeaderViewMode = true;
  404.     // hide the current view
  405.     hideHeaderView(gExpandedHeaderView);
  406.     // update the current view
  407.     updateHeaderViews();
  408.     
  409.     // now uncollapse / collapse the right views
  410.     collapsedNode.removeAttribute("collapsed");
  411.     expandedNode.setAttribute("collapsed", "true");
  412.   }  
  413.  
  414.   if (gCollapsedHeaderViewMode)
  415.     toggleHeaderView.setAttribute("state", "true");
  416.   else
  417.     toggleHeaderView.setAttribute("state", "false");
  418. }
  419.  
  420. // Clear Email Field takes the passed in div and removes all the child nodes!
  421. function ClearEmailField(parentDiv)
  422. {
  423.   if (parentDiv)
  424.   {
  425.     while (parentDiv.childNodes.length > 0)
  426.       parentDiv.removeChild(parentDiv.childNodes[0]);
  427.   }
  428. }
  429.  
  430. // default method for updating a header value into a header entry
  431. function updateHeaderValue(headerEntry, headerValue)
  432. {
  433.     headerEntry.textNode.childNodes[0].nodeValue = headerValue;
  434. }
  435.  
  436. function updateHeaderValueInTextNode(headerEntry, headerValue)
  437. {
  438.   headerEntry.textNode.setAttribute("value", headerValue);
  439. }
  440.  
  441. // UpdateMessageHeaders: Iterate through all the current header data we received from mime for this message
  442. // for each header entry table, see if we have a corresponding entry for that header. i.e. does the particular
  443. // view care about this header value. if it does then call updateHeaderEntry
  444. function UpdateMessageHeaders()
  445. {
  446.   // iterate over each header we received and see if we have a matching entry in each
  447.   // header view table...
  448.  
  449.   for (headerName in currentHeaderData)
  450.   {
  451.     var headerField = currentHeaderData[headerName];
  452.     var headerEntry = gExpandedHeaderView[headerName];
  453.     if (headerEntry != undefined && headerEntry)
  454.     {
  455.       headerEntry.outputFunction(headerEntry, headerField.headerValue);
  456.       headerEntry.valid = true;
  457.     }
  458.     headerEntry = gCollapsedHeaderView[headerName];
  459.     if (headerEntry != undefined && headerEntry)
  460.     {
  461.       headerEntry.outputFunction(headerEntry, headerField.headerValue);
  462.       headerEntry.valid = true;    
  463.     }
  464.   }
  465.  
  466.   // now update the view to make sure the right elements are visible
  467.   updateHeaderViews();
  468.   
  469.   if (this.FinishEmailProcessing != undefined)
  470.     FinishEmailProcessing();
  471. }
  472.  
  473. function ClearCurrentHeaders()
  474. {
  475.   currentHeaderData = {};
  476.   currentAttachments = new Array();
  477. }
  478.  
  479. function ShowMessageHeaderPane()
  480.   var node;
  481.   if (gCollapsedHeaderViewMode)
  482.   {          
  483.     node = document.getElementById("collapsedHeaderView");
  484.     if (node)
  485.       node.removeAttribute("collapsed");
  486.   }
  487.   else
  488.   {
  489.     node = document.getElementById("expandedHeaderView");
  490.     if (node)
  491.       node.removeAttribute("collapsed");
  492.   }
  493.  
  494.     /* workaround for 39655 */
  495.   if (gFolderJustSwitched) 
  496.   {
  497.     var el = document.getElementById("msgHeaderView");
  498.     el.setAttribute("style", el.getAttribute("style"));
  499.     gFolderJustSwitched = false;    
  500.   }
  501. }
  502.  
  503. function HideMessageHeaderPane()
  504. {
  505.   var node = document.getElementById("collapsedHeaderView");
  506.   if (node)
  507.     node.setAttribute("collapsed", "true");
  508.  
  509.   node = document.getElementById("expandedHeaderView");
  510.   if (node)
  511.     node.setAttribute("collapsed", "true");
  512. }
  513.  
  514. function OutputNewsgroups(headerEntry, headerValue)
  515.   headerValue = headerValue.replace(/,/g,", ");
  516.   updateHeaderValue(headerEntry, headerValue);
  517. }
  518.  
  519. // OutputEmailAddresses --> knows how to take a comma separated list of email addresses,
  520. // extracts them one by one, linkifying each email address into a mailto url. 
  521. // Then we add the link'ified email address to the parentDiv passed in.
  522. // 
  523. // defaultParentDiv --> the div to add the link-ified email addresses into. 
  524. // emailAddresses --> comma separated list of the addresses for this header field
  525. // includeShortLongToggle --> true if you want to include the ability to toggle between short/long
  526. // address views for this header field. If true, then pass in a another div which is the div the long
  527. // view will be added too...
  528. // useShortView --> if true, we'll only generate the Name of the email address field instead of
  529. //                        showing the name + the email address.
  530.  
  531. function OutputEmailAddresses(headerEntry, emailAddresses)
  532. {
  533.     if ( !emailAddresses ) return;
  534.  
  535.   if (msgHeaderParser)
  536.   {
  537.     // Count the number of email addresses being inserted into each header
  538.     // The headers could be "to", "cc", "from"
  539.     var myEnum = msgHeaderParser.ParseHeadersWithEnumerator(emailAddresses);
  540.     myEnum = myEnum.QueryInterface(Components.interfaces.nsISimpleEnumerator);
  541.  
  542.     numOfEmailsInEnumerator = 0;
  543.     while (myEnum.hasMoreElements())
  544.     {
  545.         myEnum.getNext();
  546.         numOfEmailsInEnumerator++;
  547.     }
  548.  
  549.     var enumerator = msgHeaderParser.ParseHeadersWithEnumerator(emailAddresses);
  550.     enumerator = enumerator.QueryInterface(Components.interfaces.nsISimpleEnumerator);
  551.     var numAddressesParsed = 0;
  552.     if (enumerator)
  553.     {
  554.       var emailAddress = {};
  555.       var name = {};
  556.  
  557.       while (enumerator.hasMoreElements())
  558.       {
  559.         var headerResult = enumerator.getNext();
  560.         headerResult = enumerator.QueryInterface(Components.interfaces.nsIMsgHeaderParserResult);
  561.         
  562.         // get the email and name fields
  563.         var addrValue = {};
  564.         var nameValue = {};
  565.         var fullAddress = headerResult.getAddressAndName(addrValue, nameValue);
  566.         emailAddress = addrValue.value;
  567.         name = nameValue.value;
  568.  
  569.         // if we want to include short/long toggle views and we have a long view, always add it.
  570.         // if we aren't including a short/long view OR if we are and we haven't parsed enough
  571.         // addresses to reach the cutoff valve yet then add it to the default (short) div.
  572.         if (headerEntry.useToggle && headerEntry.longTextNode)
  573.         {
  574.           InsertEmailAddressUnderEnclosingBox(headerEntry, headerEntry.longTextNode, emailAddress, fullAddress, name);
  575.         }
  576.         if (!headerEntry.useToggle || (numAddressesParsed < gNumAddressesToShow))
  577.         {
  578.           InsertEmailAddressUnderEnclosingBox(headerEntry, headerEntry.textNode, emailAddress, fullAddress, name);
  579.         }
  580.         
  581.         numAddressesParsed++;
  582.       } 
  583.     } // if enumerator
  584.  
  585.     if (headerEntry.useToggle && headerEntry.toggleIcon)
  586.     {
  587.       if (numAddressesParsed > gNumAddressesToShow) // make sure the icon is always visible if we have more than the # of addresses to show
  588.         headerEntry.toggleIcon.removeAttribute('collapsed');
  589.       else
  590.        headerEntry.toggleIcon.setAttribute('collapsed', true);
  591.     }
  592.   } // if msgheader parser
  593. }
  594.  
  595. function updateEmailAddressNode(emailAddressNode, emailAddress, fullAddress, displayName, useShortView)
  596. {
  597.   if (useShortView && displayName)
  598.     emailAddressNode.setAttribute("label", displayName);  
  599.   else
  600.     emailAddressNode.setAttribute("label", fullAddress);    
  601.   emailAddressNode.setTextAttribute("emailAddress", emailAddress);
  602.   emailAddressNode.setTextAttribute("fullAddress", fullAddress);  
  603.   emailAddressNode.setTextAttribute("displayName", displayName);  
  604.   
  605.   if (this.AddExtraAddressProcessing != undefined)
  606.     AddExtraAddressProcessing(emailAddress, emailAddressNode);
  607. }
  608.  
  609. /* InsertEmailAddressUnderEnclosingBox --> right now all email addresses are borderless titled buttons
  610.    with formatting to make them look like html anchors. When you click on the button,
  611.    you are prompted with a popup asking you what you want to do with the email address
  612.    useShortView --> only show the name (if present) instead of the name + email address
  613. */
  614.    
  615. function InsertEmailAddressUnderEnclosingBox(headerEntry, parentNode,
  616.                                              emailAddress, fullAddress, displayName) 
  617. {
  618.   var itemInDocument = parentNode;
  619.   // if this header uses a toggle then it can contain multiple email addresses. In this case,
  620.   // parentNode is really an HTML div. we need to create a mail-emailaddress element and insert it into
  621.   // the html div.
  622.   if ( headerEntry.useToggle)
  623.   {
  624.     var item = document.createElement("mail-emailaddress");
  625.     if (item && parentNode) 
  626.     { 
  627.       if (parentNode.childNodes.length >= 1)
  628.       {
  629.         var textNode = document.createElement("text");
  630.         textNode.setAttribute("value", ", ");
  631.         textNode.setAttribute("class", "emailSeparator");
  632.         parentNode.appendChild(textNode);
  633.       }
  634.   
  635.       itemInDocument = parentNode.appendChild(item);
  636.     }
  637.   } 
  638.  
  639.   updateEmailAddressNode(itemInDocument, emailAddress, fullAddress, displayName, headerEntry.useShortView);
  640. }
  641.  
  642. // ToggleLongShortAddresses is used to toggle between showing
  643. // all of the addresses on a given header line vs. only the first 'n'
  644. // where 'n' is a user controlled preference. By toggling on the more/less
  645. // images in the header window, we'll hide / show the appropriate div for that header.
  646.  
  647. function ToggleLongShortAddresses(imageID, shortDivID, longDivID)
  648. {
  649.   var shortNode = document.getElementById(shortDivID);
  650.   var longNode = document.getElementById(longDivID);
  651.   var imageNode = document.getElementById(imageID);
  652.  
  653.   var nodeToReset;
  654.  
  655.   // test to see which if short is already hidden...
  656.   if (shortNode.getAttribute("collapsed") == "true")
  657.   {        
  658.     longNode.setAttribute("collapsed", true);
  659.     shortNode.removeAttribute("collapsed"); 
  660.     imageNode.setAttribute("class", 'showMoreAddressesButton');
  661.     nodeToReset = shortNode;
  662.   }
  663.   else
  664.   {
  665.     shortNode.setAttribute("collapsed", true);
  666.     longNode.removeAttribute("collapsed"); 
  667.     imageNode.setAttribute("class", 'showFewerAddressesButton');
  668.     nodeToReset = longNode;
  669.   }
  670. }
  671.  
  672. function AddNodeToAddressBook (emailAddressNode)
  673. {
  674.   if (emailAddressNode)
  675.   {
  676.     var primaryEmail = emailAddressNode.getAttribute("emailAddress");
  677.     var displayName = emailAddressNode.getAttribute("displayName");
  678.       window.openDialog("chrome://messenger/content/addressbook/abNewCardDialog.xul",
  679.                       "",
  680.                       "chrome,titlebar,resizeable=no", 
  681.             {primaryEmail:primaryEmail, displayName:displayName });
  682.   }
  683. }
  684.  
  685. // SendMailToNode takes the email address title button, extracts
  686. // the email address we stored in there and opens a compose window
  687. // with that address
  688. function SendMailToNode(emailAddressNode)
  689. {
  690.   if (emailAddressNode)
  691.   {
  692.      var emailAddress = emailAddressNode.getAttribute("emailAddress");
  693.      if (emailAddress)
  694.         messenger.OpenURL("mailto:" + emailAddress );
  695.   }
  696. }
  697.  
  698. // CopyEmailAddress takes the email address title button, extracts
  699. // the email address we stored in there and copies it to the clipboard
  700. function CopyEmailAddress(emailAddressNode)
  701. {
  702.   if (emailAddressNode)
  703.   {
  704.     var emailAddress = emailAddressNode.getAttribute("emailAddress");
  705.  
  706.     var contractid = "@mozilla.org/widget/clipboardhelper;1";
  707.     var iid = Components.interfaces.nsIClipboardHelper;
  708.     var clipboard = Components.classes[contractid].getService(iid);
  709.     clipboard.copyString(emailAddress);
  710.   }
  711. }
  712.  
  713. // createnewAttachmentInfo --> constructor method for creating new attachment object which goes into the
  714. // data attachment array.
  715. function createNewAttachmentInfo(contentType, url, displayName, uri, notDownloaded)
  716. {
  717.   this.contentType = contentType;
  718.   this.url = url;
  719.   this.displayName = displayName;
  720.   this.uri = uri;
  721.   this.notDownloaded = notDownloaded;
  722. }
  723.  
  724. function saveAttachment(contentType, url, displayName, messageUri)
  725. {
  726.   messenger.saveAttachment(contentType, url, displayName, messageUri);
  727. }
  728.  
  729. function openAttachment(contentType, url, displayName, messageUri)
  730. {
  731.   messenger.openAttachment(contentType, url, displayName, messageUri);
  732. }
  733.  
  734. function printAttachmentAttachment(contentType, url, displayName, messageUri)
  735. {
  736.   // we haven't implemented the ability to print attachments yet...
  737.   // messenger.printAttachment(contentType, url, displayName, messageUri);
  738. }
  739.  
  740. // this is our onclick handler for the attachment tree. 
  741. // A double click in a tree cell simulates "opening" the attachment....
  742. function attachmentTreeClick(event)
  743.     // we only care about button 0 (left click) events
  744.     if (event.button != 0) return;
  745.  
  746.     if (event.detail == 2) // double click
  747.     {
  748.         var target = event.originalTarget;
  749.         var item = target.parentNode.parentNode;
  750.         if (item.localName == "treeitem")
  751.     {
  752.             var commandStringSuffix = item.getAttribute("commandSuffix");
  753.       var openString = 'openAttachment' + commandStringSuffix;
  754.       eval(openString);
  755.     }
  756.     }
  757. }
  758.  
  759. // on command handlers for the attachment tree context menu...
  760. // commandPrefix matches one of our existing functions (openAttachment, saveAttachment, etc.) which we'll add to the command suffix
  761. // found on the tree item....
  762. function handleAttachmentSelection(commandPrefix)
  763. {
  764.   // get the selected attachment...and call openAttachment on it...
  765.   var attachmentTree = document.getElementById('attachmentTree');
  766.   var selectedAttachments = attachmentTree.selectedItems;
  767.   var treeItem = selectedAttachments[0];
  768.   var commandStringSuffix = treeItem.getAttribute("commandSuffix");
  769.   var openString = commandPrefix + commandStringSuffix;
  770.   eval(openString);
  771. }
  772.  
  773. function generateCommandSuffixForAttachment(attachment)
  774. {
  775.   return "('" + attachment.contentType + "', '" + attachment.url + "', '" + escape(attachment.displayName) + "', '" + attachment.uri + "')";
  776. }
  777.  
  778. function displayAttachmentsForExpandedView()
  779. {
  780.   var numAttachments = currentAttachments.length;
  781.   if (numAttachments > 0 && !gBuildAttachmentsForCurrentMsg)
  782.   {
  783.     var attachmentList = document.getElementById('attachmentsBody');
  784.     var row, cell, item;
  785.     for (index in currentAttachments)
  786.     {
  787.       var attachment = currentAttachments[index];
  788.       // we need to create a tree item, a tree row and a tree cell to insert the attachment
  789.       // into the attachment tree..
  790.  
  791.       item = document.createElement("treeitem");
  792.       row = document.createElement("treerow");
  793.       cell = document.createElement("treecell");
  794.  
  795.       cell.setAttribute('class', "treecell-iconic"); 
  796.       cell.setAttribute("label", attachment.displayName);
  797.       cell.setAttribute("tooltip", "attachmentTreeTooltip");
  798.       item.setAttribute("commandSuffix", generateCommandSuffixForAttachment(attachment)); // set the command suffix on the tree item...
  799.       setApplicationIconForAttachment(attachment, cell);
  800.       row.appendChild(cell);
  801.       item.appendChild(row);
  802.       attachmentList.appendChild(item);
  803.     } // for each attachment
  804.     gBuildAttachmentsForCurrentMsg = true;
  805.   }
  806.  
  807.   var attachmentNode = document.getElementById('expandedAttachmentBox');
  808.   if (numAttachments > 0)  // make sure the attachment button is visible
  809.     attachmentNode.removeAttribute('collapsed');
  810.   else
  811.     attachmentNode.setAttribute('collapsed', true);
  812. }
  813.  
  814. // attachment --> the attachment struct containing all the information on the attachment
  815. // treeCell --> the tree cell currently showing the attachment.
  816. function setApplicationIconForAttachment(attachment, treeCell)
  817. {
  818.    // generate a moz-icon url for the attachment so we'll show a nice icon next to it.
  819.    treeCell.setAttribute('src', "moz-icon:" + "//" + attachment.displayName + "?size=16&contentType=" + attachment.contentType);
  820. }
  821.  
  822. function displayAttachmentsForCollapsedView()
  823. {
  824.   var numAttachments = currentAttachments.length;
  825.   var attachmentNode = document.getElementById('collapsedAttachmentBox');
  826.   if (numAttachments > 0)  // make sure the attachment button is visible
  827.   {
  828.     attachmentNode.removeAttribute('hide');
  829.   }
  830.   else
  831.   {
  832.     attachmentNode.setAttribute('hide', true);
  833.   }
  834. }
  835.  
  836. // Public method called to generate a tooltip over an attachment
  837. function FillInAttachmentTooltip(cellNode)
  838. {
  839.   var attachmentName = cellNode.getAttribute("label");
  840.   var textNode = document.getElementById("attachmentTreeTooltipText");
  841.   textNode.setAttribute('value', attachmentName);
  842.   return true;
  843. }
  844.  
  845. // Public method called when we create the attachments file menu
  846. function FillAttachmentListPopup(popup)
  847. {
  848.   // the FE sometimes call this routie TWICE...I haven't been able to figure out why yet...
  849.   // protect against it...
  850.  
  851.   if (!gBuildAttachmentPopupForCurrentMsg) return; 
  852.  
  853.   // otherwise we need to build the attachment view...
  854.   // First clear out the old view...
  855.   ClearAttachmentMenu(popup);
  856.  
  857.  
  858.   for (index in currentAttachments)
  859.   {
  860.     addAttachmentToPopup(popup, currentAttachments[index]);
  861.   }
  862.  
  863.   gBuildAttachmentPopupForCurrentMsg = false;
  864.  
  865. }
  866.  
  867. // Public method used to clear the file attachment menu
  868. function ClearAttachmentMenu(popup) 
  869.   if ( popup ) 
  870.   { 
  871.      while ( popup.childNodes.length > 2 ) 
  872.        popup.removeChild(popup.childNodes[0]); 
  873.   } 
  874. }
  875.  
  876. // Public method used to determine the number of attachments for the currently displayed message...
  877. function GetNumberOfAttachmentsForDisplayedMessage()
  878. {
  879.   return currentAttachments.length;
  880. }
  881.  
  882. // private method used to build up a menu list of attachments
  883. function addAttachmentToPopup(popup, attachment) 
  884.   if (popup)
  885.   { 
  886.     var item = document.createElement('menu');
  887.     if ( item ) 
  888.     {     
  889.       // insert the item just before the separator...the separator is the 2nd to last element in the popup.
  890.       item.setAttribute('class', 'menu-iconic');
  891.       setApplicationIconForAttachment(attachment,item);
  892.       var numItemsInPopup = popup.childNodes.length;
  893.       item = popup.insertBefore(item, popup.childNodes[numItemsInPopup-2]);
  894.       item.setAttribute('label', attachment.displayName); 
  895.       var oncommandPrefix = generateCommandSuffixForAttachment(attachment);
  896.  
  897.       var openpopup = document.createElement('menupopup');
  898.       openpopup = item.appendChild(openpopup);
  899.  
  900.       var menuitementry = document.createElement('menuitem');     
  901.       menuitementry.setAttribute('oncommand', 'openAttachment' + oncommandPrefix); 
  902.  
  903.       if (!gSaveLabel || !gOpenLabel) {
  904.         var messengerBundle = document.getElementById("bundle_messenger");
  905.         gSaveLabel = messengerBundle.getString("saveLabel");
  906.         gOpenLabel = messengerBundle.getString("openLabel");
  907.       }
  908.  
  909.       menuitementry.setAttribute('label', gOpenLabel); 
  910.       menuitementry = openpopup.appendChild(menuitementry);
  911.  
  912.       var menuseparator = document.createElement('menuseparator');
  913.       openpopup.appendChild(menuseparator);
  914.       
  915.       menuitementry = document.createElement('menuitem');
  916.       menuitementry.setAttribute('oncommand', 'saveAttachment' + oncommandPrefix); 
  917.       menuitementry.setAttribute('label', gSaveLabel); 
  918.       menuitementry = openpopup.appendChild(menuitementry);
  919.     }  // if we created a menu item for this attachment...
  920.   } // if we have a popup
  921.  
  922. function SaveAllAttachments()
  923. {
  924.  try 
  925.  {
  926.    // convert our attachment data into some c++ friendly structs
  927.    var attachmentContentTypeArray = new Array();
  928.    var attachmentUrlArray = new Array();
  929.    var attachmentDisplayNameArray = new Array();
  930.    var attachmentMessageUriArray = new Array();
  931.  
  932.    // populate these arrays..
  933.    for (index in currentAttachments)
  934.    {
  935.      var attachment = currentAttachments[index];
  936.      attachmentContentTypeArray[index] = attachment.contentType;
  937.      attachmentUrlArray[index] = attachment.url;
  938.      attachmentDisplayNameArray[index] = escape(attachment.displayName);
  939.      attachmentMessageUriArray[index] = attachment.uri;
  940.    }
  941.  
  942.    // okay the list has been built...now call our save all attachments code...
  943.    messenger.saveAllAttachments(attachmentContentTypeArray.length,
  944.                                 attachmentContentTypeArray, attachmentUrlArray,
  945.                                 attachmentDisplayNameArray, attachmentMessageUriArray);
  946.  }
  947.  catch (ex)
  948.  {
  949.    dump ("** failed to save all attachments **\n");
  950.  }
  951. }
  952.  
  953. function ClearAttachmentTreeList() 
  954.   var attachmentTreebody = document.getElementById("attachmentsBody"); 
  955.   if ( attachmentTreebody ) 
  956.   { 
  957.      while ( attachmentTreebody.childNodes.length ) 
  958.        attachmentTreebody.removeChild(attachmentTreebody.childNodes[0]); 
  959.   } 
  960. }
  961.  
  962. function ShowEditMessageButton() 
  963. {
  964.   var editBox = document.getElementById("editMessageBox");
  965.   if (editBox)
  966.     editBox.removeAttribute("collapsed");
  967.  
  968. function ClearEditMessageButton() 
  969.   var editBox = document.getElementById("editMessageBox");
  970.   if (editBox)
  971.     editBox.setAttribute("collapsed", "true");
  972. }
  973.  
  974. // given a box, iterate through all of the headers and add them to 
  975. // the enclosing box. 
  976. function fillBoxWithAllHeaders(containerBox, boxPartOfPopup)
  977. {
  978.   // clear out the old popup date if there is any...
  979.   while ( containerBox.childNodes.length ) 
  980.     containerBox.removeChild(containerBox.childNodes[0]); 
  981.  
  982.   for (header in currentHeaderData)
  983.   {
  984.     var innerBox = document.createElement('box');
  985.     innerBox.setAttribute("class", "headerBox");
  986.     innerBox.setAttribute("orient", "horizontal");
  987.     if (boxPartOfPopup)
  988.         innerBox.setAttribute("autostretch", "never");
  989.  
  990.     // for each header, create a header value and header name then assign those values...
  991.     var headerValueBox = document.createElement('hbox');
  992.     headerValueBox.setAttribute("class", "headerValueBox");
  993.  
  994.       var newHeaderTitle  = document.createElement('text');
  995.     newHeaderTitle.setAttribute("class", "headerdisplayname");
  996.       newHeaderTitle.setAttribute("value", currentHeaderData[header].headerName + ':');
  997.     headerValueBox.appendChild(newHeaderTitle);
  998.  
  999.       innerBox.appendChild(headerValueBox);
  1000.  
  1001.     var newHeaderValue = document.createElement('html');
  1002.     // make sure we are properly resized...
  1003. //    if (boxPartOfPopup)
  1004. //        newHeaderValue.setAttribute("width", window.innerWidth*.65);
  1005.     newHeaderValue.setAttribute("class", "headerValue");
  1006.     if (!boxPartOfPopup)
  1007.       newHeaderValue.setAttribute("flex", "1");
  1008.     innerBox.appendChild(newHeaderValue);
  1009.     containerBox.appendChild(innerBox);
  1010.     ProcessHeaderValue(innerBox, newHeaderValue, currentHeaderData[header], boxPartOfPopup);
  1011.   }
  1012. }
  1013.  
  1014. // the on create handler for the view all headers popup...
  1015. function fillAllHeadersPopup(node)
  1016. {
  1017.   // don't bother re-filling the popup if we've already done it for the
  1018.   // currently displayed message....
  1019.   if (gGeneratedViewAllHeaderInfo == true)
  1020.     return true; 
  1021.  
  1022.   var containerBox = document.getElementById('allHeadersPopupContainer');
  1023.  
  1024.   containerBox.setAttribute("class", "header-part1");
  1025.   containerBox.setAttribute("align", "vertical");
  1026.   containerBox.setAttribute("flex", "1");
  1027.  
  1028.   fillBoxWithAllHeaders(containerBox, true);
  1029.   gGeneratedViewAllHeaderInfo = true; // don't try to regenerate this information for the currently displayed message
  1030.   return true;
  1031. }
  1032.  
  1033. // containingBox --> the box containing the header
  1034. // containerNode --> the div or box that we want to insert this specific header into
  1035. // header --> an entry from currentHeaderData contains .headerName and .headerValue
  1036. // boxPartOfPopup --> true if the box is part of a popup (certain functions are disabled in this scenario)
  1037. function ProcessHeaderValue(containingBox, containerNode, header, boxPartOfPopup)
  1038. {
  1039.   // in the simplest case, we'll just create a text node for the header
  1040.   // and append it to the container Node. for certain headers, we might want to do 
  1041.   // extra processing....
  1042.   
  1043.   var headerName = header.headerName;
  1044.   headerName = headerName.toLowerCase();
  1045.   if (!boxPartOfPopup && (headerName == "cc" || headerName == "from" || headerName == "to" || headerName == "reply-to"))
  1046.   {
  1047.     OutputEmailAddresses(containingBox, containerNode, header.headerValue, false, "", "", headerName)
  1048.     return;
  1049.   }
  1050.   
  1051.   var headerValue = header.headerValue;
  1052.   headerValue = headerValue.replace(/\n|\r/g,"");
  1053.   var textNode = document.createTextNode(headerValue);
  1054.   if (headerName == "subject")
  1055.   {
  1056.       containerNode.setAttribute("class", "subjectvalue headerValue");
  1057.   }
  1058.  
  1059.   containerNode.appendChild(textNode);
  1060. }
  1061.