home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 January / 01_02.iso / software / netscape62win / mail.xpi / bin / chrome / messenger.jar / content / messenger / messengercompose / addressingWidgetOverlay.js next >
Text File  |  2001-09-26  |  29KB  |  956 lines

  1. /*
  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.org code.
  13.  *
  14.  * The Initial Developer of the Original Code is Netscape
  15.  * Communications Corporation.  Portions created by Netscape are
  16.  * Copyright (C) 1998 Netscape Communications Corporation. All
  17.  * Rights Reserved.
  18.  *
  19.  * Contributor(s):
  20.  */
  21.  
  22. top.MAX_RECIPIENTS = 0;
  23.  
  24. var inputElementType = "";
  25. var selectElementType = "";
  26. var selectElementIndexTable = null;
  27. var gPromptService = null;
  28.  
  29. var test_addresses_sequence = false;
  30. if (prefs)
  31.   try {
  32.     test_addresses_sequence = prefs.GetBoolPref("mail.debug.test_addresses_sequence");
  33.   }
  34.   catch (ex) {}
  35.  
  36. function awInputElementName()
  37. {
  38.     if (inputElementType == "")
  39.         inputElementType = document.getElementById("msgRecipient#1").localName;
  40.     return inputElementType;
  41. }
  42.  
  43. function awSelectElementName()
  44. {
  45.     if (selectElementType == "")
  46.         selectElementType = document.getElementById("msgRecipientType#1").localName;
  47.     return selectElementType;
  48. }
  49.  
  50. function awGetSelectItemIndex(itemData)
  51. {
  52.     if (selectElementIndexTable == null)
  53.     {
  54.       selectElementIndexTable = new Object();
  55.       var selectElem = document.getElementById("msgRecipientType#1");
  56.         for (var i = 0; i < selectElem.childNodes[0].childNodes.length; i ++)
  57.     {
  58.             var aData = selectElem.childNodes[0].childNodes[i].getAttribute("value");
  59.             selectElementIndexTable[aData] = i;
  60.         }
  61.     }
  62.     return selectElementIndexTable[itemData];
  63. }
  64.  
  65. function Recipients2CompFields(msgCompFields)
  66. {
  67.   if (msgCompFields)
  68.   {
  69.     var i = 1;
  70.     var addrTo = "";
  71.     var addrCc = "";
  72.     var addrBcc = "";
  73.     var addrReply = "";
  74.     var addrNg = "";
  75.     var addrFollow = "";
  76.     var addrOther = "";
  77.     var to_Sep = "";
  78.     var cc_Sep = "";
  79.     var bcc_Sep = "";
  80.     var reply_Sep = "";
  81.     var ng_Sep = "";
  82.     var follow_Sep = "";
  83.  
  84.     var inputField;
  85.       while ((inputField = awGetInputElement(i)))
  86.       {
  87.         var fieldValue = inputField.value;
  88.  
  89.         if (fieldValue == null)
  90.           fieldValue = inputField.getAttribute("value");
  91.  
  92.         if (fieldValue != "")
  93.         {
  94.           switch (awGetPopupElement(i).selectedItem.getAttribute("value"))
  95.           {
  96.             case "addr_to"      : addrTo += to_Sep + fieldValue; to_Sep = ",";          break;
  97.             case "addr_cc"      : addrCc += cc_Sep + fieldValue; cc_Sep = ",";          break;
  98.             case "addr_bcc"     : addrBcc += bcc_Sep + fieldValue; bcc_Sep = ",";       break;
  99.             case "addr_reply"   : addrReply += reply_Sep + fieldValue; reply_Sep = ",";     break;
  100.             case "addr_newsgroups"  : addrNg += ng_Sep + fieldValue; ng_Sep = ",";          break;
  101.             case "addr_followup"  : addrFollow += follow_Sep + fieldValue; follow_Sep = ",";    break;
  102.           case "addr_other"   : addrOther += other_header + ": " + fieldValue + "\n";     break;
  103.           }
  104.         }
  105.         i ++;
  106.       }
  107.  
  108.       msgCompFields.to = addrTo;
  109.       msgCompFields.cc = addrCc;
  110.       msgCompFields.bcc = addrBcc;
  111.       msgCompFields.replyTo = addrReply;
  112.       msgCompFields.newsgroups = addrNg;
  113.       msgCompFields.followupTo = addrFollow;
  114.       msgCompFields.otherRandomHeaders = addrOther;
  115.   }
  116.   else
  117.     dump("Message Compose Error: msgCompFields is null (ExtractRecipients)");
  118. }
  119.  
  120. function CompFields2Recipients(msgCompFields, msgType)
  121. {
  122.   if (msgCompFields)
  123.   {
  124.       var treeChildren = document.getElementById('addressWidgetBody');
  125.       var newTreeChildrenNode = treeChildren.cloneNode(false);
  126.       var templateNode = treeChildren.firstChild;
  127.  
  128.     top.MAX_RECIPIENTS = 0;
  129.  
  130.     awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.replyTo, false), "addr_reply", newTreeChildrenNode, templateNode);
  131.     awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.to, false), "addr_to", newTreeChildrenNode, templateNode);
  132.     awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.cc, false), "addr_cc", newTreeChildrenNode, templateNode);
  133.     awSetInputAndPopupFromArray(msgCompFields.SplitRecipients(msgCompFields.bcc, false), "addr_bcc", newTreeChildrenNode, templateNode);
  134.     awSetInputAndPopup(msgCompFields.otherRandomHeaders, "addr_other", newTreeChildrenNode, templateNode);
  135.     awSetInputAndPopup(msgCompFields.newsgroups, "addr_newsgroups", newTreeChildrenNode, templateNode);
  136.     awSetInputAndPopup(msgCompFields.followupTo, "addr_followup", newTreeChildrenNode, templateNode);
  137.     //If it's a new message, we need to add an extrat empty recipient.
  138.     var msgComposeType = Components.interfaces.nsIMsgCompType;
  139.     if (!msgCompFields.to && !msgCompFields.newsgroups)
  140.       _awSetInputAndPopup("", "addr_to", newTreeChildrenNode, templateNode);
  141.       // dump("replacing child in comp fields 2 recips \n");
  142.       var parent = treeChildren.parentNode;
  143.       parent.replaceChild(newTreeChildrenNode, treeChildren);
  144.       awFitDummyRows();
  145.         setTimeout("awFinishCopyNodes();", 0);
  146.   }
  147. }
  148.  
  149. function awSetInputAndPopupValue(inputElem, inputValue, popupElem, popupValue, rowNumber)
  150. {
  151.   // remove leading spaces
  152.   while (inputValue && inputValue[0] == " " )
  153.     inputValue = inputValue.substring(1, inputValue.length);
  154.  
  155.   inputElem.setAttribute("value", inputValue);
  156.   inputElem.value = inputValue;
  157.  
  158.   popupElem.selectedItem = popupElem.childNodes[0].childNodes[awGetSelectItemIndex(popupValue)];
  159.  
  160.   if (rowNumber >= 0)
  161.   {
  162.     inputElem.setAttribute("id", "msgRecipient#" + rowNumber);
  163.     popupElem.setAttribute("id", "msgRecipientType#" + rowNumber);
  164.   }
  165.  
  166.   _awSetAutoComplete(popupElem, inputElem);
  167. }
  168.  
  169. function _awSetInputAndPopup(inputValue, popupValue, parentNode, templateNode)
  170. {
  171.     top.MAX_RECIPIENTS++;
  172.  
  173.     var newNode = templateNode.cloneNode(true);
  174.     parentNode.appendChild(newNode); // we need to insert the new node before we set the value of the select element!
  175.  
  176.     var input = newNode.getElementsByTagName(awInputElementName());
  177.     var select = newNode.getElementsByTagName(awSelectElementName());
  178.  
  179.     if (input && input.length == 1 && select && select.length == 1)
  180.       awSetInputAndPopupValue(input[0], inputValue, select[0], popupValue, top.MAX_RECIPIENTS)
  181. }
  182.  
  183. function awSetInputAndPopup(inputValue, popupValue, parentNode, templateNode)
  184. {
  185.   if ( inputValue && popupValue )
  186.   {
  187.     var addressArray = inputValue.split(",");
  188.  
  189.     for ( var index = 0; index < addressArray.length; index++ )
  190.         _awSetInputAndPopup(addressArray[index], popupValue, parentNode, templateNode);
  191.   }
  192. }
  193.  
  194. function awSetInputAndPopupFromArray(inputArray, popupValue, parentNode, templateNode)
  195. {
  196.   if ( inputArray && popupValue )
  197.   {
  198.     for ( var index = 0; index < inputArray.count; index++ )
  199.         _awSetInputAndPopup(inputArray.StringAt(index), popupValue, parentNode, templateNode);
  200.   }
  201. }
  202.  
  203. function awRemoveRecipients(msgCompFields, recipientType, recipientsList)
  204. {
  205.   if (!msgCompFields)
  206.     return;
  207.  
  208.   var recipientArray = msgCompFields.SplitRecipients(recipientsList, false);
  209.   if (! recipientArray)
  210.     return;
  211.  
  212.   for ( var index = 0; index < recipientArray.count; index++ )
  213.     for (var row = 1; row <= top.MAX_RECIPIENTS; row ++)
  214.     {
  215.       var popup = awGetPopupElement(row);
  216.       if (popup.selectedItem.getAttribute("value") == recipientType)
  217.       {
  218.         var input = awGetInputElement(row);
  219.         if (input.value == recipientArray.StringAt(index))
  220.         {
  221.           awSetInputAndPopupValue(input, "", popup, "addr_to", -1);
  222.           break;
  223.         }
  224.       }
  225.     }
  226. }
  227.  
  228. function awAddRecipients(msgCompFields, recipientType, recipientsList)
  229. {
  230.   if (!msgCompFields)
  231.     return;
  232.  
  233.   var recipientArray = msgCompFields.SplitRecipients(recipientsList, false);
  234.   if (! recipientArray)
  235.     return;
  236.  
  237.   for ( var index = 0; index < recipientArray.count; index++ )
  238.   {
  239.     for (var row = 1; row <= top.MAX_RECIPIENTS; row ++)
  240.     {
  241.       if (awGetInputElement(row).value == "")
  242.         break;
  243.     }
  244.     if (row > top.MAX_RECIPIENTS)
  245.       awAppendNewRow(false);
  246.  
  247.     awSetInputAndPopupValue(awGetInputElement(row), recipientArray.StringAt(index), awGetPopupElement(row), recipientType, row);
  248.  
  249.     /* be sure we still have an empty row left at the end */
  250.     if (row == top.MAX_RECIPIENTS)
  251.     {
  252.       awAppendNewRow(true);
  253.       awSetInputAndPopupValue(awGetInputElement(top.MAX_RECIPIENTS), "", awGetPopupElement(top.MAX_RECIPIENTS), "addr_to", top.MAX_RECIPIENTS);
  254.     }
  255.   }
  256. }
  257.  
  258. function awTestRowSequence()
  259. {
  260.   /*
  261.     This function is for debug and testing purpose only, normal user should not run it!
  262.  
  263.     Everytime we insert or delete a row, we must be sure we didn't break the ID sequence of
  264.     the addressing widget rows. This function will run a quick test to see if the sequence still ok
  265.  
  266.     You need to define the pref mail.debug.test_addresses_sequence to true in order to activate it
  267.   */
  268.  
  269.   if (! test_addresses_sequence)
  270.     return true;
  271.  
  272.   /* debug code to verify the sequence still good */
  273.   var body = document.getElementById('addressWidgetBody');
  274.   var treeitems = body.getElementsByTagName('treeitem');
  275.   if (treeitems.length == top.MAX_RECIPIENTS )
  276.   {
  277.     for (var i = 1; i <= treeitems.length; i ++)
  278.     {
  279.       var item = treeitems[i - 1];
  280.       var inputID = item.getElementsByTagName(awInputElementName())[0].getAttribute("id").split("#")[1];
  281.       var popupID = item.getElementsByTagName(awSelectElementName())[0].getAttribute("id").split("#")[1];
  282.       if (inputID != i || popupID != i)
  283.       {
  284.         dump("#ERROR: sequence broken at row " + i + ", inputID=" + inputID + ", popupID=" + popupID + "\n");
  285.         break;
  286.       }
  287.     }
  288.     dump("---SEQUENCE OK---\n");
  289.     return true;
  290.   }
  291.   else
  292.     dump("#ERROR: treeitems.length(" + treeitems.length + ") != top.MAX_RECIPIENTS(" + top.MAX_RECIPIENTS + ")\n");
  293.  
  294.   return false;
  295. }
  296.  
  297. function awCleanupRows()
  298. {
  299.   var maxRecipients = top.MAX_RECIPIENTS;
  300.   var rowID = 1;
  301.  
  302.   for (var row = 1; row <= maxRecipients; row ++)
  303.   {
  304.     var inputElem = awGetInputElement(row);
  305.     if (inputElem.value == "" && row < maxRecipients)
  306.       awRemoveRow(row);
  307.     else
  308.     {
  309.       inputElem.setAttribute("id", "msgRecipient#" + rowID);
  310.       awGetPopupElement(row).setAttribute("id", "msgRecipientType#" + rowID);
  311.       rowID ++;
  312.     }
  313.   }
  314.  
  315.   awTestRowSequence();
  316. }
  317.  
  318. function awDeleteRow(rowToDelete)
  319. {
  320.   /* When we delete a row, we must reset the id of others row in order to not break the sequence */
  321.   var maxRecipients = top.MAX_RECIPIENTS;
  322.   var rowID = rowToDelete;
  323.  
  324.   awRemoveRow(rowToDelete);
  325.  
  326.   for (var row = rowToDelete + 1; row <= maxRecipients; row ++)
  327.   {
  328.     awGetInputElement(row).setAttribute("id", "msgRecipient#" + rowID);
  329.     awGetPopupElement(row).setAttribute("id", "msgRecipientType#" + rowID);
  330.     rowID ++;
  331.   }
  332.  
  333.   awTestRowSequence();
  334. }
  335.  
  336. function awClickEmptySpace(targ, setFocus)
  337. {
  338.   if (targ.localName != 'treechildren')
  339.     return;
  340.  
  341.   // dump("awClickEmptySpace\n");
  342.   var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
  343.  
  344.   if ( lastInput && lastInput.value )
  345.     awAppendNewRow(setFocus);
  346.   else
  347.     if (setFocus)
  348.       awSetFocus(top.MAX_RECIPIENTS, lastInput);
  349. }
  350.  
  351. function awReturnHit(inputElement)
  352. {
  353.   var row = awGetRowByInputElement(inputElement);
  354.   var nextInput = awGetInputElement(row+1);
  355.  
  356.   if ( !nextInput )
  357.   {
  358.     if ( inputElement.value )
  359.       awAppendNewRow(true);
  360.     else // No adress entered, switch to Subject field
  361.     {
  362.       var subjectField = document.getElementById( 'msgSubject' );
  363.       subjectField.select();
  364.       subjectField.focus();
  365.     }
  366.   }
  367.   else
  368.   {
  369.     nextInput.select();
  370.     awSetFocus(row+1, nextInput);
  371.   }
  372. }
  373.  
  374. function awDeleteHit(inputElement)
  375. {
  376.   var row = awGetRowByInputElement(inputElement);
  377.  
  378.   /* 1. don't delete the row if it's the last one remaining, just reset it! */
  379.   if (top.MAX_RECIPIENTS <= 1)
  380.   {
  381.     inputElement.value = "";
  382.     return;
  383.   }
  384.  
  385.   /* 2. Set the focus to the previous field if possible */
  386.   if (row > 1)
  387.     awSetFocus(row - 1, awGetInputElement(row - 1))
  388.   else
  389.     awSetFocus(1, awGetInputElement(2))   /* We have to cheat a little bit because the focus will */
  390.                                           /* be set asynchronusly after we delete the current row, */
  391.                                           /* therefore the row number still the same! */
  392.  
  393.   /* 3. Delete the row */
  394.   awDeleteRow(row);
  395. }
  396.  
  397. function awInputChanged(inputElement)
  398. {
  399.   dump("awInputChanged\n");
  400. //  AutoCompleteAddress(inputElement);
  401.  
  402.   //Do we need to add a new row?
  403.   var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
  404.   if ( lastInput && lastInput.value && !top.doNotCreateANewRow)
  405.     awAppendNewRow(false);
  406.   top.doNotCreateANewRow = false;
  407. }
  408.  
  409. function awAppendNewRow(setFocus)
  410. {
  411.   var body = document.getElementById('addressWidgetBody');
  412.   var treeitem1 = awGetTreeItem(1);
  413.  
  414.   if ( body && treeitem1 )
  415.   {
  416.       var lastRecipientType = awGetPopupElement(top.MAX_RECIPIENTS).selectedItem.getAttribute("value");
  417.  
  418.     var nextDummy = awGetNextDummyRow();
  419.     if (nextDummy)  {
  420.       body.removeChild(nextDummy);
  421.       nextDummy = awGetNextDummyRow();
  422.     }
  423.     var newNode = awCopyNode(treeitem1, body, nextDummy);
  424.  
  425.     top.MAX_RECIPIENTS++;
  426.  
  427.         var input = newNode.getElementsByTagName(awInputElementName());
  428.         if ( input && input.length == 1 )
  429.         {
  430.           input[0].setAttribute("value", "");
  431.           input[0].setAttribute("id", "msgRecipient#" + top.MAX_RECIPIENTS);
  432.  
  433.           //this copies the autocomplete sessions list from recipient#1 
  434.           input[0].syncSessions(document.getElementById('msgRecipient#1'));
  435.  
  436.       // also clone the showCommentColumn setting
  437.       //
  438.       input[0].showCommentColumn = 
  439.           document.getElementById("msgRecipient#1").showCommentColumn;
  440.  
  441.           // We always clone the first row.  The problem is that the first row
  442.           // could be focused.  When we clone that row, we end up with a cloned
  443.           // XUL textbox that has a focused attribute set.  Therefore we think
  444.           // we're focused and don't properly refocus.  The best solution to this
  445.           // would be to clone a template row that didn't really have any presentation,
  446.           // rather than using the real visible first row of the tree.
  447.           //
  448.           // For now we'll just put in a hack that ensures the focused attribute
  449.           // is never copied when the node is cloned.
  450.           if (input[0].getAttribute('focused') != '')
  451.             input[0].removeAttribute('focused');
  452.         }
  453.         var select = newNode.getElementsByTagName(awSelectElementName());
  454.         if ( select && select.length == 1 )
  455.         {
  456.             select[0].selectedItem = select[0].childNodes[0].childNodes[awGetSelectItemIndex(lastRecipientType)];
  457.           select[0].setAttribute("id", "msgRecipientType#" + top.MAX_RECIPIENTS);
  458.           if (input)
  459.             _awSetAutoComplete(select[0], input[0]);
  460.       }
  461.  
  462.     // focus on new input widget
  463.     if (setFocus && input[0] )
  464.       awSetFocus(top.MAX_RECIPIENTS, input[0]);
  465.   }
  466. }
  467.  
  468.  
  469. // functions for accessing the elements in the addressing widget
  470.  
  471. function awGetPopupElement(row)
  472. {
  473.     return document.getElementById("msgRecipientType#" + row);
  474. }
  475.  
  476. function awGetInputElement(row)
  477. {
  478.     return document.getElementById("msgRecipient#" + row);
  479. }
  480.  
  481. function awGetTreeRow(row)
  482. {
  483.   var body = document.getElementById('addressWidgetBody');
  484.  
  485.   if ( body && row > 0)
  486.   {
  487.     var treerows = body.getElementsByTagName('treerow');
  488.     if ( treerows && treerows.length >= row )
  489.       return treerows[row-1];
  490.   }
  491.   return 0;
  492. }
  493.  
  494. function awGetTreeItem(row)
  495. {
  496.   var body = document.getElementById('addressWidgetBody');
  497.  
  498.   if ( body && row > 0)
  499.   {
  500.     var treeitems = body.getElementsByTagName('treeitem');
  501.     if ( treeitems && treeitems.length >= row )
  502.       return treeitems[row-1];
  503.   }
  504.   return 0;
  505. }
  506.  
  507. function awGetRowByInputElement(inputElement)
  508. {
  509.   if ( inputElement )
  510.   {
  511.     var treerow;
  512.     var inputElementTreerow = inputElement.parentNode.parentNode;
  513.  
  514.     if ( inputElementTreerow )
  515.     {
  516.       for ( var row = 1;  (treerow = awGetTreeRow(row)); row++ )
  517.       {
  518.         if ( treerow == inputElementTreerow )
  519.           return row;
  520.       }
  521.     }
  522.   }
  523.   return 0;
  524. }
  525.  
  526.  
  527. // Copy Node - copy this node and insert ahead of the (before) node.  Append to end if before=0
  528. function awCopyNode(node, parentNode, beforeNode)
  529. {
  530.   var newNode = node.cloneNode(true);
  531.  
  532.   if ( beforeNode )
  533.     parentNode.insertBefore(newNode, beforeNode);
  534.   else
  535.     parentNode.appendChild(newNode);
  536.  
  537.     return newNode;
  538. }
  539.  
  540. // remove row
  541.  
  542. function awRemoveRow(row)
  543. {
  544.   var body = document.getElementById('addressWidgetBody');
  545.  
  546.   awRemoveNodeAndChildren(body, awGetTreeItem(row));
  547.   awFitDummyRows();
  548.  
  549.   top.MAX_RECIPIENTS --;
  550. }
  551.  
  552. function awRemoveNodeAndChildren(parent, nodeToRemove)
  553. {
  554.   // children of nodes
  555.   var childNode;
  556.  
  557.   while ( nodeToRemove.childNodes && nodeToRemove.childNodes.length )
  558.   {
  559.     childNode = nodeToRemove.childNodes[0];
  560.  
  561.     awRemoveNodeAndChildren(nodeToRemove, childNode);
  562.   }
  563.  
  564.   parent.removeChild(nodeToRemove);
  565. }
  566.  
  567. function awSetFocus(row, inputElement)
  568. {
  569.   top.awRow = row;
  570.   top.awInputElement = inputElement;
  571.   top.awFocusRetry = 0;
  572.   setTimeout("_awSetFocus();", 0);
  573. }
  574.  
  575. function _awSetFocus()
  576. {
  577.   var tree = document.getElementById('addressingWidgetTree');
  578.   try
  579.   {
  580.     var theNewRow = awGetTreeRow(top.awRow);
  581.     //temporary patch for bug 26344
  582.     awFinishCopyNode(theNewRow);
  583.  
  584.     //Warning: firstVisibleRow is zero base but top.awRow is one base!
  585.     var firstVisibleRow = tree.getIndexOfFirstVisibleRow();
  586.     var numOfVisibleRows = tree.getNumberOfVisibleRows();
  587.  
  588.     //Do we need to scroll in order to see the selected row?
  589.     if (top.awRow <= firstVisibleRow)
  590.       tree.scrollToIndex(top.awRow - 1);
  591.     else
  592.       if (top.awRow - 1 >= (firstVisibleRow + numOfVisibleRows))
  593.         tree.scrollToIndex(top.awRow - numOfVisibleRows);
  594.  
  595.     top.awInputElement.focus();
  596.   }
  597.   catch(ex)
  598.   {
  599.     top.awFocusRetry ++;
  600.     if (top.awFocusRetry < 3)
  601.     {
  602.       dump("_awSetFocus failed, try it again...\n");
  603.       setTimeout("_awSetFocus();", 0);
  604.     }
  605.     else
  606.       dump("_awSetFocus failed, forget about it!\n");
  607.   }
  608. }
  609.  
  610.  
  611. //temporary patch for bug 26344 & 26528
  612. function awFinishCopyNode(node)
  613. {
  614.     msgCompose.ResetNodeEventHandlers(node);
  615.     return;
  616. }
  617.  
  618.  
  619. function awFinishCopyNodes()
  620. {
  621.   var treeChildren = document.getElementById('addressWidgetBody');
  622.   awFinishCopyNode(treeChildren);
  623. }
  624.  
  625. function awTabFromRecipient(element, event)
  626. {
  627.   //If we are le last element in the tree, we don't want to create a new row.
  628.   if (element == awGetInputElement(top.MAX_RECIPIENTS))
  629.     top.doNotCreateANewRow = true;
  630. }
  631.  
  632. function awGetNumberOfRecipients()
  633. {
  634.     return top.MAX_RECIPIENTS;
  635. }
  636.  
  637. function DragOverTree(event)
  638. {
  639.   var validFlavor = false;
  640.   var dragSession = null;
  641.   var retVal = true;
  642.  
  643.   var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService();
  644.   if (dragService)
  645.     dragService = dragService.QueryInterface(Components.interfaces.nsIDragService);
  646.   if (!dragService) return(false);
  647.  
  648.   dragSession = dragService.getCurrentSession();
  649.   if (!dragSession) return(false);
  650.  
  651.   if (dragSession.isDataFlavorSupported("text/nsabcard")) validFlavor = true;
  652.   //XXX other flavors here...
  653.  
  654.   // touch the attribute on the rowgroup to trigger the repaint with the drop feedback.
  655.   if (validFlavor)
  656.   {
  657.     //XXX this is really slow and likes to refresh N times per second.
  658.     var rowGroup = event.target.parentNode.parentNode;
  659.     rowGroup.setAttribute ( "dd-triggerrepaint", 0 );
  660.     dragSession.canDrop = true;
  661.     // necessary??
  662.     retVal = false; // do not propagate message
  663.   }
  664.   return(retVal);
  665. }
  666.  
  667. function DropOnAddressingWidgetTree(event)
  668. {
  669.   dump("DropOnTree\n");
  670.   var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService();
  671.   if (rdf)
  672.     rdf = rdf.QueryInterface(Components.interfaces.nsIRDFService);
  673.   if (!rdf) return(false);
  674.  
  675.   var dragService = Components.classes["@mozilla.org/widget/dragservice;1"].getService();
  676.   if (dragService)
  677.     dragService = dragService.QueryInterface(Components.interfaces.nsIDragService);
  678.   if (!dragService) return(false);
  679.  
  680.   var dragSession = dragService.getCurrentSession();
  681.   if ( !dragSession ) return(false);
  682.  
  683.   var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  684.   if ( !trans ) return(false);
  685.   trans.addDataFlavor("text/nsabcard");
  686.  
  687.   for ( var i = 0; i < dragSession.numDropItems; ++i )
  688.   {
  689.     dragSession.getData ( trans, i );
  690.     dataObj = new Object();
  691.     bestFlavor = new Object();
  692.     len = new Object();
  693.     trans.getAnyTransferData ( bestFlavor, dataObj, len );
  694.     if ( dataObj )  dataObj = dataObj.value.QueryInterface(Components.interfaces.nsISupportsWString);
  695.     if ( !dataObj ) continue;
  696.  
  697.     // pull the URL out of the data object
  698.     var sourceID = dataObj.data.substring(0, len.value);
  699.     if (!sourceID)  continue;
  700.  
  701.     var cardResource = rdf.GetResource(sourceID);
  702.     var card = cardResource.QueryInterface(Components.interfaces.nsIAbCard);
  703.     var address = "\"" + card.name + "\" <" + card.primaryEmail + ">";
  704.     dump("    Address #" + i + " = " + address + "\n");
  705.  
  706.     DropRecipient(address);
  707.  
  708.   }
  709.  
  710.   return(false);
  711. }
  712.  
  713. function DropRecipient(recipient)
  714. {
  715.     awClickEmptySpace(true);    //that will automatically set the focus on a new available row, and make sure is visible
  716.     var lastInput = awGetInputElement(top.MAX_RECIPIENTS);
  717.     lastInput.value = recipient;
  718.     awAppendNewRow(true);
  719. }
  720.  
  721. function _awSetAutoComplete(selectElem, inputElem)
  722. {
  723.   if (selectElem.value != 'addr_newsgroups' && selectElem.value != 'addr_followup')
  724.     inputElem.disableAutocomplete = false;
  725.   else
  726.     inputElem.disableAutocomplete = true;
  727. }
  728.  
  729. function awSetAutoComplete(rowNumber)
  730. {
  731.     var inputElem = awGetInputElement(rowNumber);
  732.     var selectElem = awGetPopupElement(rowNumber);
  733.     _awSetAutoComplete(selectElem, inputElem)
  734. }
  735.  
  736. function awRecipientTextCommand(userAction, element)
  737. {
  738.   if (userAction == "typing" || userAction == "scrolling")
  739.     awReturnHit(element);
  740. }
  741.  
  742. // Called when an autocomplete session item is selected and the status of
  743. // the session it was selected from is nsIAutoCompleteStatus::failureItems.
  744. //
  745. // As of this writing, the only way that can happen is when an LDAP 
  746. // autocomplete session returns an error to be displayed to the user.
  747. //
  748. // There are hardcoded messages in here, but these are just fallbacks for
  749. // when string bundles have already failed us.
  750. //
  751. function awRecipientErrorCommand(errItem, element)
  752. {
  753.     // remove the angle brackets from the general error message to construct 
  754.     // the title for the alert.  someday we'll pass this info using a real
  755.     // exception object, and then this code can go away.
  756.     //
  757.     var generalErrString;
  758.     if (errItem.value != "") {
  759.     generalErrString = errItem.value.slice(1, errItem.value.length-1);
  760.     } else {
  761.     generalErrString = "Unknown LDAP server problem encountered";
  762.     }    
  763.  
  764.     // try and get the string of the specific error to contruct the complete
  765.     // err msg, otherwise fall back to something generic.  This message is
  766.     // handed to us as an nsISupportsWString in the param slot of the 
  767.     // autocomplete error item, by agreement documented in 
  768.     // nsILDAPAutoCompFormatter.idl
  769.     //
  770.     var specificErrString = "";
  771.     try {
  772.     var specificError = errItem.param.QueryInterface(
  773.         Components.interfaces.nsISupportsWString);
  774.     specificErrString = specificError.data;
  775.     } catch (ex) {
  776.     }
  777.     if (specificErrString == "") {
  778.     specificErrString = "Internal error";
  779.     }
  780.  
  781.     try {
  782.     if (!gPromptService) {
  783.         gPromptService = Components.classes[
  784.         "@mozilla.org/embedcomp/prompt-service;1"].getService().
  785.         QueryInterface(Components.interfaces.nsIPromptService);
  786.     }
  787.     } catch (ex) {
  788.     }
  789.  
  790.     if (gPromptService) {
  791.     gPromptService.alert(window, generalErrString, specificErrString);
  792.     } else {
  793.     window.alert(generalErrString + ": " + specificErrString);
  794.     }
  795. }
  796.  
  797. function awRecipientKeyPress(event, element)
  798. {
  799.   switch(event.keyCode) {
  800.   case 9:
  801.     awTabFromRecipient(element, event);
  802.     break;
  803.   }
  804. }
  805.  
  806. function awRecipientKeyDown(event, element)
  807. {
  808.   switch(event.keyCode) {
  809.   case 46:
  810.   case 8:
  811.     /* do not query directly the value of the text field else the autocomplete widget could potentially
  812.        alter it value while doing some internal cleanup, instead, query the value through the first child
  813.     */
  814.     if (!element.value)
  815.       awDeleteHit(element);
  816.     event.preventBubble();  //We need to stop the event else the tree will receive it and the function
  817.                             //awKeyDown will be executed!
  818.     break;
  819.   }
  820. }
  821.  
  822. function awKeyDown(event, treeElement)
  823. {
  824.   switch(event.keyCode) {
  825.   case 46:
  826.   case 8:
  827.     /* Warning, the treeElement.selectedItems will change everytime we delete a row */
  828.     var selItems = treeElement.selectedItems;
  829.     var length = treeElement.selectedItems.length;
  830.     for (var i = 1; i <= length; i++) {
  831.       var inputs = treeElement.selectedItems[0].getElementsByTagName(awInputElementName());
  832.       if (inputs && inputs.length == 1)
  833.         awDeleteHit(inputs[0]);
  834.     }
  835.     break;
  836.   }
  837. }
  838.  
  839. /* ::::::::::: addressing widget dummy rows ::::::::::::::::: */
  840.  
  841. var gAWContentHeight = 0;
  842. var gAWRowHeight = 0;
  843.  
  844. function awFitDummyRows()
  845. {
  846.   awCalcContentHeight();
  847.   awCreateOrRemoveDummyRows();
  848. }
  849.  
  850. function awCreateOrRemoveDummyRows()
  851. {
  852.   var body = document.getElementById("addressWidgetBody");
  853.   var bodyHeight = body.boxObject.height;
  854.  
  855.   // remove rows to remove scrollbar
  856.   var kids = body.childNodes;
  857.   for (var i = kids.length-1; gAWContentHeight > bodyHeight && i >= 0; --i) {
  858.     if (kids[i].hasAttribute("_isDummyRow")) {
  859.       gAWContentHeight -= gAWRowHeight;
  860.       body.removeChild(kids[i]);
  861.     }
  862.   }
  863.  
  864.   // add rows to fill space
  865.   if (gAWRowHeight) {
  866.     while (gAWContentHeight+gAWRowHeight < bodyHeight) {
  867.       awCreateDummyItem(body);
  868.       gAWContentHeight += gAWRowHeight;
  869.     }
  870.   }
  871. }
  872.  
  873. function awCalcContentHeight()
  874. {
  875.   var body = document.getElementById("addressWidgetBody");
  876.   var kids = body.getElementsByTagName("treerow");
  877.  
  878.   gAWContentHeight = 0;
  879.   if (kids.length > 0) {
  880.     // all rows are forced to a uniform height in xul trees, so
  881.     // find the first tree row with a boxObject and use it as precedent
  882.     var i = 0;
  883.     do {
  884.       gAWRowHeight = kids[i].boxObject.height;
  885.       ++i;
  886.     } while (i < kids.length && !gAWRowHeight);
  887.     gAWContentHeight = gAWRowHeight*kids.length;
  888.   }
  889. }
  890.  
  891. function awCreateDummyItem(aParent)
  892. {
  893.   var titem = document.createElement("treeitem");
  894.   titem.setAttribute("_isDummyRow", "true");
  895.  
  896.   var trow = document.createElement("treerow");
  897.   trow.setAttribute("class", "dummy-row");
  898.   trow.setAttribute("onclick", "awDummyRow_onclick()");
  899.   titem.appendChild(trow);
  900.  
  901.   awCreateDummyCell(trow);
  902.   awCreateDummyCell(trow);
  903.  
  904.   if (aParent)
  905.     aParent.appendChild(titem);
  906.  
  907.   return titem;
  908. }
  909.  
  910. function awCreateDummyCell(aParent)
  911. {
  912.   var cell = document.createElement("treecell");
  913.   cell.setAttribute("class", "treecell-addressingWidget dummy-row-cell");
  914.   if (aParent)
  915.     aParent.appendChild(cell);
  916.  
  917.   return cell;
  918. }
  919.  
  920. function awDummyRow_onclick() {
  921.   // pass click event back to handler
  922.   awClickEmptySpace(document.getElementById("addressWidgetBody"), true);
  923. }
  924.  
  925. function awGetNextDummyRow()
  926. {
  927.   // gets the next row from the top down
  928.   var body = document.getElementById("addressWidgetBody");
  929.   var kids = body.childNodes;
  930.   for (var i = 0; i < kids.length; ++i) {
  931.     if (kids[i].hasAttribute("_isDummyRow"))
  932.       return kids[i];
  933.   }
  934.   return null;
  935. }
  936.  
  937. function awSizerListen()
  938. {
  939.   // when splitter is clicked, fill in necessary dummy rows each time the mouse is moved
  940.   awCalcContentHeight(); // precalculate
  941.   document.addEventListener("mousemove", awSizerMouseMove, true);
  942.   document.addEventListener("mouseup", awSizerMouseUp, false);
  943. }
  944.  
  945. function awSizerMouseMove()
  946. {
  947.   awCreateOrRemoveDummyRows();
  948. }
  949.  
  950. function awSizerMouseUp()
  951. {
  952.   document.removeEventListener("mousemove", awSizerMouseUp, false);
  953.   document.removeEventListener("mouseup", awSizerMouseUp, false);
  954. }
  955.  
  956.