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 pager;
179     try {
180       // swap pager and mobile phone textboxes and values
181       pager = document.getElementById("PagerNumber");
182       pager.setAttribute("id", "tmp");
183       var pagerValue = pager.value,
184           mobile     = document.getElementById("CellularNumber");
185       mobile.setAttribute("id", "PagerNumber");
186       pager.setAttribute("id", "CellularNumber");
187       pager.value = mobile.value;
188       mobile.value = pagerValue;
189     }
190     catch (e1) {
191       com.gContactSync.alertError("Unable to swap pager and mobile number values\n" + e1);
192     }
193     var newDialog      = false, // post-Mailnews Core Bug 63941
194         showPhoneTypes = com.gContactSync.Preferences.mSyncPrefs.phoneTypes.value,
195         work           = document.getElementById("WorkPhone"),
196         home           = document.getElementById("HomePhone"),
197         fax            = document.getElementById("FaxNumber");
198     mobile             = document.getElementById("CellularNumber");
199     pager              = document.getElementById("PagerNumber");
200     // then replace all phone labels and remove the access keys
201     var workLabel = work.parentNode.previousSibling;
202     if (!workLabel) {
203       newDialog = true;
204       workLabel = work.previousSibling;
205     }
206     if (showPhoneTypes) {
207       try {
208         workLabel.value = com.gContactSync.StringBundle.getStr("first");
209         workLabel.setAttribute("accesskey", "");
210         var homeLabel = newDialog ? home.previousSibling
211                                   : home.parentNode.previousSibling;
212         homeLabel.value = com.gContactSync.StringBundle.getStr("second");
213         homeLabel.setAttribute("accesskey", "");
214         var faxLabel = newDialog ? fax.previousSibling
215                                  : fax.parentNode.previousSibling;
216         faxLabel.value = com.gContactSync.StringBundle.getStr("third");
217         faxLabel.setAttribute("accesskey", "");
218         var mobileLabel = newDialog ? mobile.previousSibling
219                                     : mobile.parentNode.previousSibling;
220         mobileLabel.value = com.gContactSync.StringBundle.getStr("fourth");
221         mobileLabel.setAttribute("accesskey", "");
222         var pagerLabel = newDialog ? pager.previousSibling
223                                    : pager.parentNode.previousSibling;
224         pagerLabel.value = com.gContactSync.StringBundle.getStr("fifth");
225         pagerLabel.setAttribute("accesskey", "");
226       }
227       catch (ex2) {
228         com.gContactSync.alertError("Unable to replace phone labels and remove access keys\n" + ex2);
229       }
230     }
231     else {
232       // TODO - replace the Sixth and Seventh labels
233     }
234     var phoneTypes = com.gContactSync.gdata.contacts.PHONE_TYPES;
235     try {
236       // setup the types for the phone numbers
237       var workBox = work.parentNode;
238       this.addMenuItems(workBox, phoneTypes, "WorkPhoneType", "work")
239           .collapsed = !showPhoneTypes;
240       var homeBox = home.parentNode;
241       this.addMenuItems(homeBox, phoneTypes, "HomePhoneType", "home")
242           .collapsed = !showPhoneTypes;
243       var faxBox = fax.parentNode;
244       this.addMenuItems(faxBox, phoneTypes, "FaxNumberType", "work_fax")
245           .collapsed = !showPhoneTypes;
246       var mobileBox = mobile.parentNode;
247       this.addMenuItems(mobileBox, phoneTypes, "CellularNumberType", "mobile")
248           .collapsed = !showPhoneTypes;
249       var pagerBox = pager.parentNode;
250       this.addMenuItems(pagerBox, phoneTypes, "PagerNumberType", "pager")
251           .collapsed = !showPhoneTypes;
252       var homeFaxBox = document.getElementById("HomeFaxNumber").parentNode;
253       this.addMenuItems(homeFaxBox, phoneTypes, "HomeFaxNumberType", "home_fax")
254           .collapsed = !showPhoneTypes;
255       var otherNumberBox = document.getElementById("OtherNumber").parentNode;
256       this.addMenuItems(otherNumberBox, phoneTypes, "OtherNumberType", "other")
257           .collapsed = !showPhoneTypes;
258     }
259     catch (ex3) {
260       com.gContactSync.alertError("Unable to setup phone number types\n" + ex3);
261     }
262     
263     // Add the website types
264     var websiteTypes = com.gContactSync.gdata.contacts.WEBSITE_TYPES;
265     var site1Box = document.getElementById("WebPage1").parentNode;
266     this.addMenuItems(site1Box, websiteTypes, "WebPage1Type", "work");
267     var site2Box = document.getElementById("WebPage2").parentNode;
268     this.addMenuItems(site2Box, websiteTypes, "WebPage2Type", "home");
269     if (newDialog) {
270       // rename the hidden phone number field IDs
271       try {
272         document.getElementById("HomeFaxNumber").id     = "OldHomeFaxNumber";
273         document.getElementById("HomeFaxNumberType").id = "OldHomeFaxNumberType";
274         document.getElementById("OtherNumber").id       = "OldOtherNumber";
275         document.getElementById("OtherNumberType").id   = "OldOtherNumberType";
276       }
277       catch (e) {}
278       try {
279         // change the width of the phone numbers
280         var phoneIDs = ["HomePhone", "WorkPhone", "CellularNumber", "FaxNumber",
281                         "PagerNumber"];
282         for (var i = 0; i < phoneIDs.length; i++) {
283           var elem = document.getElementById(phoneIDs[i]);
284           if (!elem) continue;
285           elem.setAttribute("width", "150px");
286         }
287         // add the sixth and seventh numbers below 1 - 5
288         var sixthNum   = this.setupNumBox("HomeFaxNumber",
289                                           com.gContactSync.StringBundle.getStr("sixth")),
290             seventhNum = this.setupNumBox("OtherNumber",
291                                      com.gContactSync.StringBundle.getStr("seventh"));
292         pager.parentNode.parentNode.appendChild(sixthNum);
293         this.addMenuItems(sixthNum, phoneTypes, "HomeFaxNumberType", "home_fax")
294           .collapsed = !showPhoneTypes;
295         pager.parentNode.parentNode.appendChild(seventhNum);
296         this.addMenuItems(seventhNum, phoneTypes, "OtherNumberType", "other")
297           .collapsed = !showPhoneTypes;
298         
299         // Add the relation fields
300         try {
301           document.getElementById("relationFields").removeAttribute("hidden");
302           var relationTypes = [""];
303           // copy the relation types over
304           for (i in com.gContactSync.gdata.contacts.RELATION_TYPES) {
305             relationTypes.push(i);
306           }
307           for (i = 0; i < 4; i++) {
308             var relationBox = document.getElementById("Relation" + i + "Box");
309             this.addMenuItems(relationBox, relationTypes, "Relation" + i + "Type", "", com.gContactSync.StringBundle.getStr("relationWidth"));
310           }
311         }
312         catch (ex5) {
313           com.gContactSync.LOGGER.LOG_WARNING("Could not add the relation fields.", ex5);
314         }
315         /*
316         var nameWidth = "width: 30ch;";
317         document.getElementById("FirstName").setAttribute("style", nameWidth);
318         document.getElementById("LastName").setAttribute("style", nameWidth);
319         document.getElementById("DisplayName").setAttribute("style", nameWidth);
320         document.getElementById("NickName").setAttribute("style", nameWidth);
321         document.getElementById("FirstName").removeAttribute("flex");
322         document.getElementById("LastName").removeAttribute("flex");
323         document.getElementById("DisplayName").removeAttribute("flex");
324         document.getElementById("NickName").removeAttribute("flex");
325         var emailWidth = "width: 20ch;";
326         document.getElementById("PrimaryEmail").setAttribute("style", emailWidth);
327         document.getElementById("SecondEmail").setAttribute("style", emailWidth);
328         document.getElementById("ScreenName").setAttribute("width", "150px");
329         document.getElementById("PrimaryEmail").removeAttribute("flex");
330         document.getElementById("SecondEmail").removeAttribute("flex");
331         document.getElementById("ScreenName").removeAttribute("flex");
332         document.getElementById("abNameTab").firstChild.firstChild.style.minWidth = "50ch";
333         document.getElementById("abNameTab").firstChild.firstChild.style.maxWidth = "50ch";
334         var elem = document.getElementById("abTabPanels");
335         elem.style.width = "850px";
336         elem.style.maxWidth = "850px";
337         elem.style.minWidth = "850px";
338         */
339       }
340       catch (ex6) {
341         com.gContactSync.alertError("Unable to setup the extra tabs\n" + ex6);
342       }
343     }
344     // if this is the old dialog, show the extra phone numbers
345     else {
346       try {
347         // move the address descriptions below the addresses (rather than beside)
348         var gbox = document.getElementById("addressDescGroupBox");
349         if (gbox) {
350           var parent = gbox.parentNode;
351           parent.removeChild(gbox);
352           parent.parentNode.firstChild.appendChild(gbox);
353         }
354         document.getElementById("numbersGroupBox").removeAttribute("hidden");
355       }
356       catch (e) {
357         com.gContactSync.LOGGER.LOG_WARNING("Unable to move addressDescGroupBox" +
358                                             " or remove hidden from numbersGB", e);
359       }
360     }
361     
362     // if this is a read-only card, make added elements disabled
363     // the menulists are already taken care of
364     // TODO update CardDialogOverlay...
365     if (com.gContactSync.CardDialogOverlay.mDisabled) {
366       document.getElementById("ThirdEmail").readOnly       = true;
367       document.getElementById("FourthEmail").readOnly      = true;
368       document.getElementById("TalkScreenName").readOnly   = true;
369       document.getElementById("ICQScreenName").readOnly    = true;
370       document.getElementById("YahooScreenName").readOnly  = true;
371       document.getElementById("MSNScreenName").readOnly    = true;
372       document.getElementById("JabberScreenName").readOnly = true;
373       document.getElementById("HomeFaxNumber").readOnly    = true;
374       document.getElementById("OtherNumber").readOnly      = true;
375       document.getElementById("Relation").readOnly         = true;
376     }
377     // fix the width of the dialog
378     window.sizeToContent();
379 
380     // override the check and set card values function
381     com.gContactSync.originalCheckAndSetCardValues = CheckAndSetCardValues;
382     CheckAndSetCardValues = com.gContactSync.CardDialogOverlay.CheckAndSetCardValues;
383     // get the extra card values
384     this.GetCardValues(gEditCard.card, document);
385   },
386   /**
387    * Gets the parent node of an element with the given ID.
388    * If there is no element with the given ID then this function will return
389    * null.
390    * @param aID The ID of the element whose parent node is returned.
391    * @returns {XULElement} The parentNode of the element with the given ID.
392    */
393   getBox: function CardDialogOverlay_getBox(aID) {
394     var elem = document.getElementById(aID);
395     if (elem && elem.tagName === "emailaddress-input") { // Postbox
396       return elem;
397     }
398     return elem ? elem.parentNode : null;
399   },
400   /**
401    * Adds the e-mail type menulist to Postbox's emailaddress-input element.
402    * This also overrides the addRow method to add the type menulist to
403    * future emailaddress-input elements (and calls the original addRow)
404    * @param aElem The emailaddress-input element.
405    * @returns {boolean} True if the application is Postbox and the menulist was
406    *                    added.
407    */
408   addPostboxEmailType: function CardDialogOverlay_addPBEmailType(aElem) {
409     if (!aElem || aElem.tagName !== "emailaddress-input") {
410       return false;
411     }
412     /*
413     var emailTypes = com.gContactSync.gdata.contacts.EMAIL_TYPES;
414     this.addMenuItems(aElem, emailTypes, "", "other");
415     // save the original addRow method
416     aElem.origAddRow = aElem.addRow;
417     // override addRow to call the original then add the e-mail types
418     aElem.addRow = function gContactSync_addRow() {
419       // call the original
420       this.origAddRow.apply(this, arguments);
421       // add the e-mail type menulist
422       com.gContactSync.CardDialogOverlay.addPostboxEmailType(this.nextSibling);
423       // resize the window
424       window.sizeToContent();
425     }
426     */
427     return true;
428   },
429   /**
430    * Sets the attributes added by this extension as the value in the textbox or
431    * drop down menu in aDoc whose ID is identical to the attribute's name.
432    * Calls the original CheckAndSetCardValues function when finished.
433    * @param aCard  {nsIAbCard} The card to set the values for.
434    * @param aDoc   {Document Object} The document.
435    * @param aCheck Unused, but passed to the original method.
436    */
437   CheckAndSetCardValues: function CardDialogOverlay_CheckAndSetCardValues(aCard, aDoc, aCheck) {
438     // make sure the required data is present (abCardOverlay.js)
439     if (!CheckCardRequiredDataPresence(aDoc)) {
440       return false;
441     }
442     var contact = new com.gContactSync.TBContact(aCard);
443     var existingTypes = {
444       "WorkPhoneType":      {},
445       "HomePhoneType":      {},
446       "FaxNumberType":      {},
447       "CellularNumberType": {},
448       "PagerNumberType":    {}
449     };
450     // iterate through all the added attributes and types and set the card's value
451     // for each one of them
452     for (var attr in com.gContactSync.gAttributes) {
453       try {
454         // if the element exists, set the card's value as its value
455         var elem = aDoc.getElementById(attr);
456         if (elem) {
457           // I do not know why this is necessary, but it seems to be the only
458           // way to get the value correct in TB 2...
459           if (attr === "HomeFaxNumberType" || attr === "OtherNumberType") {
460             elem.value = elem.getAttribute("value");
461           }
462           com.gContactSync.LOGGER.VERBOSE_LOG("Attribute: '" + attr + "' - Value: '" + elem.value + "'");
463           contact.setValue(attr, elem.value);
464         }
465       }
466       catch (e) {
467         com.gContactSync.alertError("Error in com.gContactSync.CheckAndSetCardValues: " + attr + "\n" + e);
468       }
469     }
470     if (!contact.mContact.getProperty && gEditCard.abURI) {
471       if (contact.mContact.editCardToDatabase) { // Thunderbird 2
472         contact.mContact.editCardToDatabase(gEditCard.abURI);
473       }
474       else if (GetDirectoryFromURI) { // Postbox doesn't have editCardToDatabase
475         var dir = GetDirectoryFromURI(gEditCard.abURI);
476         if (dir) {
477           dir.modifyCard(contact.mContact);
478         }
479       }
480     }
481     // ensure that every contact edited through this dialog has at least a dummy
482     // e-mail address if necessary
483     var primEmailElem = aDoc.getElementById("PrimaryEmail");
484     if (!primEmailElem.value) {
485       // if it is a new contact it isn't already in any lists
486       if (gEditCard.abURI) {
487       // Check if it is in any mailing lists.  If so, force a dummy address
488       // When fetching lists, do not get the contacts (if it is found there is
489       // no need to get the contacts in every list)
490         var ab    = com.gContactSync.GAbManager.getAbByURI(gEditCard.abURI),
491             ab    = (ab ? new com.gContactSync.GAddressBook(ab) : null),
492             lists = ab.getAllLists(true);
493         for (var i in lists) {
494           // if the list does have the contact then make sure it gets a dummy
495           // e-mail address regardless of the preference
496           // do not check the PrimaryEmail address in hasContact since it is now
497           // empty
498           if (lists[i].hasContact(contact)) {
499             primEmailElem.value = com.gContactSync.makeDummyEmail(contact.mContact, true);
500             com.gContactSync.alertError(com.gContactSync.StringBundle.getStr("dummyEmailAdded") + "\n" + primEmailElem.value);
501             break;
502           }
503         }
504       }
505     }
506     // call the original and return its return value
507     return com.gContactSync.originalCheckAndSetCardValues.apply(this, arguments);
508   },
509   /**
510    * A method that gets all of the attributes added by this extension and sets
511    * the value of the textbox or drop down menu in aDoc whose ID is identical to
512    * the attribute's name.
513    * @param aCard {nsIAbCard} The card to get the values from.
514    * @param aDoc  {Document Object} The document.
515    */
516   GetCardValues: function CardDialogOverlay_GetCardValues(aCard, aDoc) {
517     // iterate through all the added type elements and get the card's value for
518     // each one of them to set as the value for the element
519     for (var attr in com.gContactSync.gAttributes) {
520       try {
521         var elem = aDoc.getElementById(attr);
522         // if the element exists, set its value as the card's value
523         if (elem) {
524           var value;
525           if (aCard.getProperty) // post Bug 413260
526             value = aCard.getProperty(attr, null);
527           else // pre Bug 413260
528             value = aCard.getStringAttribute(attr);
529           // set the element's value if attr isn't a type OR it is a type and
530           // the card's value for the attribute isn't null or blank
531           if (attr.indexOf("Type") == -1 || (value && value != "")) {
532             elem.value = value;
533           }
534         }
535       } catch (e) { com.gContactSync.alertError("Error in com.gContactSync.GetCardValues: " + attr + "\n" + e); }
536     }
537   
538     if (com.gContactSync.isDummyEmail(aDoc.getElementById("PrimaryEmail").value))
539       aDoc.getElementById("PrimaryEmail").value = null;
540   },
541   /**
542    * Sets up a type menu list element with a menuitem for each string in the
543    * array.
544    * @param aBox   {XUL Box} The box element to which this menu list is added.
545    * @param aArray {array}  The array of values to set for the menuitems.  There
546    *                        must be a string in the string bundle with the same
547    *                        name as the value.
548    * @param aID    {string} The ID for this menu list, which should be the name
549    *                        of the attribute with Type added to the end, such as
550    *                        WorkNumberType
551    * @param aValue {string} The default value to set for this list.
552    * @param aWidth {int}    The maximum width, if any.
553    *
554    * @returns {XULElement}  The menulist element.
555    */
556   addMenuItems: function CardDialogOverlay_addMenuItems(aBox, aArray, aID, aValue, aWidth) {
557     if (!aBox) {
558       return false;
559     }
560     var menuList = document.createElement("menulist");
561     menuList.setAttribute("id", aID);
562     var menuPopup = document.createElement("menupopup");
563     // put the default value first in the menupopup, if possible
564     var index = aArray.indexOf(aValue);
565     var elem;
566     if (index != -1) {
567       elem = document.createElement("menuitem");
568       elem.setAttribute("value", aValue);
569       elem.setAttribute("label", com.gContactSync.StringBundle.getStr(aValue ? aValue : "blank"));
570       aArray[index] = null;
571       menuPopup.appendChild(elem);
572     }
573     // then add the other values
574     for (var i = 0; i < aArray.length; i++) {
575       if (!aArray[i]) { // if this element is null it was the default value
576         aArray[i] = aValue; // so restore its value and skip adding it again
577         continue;
578       }
579       elem = document.createElement("menuitem");
580       elem.setAttribute("value", aArray[i]);
581       elem.setAttribute("label", com.gContactSync.StringBundle.getStr(aArray[i]));
582       menuPopup.appendChild(elem);
583     }
584     menuList.setAttribute("sizetopopup", "always");
585     if (aWidth) {
586       menuList.setAttribute("width", aWidth);
587       menuList.style.width = aWidth;
588       menuList.style.maxWidth = aWidth;
589     }
590     // add the popup to the menu list
591     menuList.appendChild(menuPopup);
592     // disable the menu list if this card is read-only
593     menuList.setAttribute("disabled", com.gContactSync.CardDialogOverlay.mDisabled);
594     // add the menu list to the box
595     aBox.appendChild(menuList);
596     return menuList;
597   },
598   /**
599    * Adds an hbox containing a label and textbox for a phone number.
600    * @param aID    {string} The ID for the textbox.
601    * @param aLabel {string} The text for the textbox's label.
602    */
603   setupNumBox: function CardDialogOverlay_setupNumBox(aID, aLabel) {
604     var box = document.createElement("hbox");
605     box.setAttribute("align", "center");
606     var spacer = document.createElement("spacer");
607     spacer.setAttribute("flex", 1);
608     box.appendChild(spacer);
609     var label = document.createElement("label");
610     label.setAttribute("control", aID);
611     label.setAttribute("value", aLabel);
612     box.appendChild(label);
613     var textbox = document.createElement("textbox");
614     textbox.setAttribute("id", aID);
615     textbox.setAttribute("class", "PhoneEditWidth");
616     if (com.gContactSync.CardDialogOverlay.mDisabled)
617       textbox.setAttribute("readonly", true);
618     else if (textbox.hasAttribute("readonly"))
619       textbox.removeAttribute("readonly");
620     textbox.setAttribute("width", "150px");
621     box.appendChild(textbox);
622     return box;
623   }
624 };
625