1 /* ***** BEGIN LICENSE BLOCK *****
  2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3  *
  4  * The contents of this file are subject to the Mozilla Public License Version
  5  * 1.1 (the "License"); you may not use this file except in compliance with
  6  * the License. You may obtain a copy of the License at
  7  * http://www.mozilla.org/MPL/
  8  *
  9  * Software distributed under the License is distributed on an "AS IS" basis,
 10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11  * for the specific language governing rights and limitations under the
 12  * License.
 13  *
 14  * The Original Code is gContactSync.
 15  *
 16  * The Initial Developer of the Original Code is
 17  * Josh Geenen <gcontactsync@pirules.org>.
 18  * Portions created by the Initial Developer are Copyright (C) 2008-2009
 19  * the Initial Developer. All Rights Reserved.
 20  *
 21  * Contributor(s):
 22  *
 23  * Alternatively, the contents of this file may be used under the terms of
 24  * either the GNU General Public License Version 2 or later (the "GPL"), or
 25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 26  * in which case the provisions of the GPL or the LGPL are applicable instead
 27  * of those above. If you wish to allow use of your version of this file only
 28  * under the terms of either the GPL or the LGPL, and not to allow others to
 29  * use your version of this file under the terms of the MPL, indicate your
 30  * decision by deleting the provisions above and replace them with the notice
 31  * and other provisions required by the GPL or the LGPL. If you do not delete
 32  * the provisions above, a recipient may use your version of this file under
 33  * the terms of any one of the MPL, the GPL or the LGPL.
 34  *
 35  * ***** END LICENSE BLOCK ***** */
 36 
 37 if (!com) var com = {}; // A generic wrapper variable
 38 // A wrapper for all GCS functions and variables
 39 if (!com.gContactSync) com.gContactSync = {};
 40 
 41 window.addEventListener("load",
 42   /** Initializes the CardDialogOverlay when the window has finished loading. */
 43   function gCS_CardDialogOverlayLoadListener(e) {
 44     com.gContactSync.CardDialogOverlay.init();
 45   },
 46 false);
 47 /**
 48  * Attributes added to TB by gContactSync AND present in the card dialog overlay
 49  */
 50 com.gContactSync.gAttributes = {
 51   "ThirdEmail":           {}, 
 52   "FourthEmail":          {},
 53   "TalkScreenName":       {},
 54   "JabberScreenName":     {},
 55   "YahooScreenName":      {},
 56   "MSNScreenName":        {},
 57   "ICQScreenName":        {},
 58   "HomeFaxNumber":        {},
 59   "OtherNumber":          {},
 60   "PrimaryEmailType":     {},
 61   "SecondEmailType":      {},
 62   "ThirdEmailType":       {},
 63   "FourthEmailType":      {},
 64   "_AimScreenNameType":   {},
 65   "TalkScreenNameType":   {},
 66   "JabberScreenNameType": {},
 67   "YahooScreenNameType":  {},
 68   "MSNScreenNameType":    {},
 69   "ICQScreenNameType":    {},
 70   "WorkPhoneType":        {},
 71   "HomePhoneType":        {},
 72   "FaxNumberType":        {},
 73   "CellularNumberType":   {},
 74   "PagerNumberType":      {},
 75   "HomeFaxNumberType":    {},
 76   "OtherNumberType":      {},
 77   "Relation0":            {},
 78   "Relation0Type":        {},
 79   "Relation1":            {},
 80   "Relation1Type":        {},
 81   "Relation2":            {},
 82   "Relation2Type":        {},
 83   "Relation3":            {},
 84   "Relation3Type":        {},
 85   "WebPage1Type":         {},
 86   "WebPage2Type":         {},
 87   "FullHomeAddress":      {},
 88   "FullWorkAddress":      {},
 89   "OtherAddress":         {}
 90 };
 91 /**
 92  * Adds a tab to the tab box in the New and Edit Card Dialogs.  Using JavaScript
 93  * is necessary because the tab box doesn't have an ID.
 94  * @class
 95  */
 96 com.gContactSync.CardDialogOverlay = {
 97   /** The XUL namespace */
 98   mNamespace:  "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
 99   /** The number of times an attempt was made to initialize the dialog */
100   mLoadNumber: 0,
101   /** This stores whether the contact is read-only (ie from LDAP or the Mac AB) */
102   mDisabled:   false,
103   /**
104    * Adds a tab to the tab box, if possible.  Waits until the abCardOverlay is
105    * loaded.
106    */
107   init: function CardDialogOverlay_init() {
108     // if it isn't finished loading yet wait another 200 ms and try again
109     if (!document.getElementById("abTabs")) {
110       // if it has tried to load more than 50 times something is wrong, so quit
111       if (com.gContactSync.CardDialogOverlay.mLoadNumber < 50)
112         setTimeout(com.gContactSync.CardDialogOverlay.init, 200);
113       com.gContactSync.CardDialogOverlay.mLoadNumber++;
114       return;
115     }
116 
117     try {
118       // QI the card if it doesn't have the getProperty method
119       // if the card cannot accept custom attributes, quit and do not add the
120       // extra tabs
121       if (!gEditCard.card.getProperty)
122         gEditCard.card.QueryInterface(Components.interfaces.nsIAbMDBCard);
123     }
124     catch (e) {
125       document.getElementById("gContactSyncTab").collapsed = true;
126       return;
127     }
128     // some contacts are read-only so extra attributes should be disabled for
129     // those cards (see Mozdev Bug 20169)
130     try {
131       com.gContactSync.CardDialogOverlay.mDisabled = document.getElementById("PreferMailFormatPopup").disabled;
132     }
133     catch (ex) {
134       com.gContactSync.alertError("Error while determining if contact is read-only: " + ex);
135       com.gContactSync.CardDialogOverlay.mDisabled = true;
136     }
137     // add the email type drop down menus
138     try {
139       // add the type for Postbox (this does nothing for TB or Seamonkey)
140       if (!this.addPostboxEmailType(document.getElementById("PrimaryEmail"))) {
141         var emailTypes       = com.gContactSync.gdata.contacts.EMAIL_TYPES,
142             primaryEmailBox  = this.getBox("PrimaryEmail"),
143             secondEmailBox   = this.getBox("SecondEmail"),
144             thirdEmailBox    = this.getBox("ThirdEmail"),
145             fourthEmailBox   = this.getBox("FourthEmail");
146         // add the type menulist to e-mail elements if this isn't Postbox
147         this.addMenuItems(primaryEmailBox, emailTypes, "PrimaryEmailType", "other");
148         this.addMenuItems(secondEmailBox,  emailTypes, "SecondEmailType",  "other");
149         this.addMenuItems(thirdEmailBox,   emailTypes, "ThirdEmailType",   "other");
150         this.addMenuItems(fourthEmailBox,  emailTypes, "FourthEmailType",  "other");
151       }
152       else {
153         document.getElementById("additionalEmailBox").collapsed = true;
154       }
155     }
156     catch (ex0) {
157       com.gContactSync.alertError("Unable to setup email types: " + ex0);
158     }
159     try {
160       // add drop down menus for screen name protocols
161       var imTypes   = com.gContactSync.gdata.contacts.IM_TYPES,
162           aimBox    = this.getBox("ScreenName"),
163           talkBox   = this.getBox("TalkScreenName"),
164           yahooBox  = this.getBox("YahooScreenName"),
165           icqBox    = this.getBox("ICQScreenName"),
166           msnBox    = this.getBox("MSNScreenName"),
167           jabberBox = this.getBox("JabberScreenName");
168       this.addMenuItems(aimBox,    imTypes, "_AimScreenNameType",   "AIM");
169       this.addMenuItems(talkBox,   imTypes, "TalkScreenNameType",   "GOOGLE_TALK");
170       this.addMenuItems(icqBox,    imTypes, "ICQScreenNameType",    "ICQ");
171       this.addMenuItems(yahooBox,  imTypes, "YahooScreenNameType",  "YAHOO");
172       this.addMenuItems(msnBox,    imTypes, "MSNScreenNameType",    "MSN");
173       this.addMenuItems(jabberBox, imTypes, "JabberScreenNameType", "JABBER");
174     }
175     catch (ex1) {
176       com.gContactSync.alertError("Unable to setup screen name protocol menus\n" + ex1);
177     }
178     var newDialog      = false, // post-Mailnews Core Bug 63941
179         showPhoneTypes = com.gContactSync.Preferences.mSyncPrefs.phoneTypes.value,
180         swap           = com.gContactSync.Preferences.mSyncPrefs.swapMobilePager.value,
181         work           = document.getElementById("WorkPhone"),
182         home           = document.getElementById("HomePhone"),
183         fax            = document.getElementById("FaxNumber"),
184         pager          = document.getElementById("PagerNumber"),
185         mobile         = document.getElementById("CellularNumber"),
186         workLabel      = work.parentNode.previousSibling;
187     // then replace all phone labels and remove the access keys
188     if (!workLabel) {
189       newDialog = true;
190       workLabel = work.previousSibling;
191     }
192     if (showPhoneTypes) {
193       if (swap) {
194         try {
195           // swap pager and mobile phone textboxes and values
196           pager = document.getElementById("PagerNumber");
197           pager.setAttribute("id", "tmp");
198           var pagerValue = pager.value,
199               mobile     = document.getElementById("CellularNumber");
200           mobile.setAttribute("id", "PagerNumber");
201           pager.setAttribute("id", "CellularNumber");
202           pager.value = mobile.value;
203           mobile.value = pagerValue;
204         }
205         catch (e1) {
206           com.gContactSync.alertError("Unable to swap pager and mobile number values\n" + e1);
207         }
208       }
209 
210       try {
211         workLabel.value = com.gContactSync.StringBundle.getStr("first");
212         workLabel.setAttribute("accesskey", "");
213         var homeLabel = newDialog ? home.previousSibling
214                                   : home.parentNode.previousSibling;
215         homeLabel.value = com.gContactSync.StringBundle.getStr("second");
216         homeLabel.setAttribute("accesskey", "");
217         var faxLabel = newDialog ? fax.previousSibling
218                                  : fax.parentNode.previousSibling;
219         faxLabel.value = com.gContactSync.StringBundle.getStr("third");
220         faxLabel.setAttribute("accesskey", "");
221         pager = document.getElementById("PagerNumber");
222         var pagerLabel = newDialog ? pager.previousSibling
223                                    : pager.parentNode.previousSibling;
224         pagerLabel.value = com.gContactSync.StringBundle.getStr(swap ? "fifth" : "fourth");
225         pagerLabel.setAttribute("accesskey", "");
226         mobile = document.getElementById("CellularNumber");
227         var mobileLabel = newDialog ? mobile.previousSibling
228                                     : mobile.parentNode.previousSibling;
229         mobileLabel.value = com.gContactSync.StringBundle.getStr(swap ? "fourth" : "fifth");
230         mobileLabel.setAttribute("accesskey", "");
231       }
232       catch (ex2) {
233         com.gContactSync.alertError("Unable to replace phone labels and remove access keys\n" + ex2);
234       }
235     }
236     else {
237       // TODO - replace the Sixth and Seventh labels
238     }
239     var phoneTypes = com.gContactSync.gdata.contacts.PHONE_TYPES;
240     try {
241       // Add a Google Voice menuitem
242       phoneTypes.push("grandcentral");
243       
244       // setup the types for the phone numbers
245       var workBox = work.parentNode;
246       this.addMenuItems(workBox, phoneTypes, "WorkPhoneType", "work")
247           .collapsed = !showPhoneTypes;
248       var homeBox = home.parentNode;
249       this.addMenuItems(homeBox, phoneTypes, "HomePhoneType", "home")
250           .collapsed = !showPhoneTypes;
251       var faxBox = fax.parentNode;
252       this.addMenuItems(faxBox, phoneTypes, "FaxNumberType", "work_fax")
253           .collapsed = !showPhoneTypes;
254       var mobileBox = mobile.parentNode;
255       this.addMenuItems(mobileBox, phoneTypes, "CellularNumberType", "mobile")
256           .collapsed = !showPhoneTypes;
257       var pagerBox = pager.parentNode;
258       this.addMenuItems(pagerBox, phoneTypes, "PagerNumberType", "pager")
259           .collapsed = !showPhoneTypes;
260       var homeFaxBox = document.getElementById("HomeFaxNumber").parentNode;
261       this.addMenuItems(homeFaxBox, phoneTypes, "HomeFaxNumberType", "home_fax")
262           .collapsed = !showPhoneTypes;
263       var otherNumberBox = document.getElementById("OtherNumber").parentNode;
264       this.addMenuItems(otherNumberBox, phoneTypes, "OtherNumberType", "other")
265           .collapsed = !showPhoneTypes;
266     }
267     catch (ex3) {
268       com.gContactSync.alertError("Unable to setup phone number types\n" + ex3);
269     }
270     
271     // Add the website types
272     var websiteTypes = com.gContactSync.gdata.contacts.WEBSITE_TYPES;
273     var site1Box = document.getElementById("WebPage1").parentNode;
274     this.addMenuItems(site1Box, websiteTypes, "WebPage1Type", "work");
275     var site2Box = document.getElementById("WebPage2").parentNode;
276     this.addMenuItems(site2Box, websiteTypes, "WebPage2Type", "home");
277     if (newDialog) {
278       // rename the hidden phone number field IDs
279       try {
280         document.getElementById("HomeFaxNumber").id     = "OldHomeFaxNumber";
281         document.getElementById("HomeFaxNumberType").id = "OldHomeFaxNumberType";
282         document.getElementById("OtherNumber").id       = "OldOtherNumber";
283         document.getElementById("OtherNumberType").id   = "OldOtherNumberType";
284       }
285       catch (e) {}
286       try {
287         // change the width of the phone numbers
288         var phoneIDs = ["HomePhone", "WorkPhone", "CellularNumber", "FaxNumber",
289                         "PagerNumber"];
290         for (var i = 0; i < phoneIDs.length; i++) {
291           var elem = document.getElementById(phoneIDs[i]);
292           if (!elem) continue;
293           elem.setAttribute("width", "150px");
294         }
295         // add the sixth and seventh numbers below 1 - 5
296         var sixthNum   = this.setupNumBox("HomeFaxNumber",
297                                           com.gContactSync.StringBundle.getStr("sixth")),
298             seventhNum = this.setupNumBox("OtherNumber",
299                                      com.gContactSync.StringBundle.getStr("seventh"));
300         pager.parentNode.parentNode.appendChild(sixthNum);
301         this.addMenuItems(sixthNum, phoneTypes, "HomeFaxNumberType", "home_fax")
302           .collapsed = !showPhoneTypes;
303         pager.parentNode.parentNode.appendChild(seventhNum);
304         this.addMenuItems(seventhNum, phoneTypes, "OtherNumberType", "other")
305           .collapsed = !showPhoneTypes;
306         
307         // Add the relation fields
308         try {
309           document.getElementById("relationFields").removeAttribute("hidden");
310           var relationTypes = [""];
311           // copy the relation types over
312           for (i in com.gContactSync.gdata.contacts.RELATION_TYPES) {
313             relationTypes.push(i);
314           }
315           for (i = 0; i < 4; i++) {
316             var relationBox = document.getElementById("Relation" + i + "Box");
317             this.addMenuItems(relationBox, relationTypes, "Relation" + i + "Type", "", com.gContactSync.StringBundle.getStr("relationWidth"));
318           }
319         }
320         catch (ex5) {
321           com.gContactSync.LOGGER.LOG_WARNING("Could not add the relation fields.", ex5);
322         }
323         /*
324         var nameWidth = "width: 30ch;";
325         document.getElementById("FirstName").setAttribute("style", nameWidth);
326         document.getElementById("LastName").setAttribute("style", nameWidth);
327         document.getElementById("DisplayName").setAttribute("style", nameWidth);
328         document.getElementById("NickName").setAttribute("style", nameWidth);
329         document.getElementById("FirstName").removeAttribute("flex");
330         document.getElementById("LastName").removeAttribute("flex");
331         document.getElementById("DisplayName").removeAttribute("flex");
332         document.getElementById("NickName").removeAttribute("flex");
333         var emailWidth = "width: 20ch;";
334         document.getElementById("PrimaryEmail").setAttribute("style", emailWidth);
335         document.getElementById("SecondEmail").setAttribute("style", emailWidth);
336         document.getElementById("ScreenName").setAttribute("width", "150px");
337         document.getElementById("PrimaryEmail").removeAttribute("flex");
338         document.getElementById("SecondEmail").removeAttribute("flex");
339         document.getElementById("ScreenName").removeAttribute("flex");
340         document.getElementById("abNameTab").firstChild.firstChild.style.minWidth = "50ch";
341         document.getElementById("abNameTab").firstChild.firstChild.style.maxWidth = "50ch";
342         var elem = document.getElementById("abTabPanels");
343         elem.style.width = "850px";
344         elem.style.maxWidth = "850px";
345         elem.style.minWidth = "850px";
346         */
347       }
348       catch (ex6) {
349         com.gContactSync.alertError("Unable to setup the extra tabs\n" + ex6);
350       }
351     }
352     // if this is the old dialog, show the extra phone numbers
353     else {
354       try {
355         // move the address descriptions below the addresses (rather than beside)
356         var gbox = document.getElementById("addressDescGroupBox");
357         if (gbox) {
358           var parent = gbox.parentNode;
359           parent.removeChild(gbox);
360           parent.parentNode.firstChild.appendChild(gbox);
361         }
362         document.getElementById("numbersGroupBox").removeAttribute("hidden");
363       }
364       catch (e) {
365         com.gContactSync.LOGGER.LOG_WARNING("Unable to move addressDescGroupBox" +
366                                             " or remove hidden from numbersGB", e);
367       }
368     }
369     
370     // if this is a read-only card, make added elements disabled
371     // the menulists are already taken care of
372     // TODO update CardDialogOverlay...
373     if (com.gContactSync.CardDialogOverlay.mDisabled) {
374       document.getElementById("ThirdEmail").readOnly       = true;
375       document.getElementById("FourthEmail").readOnly      = true;
376       document.getElementById("TalkScreenName").readOnly   = true;
377       document.getElementById("ICQScreenName").readOnly    = true;
378       document.getElementById("YahooScreenName").readOnly  = true;
379       document.getElementById("MSNScreenName").readOnly    = true;
380       document.getElementById("JabberScreenName").readOnly = true;
381       document.getElementById("HomeFaxNumber").readOnly    = true;
382       document.getElementById("OtherNumber").readOnly      = true;
383       document.getElementById("Relation").readOnly         = true;
384     }
385     // fix the width of the dialog
386     window.sizeToContent();
387 
388     // override the check and set card values function
389     com.gContactSync.originalCheckAndSetCardValues = CheckAndSetCardValues;
390     CheckAndSetCardValues = com.gContactSync.CardDialogOverlay.CheckAndSetCardValues;
391     // get the extra card values
392     this.GetCardValues(gEditCard.card, document);
393   },
394   /**
395    * Gets the parent node of an element with the given ID.
396    * If there is no element with the given ID then this function will return
397    * null.
398    * @param aID The ID of the element whose parent node is returned.
399    * @returns {XULElement} The parentNode of the element with the given ID.
400    */
401   getBox: function CardDialogOverlay_getBox(aID) {
402     var elem = document.getElementById(aID);
403     if (elem && elem.tagName === "emailaddress-input") { // Postbox
404       return elem;
405     }
406     return elem ? elem.parentNode : null;
407   },
408   /**
409    * Adds the e-mail type menulist to Postbox's emailaddress-input element.
410    * This also overrides the addRow method to add the type menulist to
411    * future emailaddress-input elements (and calls the original addRow)
412    * @param aElem The emailaddress-input element.
413    * @returns {boolean} True if the application is Postbox and the menulist was
414    *                    added.
415    */
416   addPostboxEmailType: function CardDialogOverlay_addPBEmailType(aElem) {
417     if (!aElem || aElem.tagName !== "emailaddress-input") {
418       return false;
419     }
420     /*
421     var emailTypes = com.gContactSync.gdata.contacts.EMAIL_TYPES;
422     this.addMenuItems(aElem, emailTypes, "", "other");
423     // save the original addRow method
424     aElem.origAddRow = aElem.addRow;
425     // override addRow to call the original then add the e-mail types
426     aElem.addRow = function gContactSync_addRow() {
427       // call the original
428       this.origAddRow.apply(this, arguments);
429       // add the e-mail type menulist
430       com.gContactSync.CardDialogOverlay.addPostboxEmailType(this.nextSibling);
431       // resize the window
432       window.sizeToContent();
433     }
434     */
435     return true;
436   },
437   /**
438    * Sets the attributes added by this extension as the value in the textbox or
439    * drop down menu in aDoc whose ID is identical to the attribute's name.
440    * Calls the original CheckAndSetCardValues function when finished.
441    * @param aCard  {nsIAbCard} The card to set the values for.
442    * @param aDoc   {Document Object} The document.
443    * @param aCheck Unused, but passed to the original method.
444    */
445   CheckAndSetCardValues: function CardDialogOverlay_CheckAndSetCardValues(aCard, aDoc, aCheck) {
446     // make sure the required data is present (abCardOverlay.js)
447     if (!CheckCardRequiredDataPresence(aDoc)) {
448       return false;
449     }
450     var contact = new com.gContactSync.TBContact(aCard);
451     var existingTypes = {
452       "WorkPhoneType":      {},
453       "HomePhoneType":      {},
454       "FaxNumberType":      {},
455       "CellularNumberType": {},
456       "PagerNumberType":    {}
457     };
458     // iterate through all the added attributes and types and set the card's value
459     // for each one of them
460     for (var attr in com.gContactSync.gAttributes) {
461       try {
462         // if the element exists, set the card's value as its value
463         var elem = aDoc.getElementById(attr);
464         if (elem) {
465           // I do not know why this is necessary, but it seems to be the only
466           // way to get the value correct in TB 2...
467           if (attr === "HomeFaxNumberType" || attr === "OtherNumberType") {
468             elem.value = elem.getAttribute("value");
469           }
470           com.gContactSync.LOGGER.VERBOSE_LOG("Attribute: '" + attr + "' - Value: '" + elem.value + "'");
471           contact.setValue(attr, elem.value);
472         }
473       }
474       catch (e) {
475         com.gContactSync.alertError("Error in com.gContactSync.CheckAndSetCardValues: " + attr + "\n" + e);
476       }
477     }
478     if (!contact.mContact.getProperty && gEditCard.abURI) {
479       if (contact.mContact.editCardToDatabase) { // Thunderbird 2
480         contact.mContact.editCardToDatabase(gEditCard.abURI);
481       }
482       else if (GetDirectoryFromURI) { // Postbox doesn't have editCardToDatabase
483         var dir = GetDirectoryFromURI(gEditCard.abURI);
484         if (dir) {
485           dir.modifyCard(contact.mContact);
486         }
487       }
488     }
489     // ensure that every contact edited through this dialog has at least a dummy
490     // e-mail address if necessary
491     var primEmailElem = aDoc.getElementById("PrimaryEmail");
492     if (!primEmailElem.value) {
493       // if it is a new contact it isn't already in any lists
494       if (gEditCard.abURI) {
495       // Check if it is in any mailing lists.  If so, force a dummy address
496       // When fetching lists, do not get the contacts (if it is found there is
497       // no need to get the contacts in every list)
498         var ab    = com.gContactSync.GAbManager.getAbByURI(gEditCard.abURI),
499             ab    = (ab ? new com.gContactSync.GAddressBook(ab) : null),
500             lists = ab.getAllLists(true);
501         for (var i in lists) {
502           // if the list does have the contact then make sure it gets a dummy
503           // e-mail address regardless of the preference
504           // do not check the PrimaryEmail address in hasContact since it is now
505           // empty
506           if (lists[i].hasContact(contact)) {
507             primEmailElem.value = com.gContactSync.makeDummyEmail(contact.mContact, true);
508             com.gContactSync.alert(com.gContactSync.StringBundle.getStr("dummyEmailAdded") + "\n" + primEmailElem.value);
509             break;
510           }
511         }
512       }
513     }
514     // call the original and return its return value
515     return com.gContactSync.originalCheckAndSetCardValues.apply(this, arguments);
516   },
517   /**
518    * A method that gets all of the attributes added by this extension and sets
519    * the value of the textbox or drop down menu in aDoc whose ID is identical to
520    * the attribute's name.
521    * @param aCard {nsIAbCard} The card to get the values from.
522    * @param aDoc  {Document Object} The document.
523    */
524   GetCardValues: function CardDialogOverlay_GetCardValues(aCard, aDoc) {
525     // iterate through all the added type elements and get the card's value for
526     // each one of them to set as the value for the element
527     for (var attr in com.gContactSync.gAttributes) {
528       try {
529         var elem = aDoc.getElementById(attr);
530         // if the element exists, set its value as the card's value
531         if (elem) {
532           var value;
533           if (aCard.getProperty) // post Bug 413260
534             value = aCard.getProperty(attr, null);
535           else // pre Bug 413260
536             value = aCard.getStringAttribute(attr);
537           // set the element's value if attr isn't a type OR it is a type and
538           // the card's value for the attribute isn't null or blank
539           if (attr.indexOf("Type") == -1 || (value && value != "")) {
540             elem.value = value;
541             // If it is a menulist and the label is still blank (ie custom label)
542             // then add it as a new menuitem
543             if (elem.tagName === "menulist" && !elem.label) {
544               var item = document.createElement("menuitem");
545               item.setAttribute("value", value);
546               item.setAttribute("label", value);
547               elem.menupopup.appendChild(item);
548               elem.value = value;
549             }
550           }
551         }
552       } catch (e) { com.gContactSync.alertError("Error in com.gContactSync.GetCardValues: " + attr + "\n" + e); }
553     }
554   
555     if (com.gContactSync.isDummyEmail(aDoc.getElementById("PrimaryEmail").value))
556       aDoc.getElementById("PrimaryEmail").value = null;
557   },
558   /**
559    * Sets up a type menu list element with a menuitem for each string in the
560    * array.
561    * @param aBox   {XUL Box} The box element to which this menu list is added.
562    * @param aArray {array}  The array of values to set for the menuitems.  There
563    *                        must be a string in the string bundle with the same
564    *                        name as the value.
565    * @param aID    {string} The ID for this menu list, which should be the name
566    *                        of the attribute with Type added to the end, such as
567    *                        WorkNumberType
568    * @param aValue {string} The default value to set for this list.
569    * @param aWidth {int}    The maximum width, if any.
570    *
571    * @returns {XULElement}  The menulist element.
572    */
573   addMenuItems: function CardDialogOverlay_addMenuItems(aBox, aArray, aID, aValue, aWidth) {
574     if (!aBox) {
575       return false;
576     }
577     var menuList = document.createElement("menulist");
578     menuList.setAttribute("id", aID);
579     var menuPopup = document.createElement("menupopup");
580     // put the default value first in the menupopup, if possible
581     var index = aArray.indexOf(aValue);
582     var elem;
583     // Make sure the default value is in aArray
584     if (index == -1) {
585       aArray.push(aValue);
586       index = aArray.length - 1;
587     }
588     if (index > -1 && index < aArray.length) {
589       elem = document.createElement("menuitem");
590       elem.setAttribute("value", aValue);
591       elem.setAttribute("label", com.gContactSync.StringBundle.getStr(aValue ? aValue : "blank"));
592       aArray[index] = null;
593       menuPopup.appendChild(elem);
594     }
595     // then add the other values
596     for (var i = 0; i < aArray.length; i++) {
597       if (!aArray[i]) { // if this element is null it was the default value
598         aArray[i] = aValue; // so restore its value and skip adding it again
599         continue;
600       }
601       elem = document.createElement("menuitem");
602       elem.setAttribute("value", aArray[i]);
603       elem.setAttribute("label", com.gContactSync.StringBundle.getStr(aArray[i]));
604       menuPopup.appendChild(elem);
605     }
606     menuList.setAttribute("sizetopopup", "always");
607     if (aWidth) {
608       menuList.setAttribute("width", aWidth);
609       menuList.style.width = aWidth;
610       menuList.style.maxWidth = aWidth;
611     }
612     // add the popup to the menu list
613     menuList.appendChild(menuPopup);
614     // disable the menu list if this card is read-only
615     menuList.setAttribute("disabled", com.gContactSync.CardDialogOverlay.mDisabled);
616     // add the menu list to the box
617     aBox.appendChild(menuList);
618     return menuList;
619   },
620   /**
621    * Adds an hbox containing a label and textbox for a phone number.
622    * @param aID    {string} The ID for the textbox.
623    * @param aLabel {string} The text for the textbox's label.
624    */
625   setupNumBox: function CardDialogOverlay_setupNumBox(aID, aLabel) {
626     var box = document.createElement("hbox");
627     box.setAttribute("align", "center");
628     var spacer = document.createElement("spacer");
629     spacer.setAttribute("flex", 1);
630     box.appendChild(spacer);
631     var label = document.createElement("label");
632     label.setAttribute("control", aID);
633     label.setAttribute("value", aLabel);
634     box.appendChild(label);
635     var textbox = document.createElement("textbox");
636     textbox.setAttribute("id", aID);
637     textbox.setAttribute("class", "PhoneEditWidth");
638     if (com.gContactSync.CardDialogOverlay.mDisabled)
639       textbox.setAttribute("readonly", true);
640     else if (textbox.hasAttribute("readonly"))
641       textbox.removeAttribute("readonly");
642     textbox.setAttribute("width", "150px");
643     box.appendChild(textbox);
644     return box;
645   }
646 };
647