diff --git a/ChangeLog b/ChangeLog index aa25d4f84..37589dfb6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2010-10-26 Wolfgang Sourdeau + * UI/WebServerResources/UIxContactEditor.js: (saveCategories) + (appendCategoryInput, onComboButtonClick, onCategoryInputChange) + (onCategoryInputFocus, regenerateCategoriesMenu) + (onCategoryMenuEntryClick, onEmptyCategoryClick) new methods added + for handling the contact categories. + + * UI/Contacts/UIxContactEditor.m (-dealloc): release + addressBookItem, item, componentAddressBook and contactCategories + ivars. + (-setContactCategories, -contactCategories): new accessors for + getting/setting the contact vcard categories. Transfers are made + in JSON. + (-contactCategoriesList): new accessor to return the list of + categories defined in the preferences, combined with the list of + those found int the contact. + * SoObjects/SOGo/NSArray+Utilities.m (-mergedArrayWithArray:): new method that returns the contact of the current array merged with a unique copy of the objects of the array passed as parameter. diff --git a/UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings b/UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings index 65761b375..14281f41f 100644 --- a/UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings +++ b/UI/Contacts/BrazilianPortuguese.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Celular:"; "Pager:" = "Pager:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Título:"; "Service:" = "Serviço:"; "Company:" = "Empresa:"; diff --git a/UI/Contacts/Catalan.lproj/Localizable.strings b/UI/Contacts/Catalan.lproj/Localizable.strings index c68e2da56..71703358a 100644 --- a/UI/Contacts/Catalan.lproj/Localizable.strings +++ b/UI/Contacts/Catalan.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Mòbil:"; "Pager:" = "Cercapersones:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Títol:"; "Service:" = "Servei:"; "Company:" = "Companyia:"; diff --git a/UI/Contacts/Czech.lproj/Localizable.strings b/UI/Contacts/Czech.lproj/Localizable.strings index 5c9eccf01..a0e1ea34d 100644 --- a/UI/Contacts/Czech.lproj/Localizable.strings +++ b/UI/Contacts/Czech.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Mobilní telefon:"; "Pager:" = "Pager:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Pozice:"; "Service:" = "Služba:"; "Company:" = "Společnost:"; diff --git a/UI/Contacts/Dutch.lproj/Localizable.strings b/UI/Contacts/Dutch.lproj/Localizable.strings index faa571191..a9a0e52de 100644 --- a/UI/Contacts/Dutch.lproj/Localizable.strings +++ b/UI/Contacts/Dutch.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Mobiel:"; "Pager:" = "Pieper:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Titel:"; "Service:" = "Service:"; "Company:" = "Company:"; diff --git a/UI/Contacts/English.lproj/Localizable.strings b/UI/Contacts/English.lproj/Localizable.strings index 278d3e445..e947ad714 100644 --- a/UI/Contacts/English.lproj/Localizable.strings +++ b/UI/Contacts/English.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Mobile:"; "Pager:" = "Pager:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Title:"; "Service:" = "Service:"; "Company:" = "Company:"; diff --git a/UI/Contacts/French.lproj/Localizable.strings b/UI/Contacts/French.lproj/Localizable.strings index 9ef6fb1ee..72cfca2f3 100644 --- a/UI/Contacts/French.lproj/Localizable.strings +++ b/UI/Contacts/French.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Portable :"; "Pager:" = "Téléavertisseur :"; +/* categories */ +"contacts_category_labels" = "Ami, Client, Collègue, Concurrent, Famille, Fournisseur, Partenaire d'affaire, Presse, VIP"; +"Categories" = "Catégories"; +"New category" = "Nouvelle catégorie"; + +/* adresses */ "Title:" = "Fonction :"; "Service:" = "Service:"; "Company:" = "Company:"; diff --git a/UI/Contacts/German.lproj/Localizable.strings b/UI/Contacts/German.lproj/Localizable.strings index e977d5931..b8f16d505 100644 --- a/UI/Contacts/German.lproj/Localizable.strings +++ b/UI/Contacts/German.lproj/Localizable.strings @@ -95,6 +95,12 @@ "Mobile:" = "Mobil:"; "Pager:" = "Pager:"; +/* categories */ +"contacts_category_labels" = "Geschäftspartner, Familie, Freund, Kollegin, Konkurrenten, Kunden, Lieferant, Presse, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Titel:"; "Service:" = "Service:"; "Company:" = "Firma:"; diff --git a/UI/Contacts/Hungarian.lproj/Localizable.strings b/UI/Contacts/Hungarian.lproj/Localizable.strings index 59c5452c1..5b5c3bf2a 100644 --- a/UI/Contacts/Hungarian.lproj/Localizable.strings +++ b/UI/Contacts/Hungarian.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Mobil:"; "Pager:" = "Személyhívó:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Cím:"; "Service:" = "Szolgáltatás:"; "Company:" = "Vállalat:"; diff --git a/UI/Contacts/Italian.lproj/Localizable.strings b/UI/Contacts/Italian.lproj/Localizable.strings index 520337217..7e0b310f4 100644 --- a/UI/Contacts/Italian.lproj/Localizable.strings +++ b/UI/Contacts/Italian.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Cellulare:"; "Pager:" = "Cerca Persone:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Titolo:"; "Service:" = "Service:"; "Company:" = "Società:"; diff --git a/UI/Contacts/Polish.lproj/Localizable.strings b/UI/Contacts/Polish.lproj/Localizable.strings index 6ef1a6fdb..88195ea2b 100644 --- a/UI/Contacts/Polish.lproj/Localizable.strings +++ b/UI/Contacts/Polish.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Telefon kom.:"; "Pager:" = "Pager:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Tytuł:"; "Service:" = "Usługa:"; "Company:" = "Firma:"; diff --git a/UI/Contacts/Russian.lproj/Localizable.strings b/UI/Contacts/Russian.lproj/Localizable.strings index 1b7660ce8..ae5aa0e4d 100644 --- a/UI/Contacts/Russian.lproj/Localizable.strings +++ b/UI/Contacts/Russian.lproj/Localizable.strings @@ -84,6 +84,12 @@ "Mobile: " = "Mobile: "; "Pager: " = "Pager: "; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title: " = "Title: "; "Service: " = "Service: "; "Company: " = "Company: "; diff --git a/UI/Contacts/Spanish.lproj/Localizable.strings b/UI/Contacts/Spanish.lproj/Localizable.strings index fb2a215c2..cfebd2823 100644 --- a/UI/Contacts/Spanish.lproj/Localizable.strings +++ b/UI/Contacts/Spanish.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Móvil:"; "Pager:" = "Busca:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Título:"; "Service:" = "Service:"; "Company:" = "Company:"; diff --git a/UI/Contacts/Swedish.lproj/Localizable.strings b/UI/Contacts/Swedish.lproj/Localizable.strings index 8760af7ef..82b1ef16f 100644 --- a/UI/Contacts/Swedish.lproj/Localizable.strings +++ b/UI/Contacts/Swedish.lproj/Localizable.strings @@ -96,6 +96,12 @@ "Mobile:" = "Mobiltelefon:"; "Pager:" = "Personsökare:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Titel:"; "Service:" = "Befattning:"; "Company:" = "Företag:"; diff --git a/UI/Contacts/UIxContactEditor.h b/UI/Contacts/UIxContactEditor.h index c66acdc49..f0998f2f2 100644 --- a/UI/Contacts/UIxContactEditor.h +++ b/UI/Contacts/UIxContactEditor.h @@ -40,6 +40,7 @@ NSMutableArray *photosURL; NSMutableDictionary *snapshot; /* contains the values for editing */ SOGoContactFolder *componentAddressBook; + NSArray *contactCategories; } - (void) setAddressBookItem: (id) _item; diff --git a/UI/Contacts/UIxContactEditor.m b/UI/Contacts/UIxContactEditor.m index 43dbd4734..c97d58390 100644 --- a/UI/Contacts/UIxContactEditor.m +++ b/UI/Contacts/UIxContactEditor.m @@ -37,6 +37,11 @@ #import #import +#import +#import +#import +#import + #import #import #import @@ -54,6 +59,11 @@ snapshot = [[NSMutableDictionary alloc] initWithCapacity: 16]; preferredEmail = nil; photosURL = nil; + addressBookItem = nil; + item = nil; + card = nil; + componentAddressBook = nil; + contactCategories = nil; } return self; @@ -64,6 +74,10 @@ [snapshot release]; [preferredEmail release]; [photosURL release]; + [addressBookItem release]; + [item release]; + [componentAddressBook release]; + [contactCategories release]; [super dealloc]; } @@ -216,7 +230,6 @@ ASSIGN (componentAddressBook, _componentAddressBook); } - - (NSString *) addressBookDisplayName { NSString *fDisplayName; @@ -232,6 +245,80 @@ return fDisplayName; } +- (void) setContactCategories: (NSString *) jsonCategories +{ + NSArray *newCategories; + + newCategories = [jsonCategories objectFromJSONString]; + if ([newCategories isKindOfClass: [NSArray class]]) + ASSIGN (contactCategories, newCategories); +} + +- (NSString *) contactCategories +{ + NSString *jsonCats; + + if (!contactCategories) + ASSIGN (contactCategories, [card categories]); + jsonCats = [contactCategories jsonRepresentation]; + if (!jsonCats) + jsonCats = @"[]"; + + return jsonCats; +} + +- (NSArray *) _languageContactsCategories +{ + NSArray *categoryLabels; + + categoryLabels = [[self labelForKey: @"contacts_category_labels"] + componentsSeparatedByString: @","]; + if (!categoryLabels) + categoryLabels = [NSArray array]; + + return [categoryLabels trimmedComponents]; +} + +- (NSArray *) _fetchAndCombineCategoriesList: (NSArray *) contactCats +{ + NSString *ownerLogin; + SOGoUserDefaults *ud; + NSArray *cats, *newCats; + + ownerLogin = [[self clientObject] ownerInContext: context]; + ud = [[SOGoUser userWithLogin: ownerLogin] userDefaults]; + cats = [ud contactsCategories]; + if (!cats) + cats = [self _languageContactsCategories]; + + if (contactCats) + { + newCats = [cats mergedArrayWithArray: contactCats]; + if ([newCats count] != [cats count]) + { + cats = [newCats sortedArrayUsingSelector: + @selector (localizedCaseInsensitiveCompare:)]; + [ud setContactsCategories: cats]; + [ud synchronize]; + } + } + + return cats; +} + +- (NSString *) contactCategoriesList +{ + NSArray *cats; + NSString *list; + + cats = [self _fetchAndCombineCategoriesList: [card categories]]; + list = [cats jsonRepresentation]; + if (!list) + list = @"[]"; + + return list; +} + /* actions */ - (BOOL) shouldTakeValuesFromRequest: (WORequest *) request @@ -735,6 +822,8 @@ { // [self _fixupSnapshot]; [self _saveSnapshot]; + [card setCategories: contactCategories]; + [self _fetchAndCombineCategoriesList: contactCategories]; [contact save]; if (componentAddressBook && componentAddressBook != [self componentAddressBook]) diff --git a/UI/Contacts/Ukrainian.lproj/Localizable.strings b/UI/Contacts/Ukrainian.lproj/Localizable.strings index 7ecb1b409..d5d2d2ade 100644 --- a/UI/Contacts/Ukrainian.lproj/Localizable.strings +++ b/UI/Contacts/Ukrainian.lproj/Localizable.strings @@ -93,6 +93,12 @@ "Mobile:" = "Мобільний:"; "Pager:" = "Пейджер:"; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Посада:"; "Service:" = "Служба:"; "Company:" = "Організація:"; diff --git a/UI/Contacts/Welsh.lproj/Localizable.strings b/UI/Contacts/Welsh.lproj/Localizable.strings index 7e4d2a062..17a95ad98 100644 --- a/UI/Contacts/Welsh.lproj/Localizable.strings +++ b/UI/Contacts/Welsh.lproj/Localizable.strings @@ -85,6 +85,12 @@ "Mobile:" = "Symudol: "; "Pager:" = "Peiriant galw: "; +/* categories */ +"contacts_category_labels" = "Colleague, Competitor, Customer, Friend, Family, Business Partner, Provider, Press, VIP"; +"Categories" = "Categories"; +"New category" = "New category"; + +/* adresses */ "Title:" = "Teitl: "; "Service:" = "Gwasanaeth: "; "Company:" = "Cwmni: "; diff --git a/UI/WebServerResources/UIxContactEditor.css b/UI/WebServerResources/UIxContactEditor.css index 8d3989a86..7036dea41 100644 --- a/UI/WebServerResources/UIxContactEditor.css +++ b/UI/WebServerResources/UIxContactEditor.css @@ -47,5 +47,15 @@ DIV.tab TD.firstColumn INPUT.textField, DIV.tab TD.secondColumn INPUT.textField { width: 35%; } +#categoryInfos +{ margin-left: 20px; } + +#categoryContainer +{ max-height: 320px; + overflow-y: auto; } + +INPUT.comboBoxField, #emptyCategory +{ width: 330px; } + #otherInfos TEXTAREA { width: 70%; } diff --git a/UI/WebServerResources/UIxContactEditor.js b/UI/WebServerResources/UIxContactEditor.js index 68f941671..acd7832b0 100644 --- a/UI/WebServerResources/UIxContactEditor.js +++ b/UI/WebServerResources/UIxContactEditor.js @@ -1,36 +1,38 @@ -/* -*- Mode: java; tab-width: 2; c-label-minimum-indentation: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* -*- Mode: js2-mode; tab-width: 4; c-label-minimum-indentation: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Copyright (C) 2005 SKYRIX Software AG Copyright (C) 2006-2010 Inverse - + This file is part of OpenGroupware.org. - + OGo is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. - + OGo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - + You should have received a copy of the GNU Lesser General Public License along with OGo; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -var uixEmailUsr = +var uixEmailUsr = "([a-zA-Z0-9][a-zA-Z0-9_.-]*|\"([^\\\\\x80-\xff\015\012\"]|\\\\[^\x80-\xff])+\")"; -var uixEmailDomain = +var uixEmailDomain = "([a-zA-Z0-9][a-zA-Z0-9._-]*\\.)*[a-zA-Z0-9][a-zA-Z0-9._-]*\\.[a-zA-Z]{2,5}"; var uixEmailRegex = new RegExp("^"+uixEmailUsr+"\@"+uixEmailDomain+"$"); var dateRegex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/; var displayNameChanged = false; +var tabIndex = 0; + function unescapeCallbackParameter(s) { if(!s || s.length == 0) return s; @@ -41,21 +43,21 @@ function unescapeCallbackParameter(s) { function copyContact(type, email, uid, sn, cn, givenName, telephoneNumber, facsimileTelephoneNumber, - mobile, postalAddress, homePostalAddress, + mobile, postalAddress, homePostalAddress, departmentNumber, l) { - // var type = arguments[0]; - // var email = arguments[1]; - // var uid = arguments[2]; - // var sn = arguments[3]; - // var givenName = arguments[4]; - // var telephoneNumber = arguments[5]; - // var facsimileTelephoneNumber = arguments[6]; - // var mobile = arguments[7]; - // var postalAddress = arguments[8]; - // var homePostalAddress = arguments[9]; - // var departmentNumber = arguments[10]; - // var l = arguments[11]; + // var type = arguments[0]; + // var email = arguments[1]; + // var uid = arguments[2]; + // var sn = arguments[3]; + // var givenName = arguments[4]; + // var telephoneNumber = arguments[5]; + // var facsimileTelephoneNumber = arguments[6]; + // var mobile = arguments[7]; + // var postalAddress = arguments[8]; + // var homePostalAddress = arguments[9]; + // var departmentNumber = arguments[10]; + // var l = arguments[11]; var e; e = $('cn'); e.setAttribute('value', unescapeCallbackParameter(cn)); @@ -83,7 +85,7 @@ function copyContact(type, email, uid, sn, function validateContactEditor() { var rc = true; - + var e = $('workMail'); if (e.value.length > 0 && !uixEmailRegex.test(e.value)) { @@ -139,11 +141,31 @@ function onEditorCancelClick(event) { } function onEditorSubmitClick(event) { - if (validateContactEditor()) + if (validateContactEditor()) { + saveCategories(); $('mainForm').submit(); + } this.blur(); } +function saveCategories() { + var container = $("categoryContainer"); + var catsInput = $("contactCategories"); + if (container && catsInput) { + var newCategories = $([]); + var inputs = container.select("INPUT"); + for (var i = 0; i < inputs.length; i++) { + var newValue = inputs[i].value.trim(); + if (newValue.length > 0 && newValue != _("New category") + && newCategories.indexOf(newValue) == -1) { + newCategories.push(newValue); + } + } + var json = newCategories.toJSON(); + catsInput.value = json; + } +} + function onDocumentKeydown(event) { var target = Event.element(event); if (target.tagName == "INPUT" || target.tagName == "SELECT") { @@ -155,6 +177,95 @@ function onDocumentKeydown(event) { } } +function appendCategoryInput(label) { + var container = $("categoryContainer"); + + var inputContainer = createElement("div"); + var textInput = createElement("input", null, "comboBoxField", null, + { type: "text"}, inputContainer); + textInput.observe("change", onCategoryInputChange); + textInput.observe("focus", onCategoryInputFocus); + textInput.value = label; + textInput.tabIndex = tabIndex; + tabIndex++; + var button = createElement("button", null, "comboBoxButton"); + inputContainer.appendChild(button); + button.observe("click", onComboButtonClick); + button.textInput = textInput; + button.menuName = "categoriesMenu"; + + container.appendChild(inputContainer); + + return textInput; +} + +function onComboButtonClick(event) { + var menu = $(this.menuName); + popupMenu(event, this.menuName, this.textInput); + var container = $("categoryContainer"); + var menuTop = (container.cascadeTopOffset() + + this.textInput.offsetTop + this.textInput.clientHeight); + var menuLeft = this.textInput.cascadeLeftOffset(); + var minWidth = this.textInput.clientWidth; + menu.setStyle({ "top": menuTop + "px", + "left": menuLeft + "px", + "min-width": minWidth + "px" }); + + event.preventDefault(); +} + +function onCategoryInputChange(event) { + var newValue = this.value.trim(); + if (newValue == "") { + var inputContainer = this.parentNode; + inputContainer.parentNode.removeChild(inputContainer); + } + else { + if (gCategories.indexOf(newValue) == -1) { + gCategories.push(newValue); + gCategories.sort(function (a, b) + { return a.toLowerCase() > b.toLowerCase(); } ); + regenerateCategoriesMenu(); + } + } +} + +function onCategoryInputFocus(event) { + this.select(); +} + +function regenerateCategoriesMenu() { + var menu = $("categoriesMenu"); + if (menu) { + while (menu.lastChild) { + menu.removeChild(menu.lastChild); + } + var list = createElement("ul"); + for (var i = 0; i < gCategories.length; i++) { + var label = gCategories[i]; + var entry = createElement("li"); + entry.label = label; + entry.menuCallback = onCategoryMenuEntryClick; + entry.observe("click", onMenuClickHandler); + entry.appendChild(document.createTextNode(label)); + list.appendChild(entry); + } + menu.appendChild(list); + } +} + +function onCategoryMenuEntryClick(event) { + document.menuTarget.value = this.label; + document.menuTarget.focus(); +} + +function onEmptyCategoryClick(event) { + var textInput = appendCategoryInput(_("New category")); + window.setTimeout(function() {textInput.focus();}, + 100); + event.preventDefault(); +} + function initEditorForm() { var tabsContainer = $("editorTabs"); var controller = new SOGoTabsController(); @@ -166,9 +277,25 @@ function initEditorForm() { $("givenName").onkeyup = onFnNewValue; $("cancelButton").observe("click", onEditorCancelClick); - $("submitButton").observe("click", onEditorSubmitClick); + var submitButton = $("submitButton"); + if (submitButton) { + submitButton.observe("click", onEditorSubmitClick); + } Event.observe(document, "keydown", onDocumentKeydown); + + regenerateCategoriesMenu(); + var catsInput = $("contactCategories"); + if (catsInput && catsInput.value.length > 0) { + var contactCats = $(catsInput.value.evalJSON(false)); + for (var i = 0; i < contactCats.length; i++) { + appendCategoryInput(contactCats[i]); + } + } + + var emptyCategory = $("emptyCategory"); + emptyCategory.tabIndex = 10000; + emptyCategory.observe("click", onEmptyCategoryClick); } document.observe("dom:loaded", initEditorForm);