Ajaxifed the addressbook module. See ChangeLog.

Monotone-Parent: cb0ecd99fcf222060f6e3bc248d2e9a9c47624c1
Monotone-Revision: 1cdaff22cdbdb961e1937dc8f1ac1936bd06dc99

Monotone-Author: flachapelle@inverse.ca
Monotone-Date: 2011-04-14T18:01:02
Monotone-Branch: ca.inverse.sogo
maint-2.0.2
Francis Lachapelle 2011-04-14 18:01:02 +00:00
parent a9806e6b6d
commit a7d852fbec
9 changed files with 249 additions and 119 deletions

View File

@ -1,3 +1,20 @@
2011-04-14 Francis Lachapelle <flachapelle@inverse.ca>
* UI/Contacts/UIxContactsListActions.m: was
UIxContactsListView.m. The new method contactsListAction now
returns a JSON representation of the addressbook.
* UI/Contacts/UIxContactFoldersView.m (-personalContactInfos): new
method used to populate the wox template with the personal
addressbook. The addressbook module is now pre-loaded with it,
eliminating an Ajax query.
* UI/WebServerResources/ContactsUI.js (initContacts): delegate
all events to the table instead of each row.
(contactsListCallback): we now receive a JSON representation of
the contacts and reuse the existing table rows instead of
overwriting the table.
2011-04-13 Wolfgang Sourdeau <wsourdeau@inverse.ca> 2011-04-13 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* OpenChange/MAPIStoreObject.m: make use of the IMP cache methods * OpenChange/MAPIStoreObject.m: make use of the IMP cache methods

View File

@ -9,7 +9,7 @@ ContactsUI_PRINCIPAL_CLASS = ContactsUIProduct
ContactsUI_LANGUAGES = BrazilianPortuguese Catalan Czech Dutch English French German Hungarian Italian Norwegian Polish Russian Spanish Swedish Ukrainian Welsh ContactsUI_LANGUAGES = BrazilianPortuguese Catalan Czech Dutch English French German Hungarian Italian Norwegian Polish Russian Spanish Swedish Ukrainian Welsh
ContactsUI_OBJC_FILES = \ ContactsUI_OBJC_FILES = \
UIxContactsUserFolders.m \ UIxContactsUserFolders.m \
UIxContactsMailerSelection.m \ UIxContactsMailerSelection.m \
UIxContactsUserRightsEditor.m \ UIxContactsUserRightsEditor.m \
\ \
@ -18,10 +18,10 @@ ContactsUI_OBJC_FILES = \
UIxContactActions.m \ UIxContactActions.m \
UIxContactView.m \ UIxContactView.m \
UIxContactEditor.m \ UIxContactEditor.m \
UIxListView.m \ UIxListView.m \
UIxListEditor.m \ UIxListEditor.m \
UIxContactsListView.m \ UIxContactsListActions.m \
UIxContactFoldersView.m \ UIxContactFoldersView.m \
UIxContactFolderActions.m UIxContactFolderActions.m
ContactsUI_RESOURCE_FILES += \ ContactsUI_RESOURCE_FILES += \

View File

@ -29,6 +29,7 @@
@interface UIxContactFoldersView : UIxComponent @interface UIxContactFoldersView : UIxComponent
{ {
SOGoUserSettings *us; SOGoUserSettings *us;
NSDictionary *currentContact;
NSString *selectorComponentClass; NSString *selectorComponentClass;
NSMutableDictionary *moduleSettings; NSMutableDictionary *moduleSettings;
id currentFolder; id currentFolder;
@ -37,6 +38,8 @@
- (NSArray *) contactFolders; - (NSArray *) contactFolders;
- (NSArray *) personalContactInfos;
- (NSString *) currentContactFolderId; - (NSString *) currentContactFolderId;
- (NSString *) currentContactFolderOwner; - (NSString *) currentContactFolderOwner;
- (NSString *) currentContactFolderName; - (NSString *) currentContactFolderName;

View File

@ -87,6 +87,38 @@
} }
} }
- (void) setCurrentContact: (NSDictionary *) _contact
{
currentContact = _contact;
}
- (NSDictionary *) currentContact
{
return currentContact;
}
- (NSString *) currentContactClasses
{
return [[currentContact objectForKey: @"c_component"] lowercaseString];
}
- (NSArray *) personalContactInfos
{
SOGoContactFolders *folders;
id <SOGoContactFolder> folder;
NSArray *contactInfos;
folders = [self clientObject];
folder = [folders lookupPersonalFolder: @"personal" ignoringRights: YES];
contactInfos = [folder lookupContactsWithFilter: nil
onCriteria: nil
sortBy: @"c_cn"
ordering: NSOrderedAscending];
return contactInfos;
}
- (id <WOActionResults>) mailerContactsAction - (id <WOActionResults>) mailerContactsAction
{ {
selectorComponentClass = @"UIxContactsMailerSelection"; selectorComponentClass = @"UIxContactsMailerSelection";

View File

@ -19,17 +19,17 @@
02111-1307, USA. 02111-1307, USA.
*/ */
#ifndef __UIxContactsListView_H__ #ifndef __UIxContactsListActions_H__
#define __UIxContactsListView_H__ #define __UIxContactsListActions_H__
#import <SOGoUI/UIxComponent.h> #import <NGObjWeb/WODirectAction.h>
@class NSDictionary; @class NSDictionary;
@class NSString; @class NSString;
@protocol SOGoContactObject; @protocol SOGoContactObject;
@interface UIxContactsListView : UIxComponent @interface UIxContactsListActions : WODirectAction
{ {
NSDictionary *currentContact; NSDictionary *currentContact;
NSArray *contactInfos; NSArray *contactInfos;
@ -37,4 +37,4 @@
@end @end
#endif /* __UIxContactsListView_H__ */ #endif /* __UIxContactsListActions_H__ */

View File

@ -1,5 +1,5 @@
/* /*
Copyright (C) 2006-2010 Inverse inc. Copyright (C) 2006-2011 Inverse inc.
Copyright (C) 2004-2005 SKYRIX Software AG Copyright (C) 2004-2005 SKYRIX Software AG
This file is part of OpenGroupware.org. This file is part of OpenGroupware.org.
@ -32,6 +32,8 @@
#import <NGExtensions/NSString+misc.h> #import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSNull+misc.h> #import <NGExtensions/NSNull+misc.h>
#import <Common/WODirectAction+SOGo.h>
#import <Contacts/SOGoContactObject.h> #import <Contacts/SOGoContactObject.h>
#import <Contacts/SOGoContactFolder.h> #import <Contacts/SOGoContactFolder.h>
#import <Contacts/SOGoContactFolders.h> #import <Contacts/SOGoContactFolders.h>
@ -44,9 +46,9 @@
#import <SoObjects/Contacts/SOGoContactGCSFolder.h> #import <SoObjects/Contacts/SOGoContactGCSFolder.h>
#import <GDLContentStore/GCSFolder.h> #import <GDLContentStore/GCSFolder.h>
#import "UIxContactsListView.h" #import "UIxContactsListActions.h"
@implementation UIxContactsListView @implementation UIxContactsListActions
- (id) init - (id) init
{ {
@ -64,16 +66,6 @@
/* accessors */ /* accessors */
- (void) setCurrentContact: (NSDictionary *) _contact
{
currentContact = _contact;
}
- (NSDictionary *) currentContact
{
return currentContact;
}
- (NSString *) defaultSortKey - (NSString *) defaultSortKey
{ {
return @"c_cn"; return @"c_cn";
@ -82,8 +74,10 @@
- (NSString *) sortKey - (NSString *) sortKey
{ {
NSString *s; NSString *s;
WORequest *rq;
s = [self queryParameterForKey: @"sort"]; rq = [context request];
s = [rq formValueForKey: @"sort"];
if (![s length]) if (![s length])
s = [self defaultSortKey]; s = [self defaultSortKey];
@ -95,18 +89,20 @@
id <SOGoContactFolder> folder; id <SOGoContactFolder> folder;
NSString *ascending, *searchText, *valueText; NSString *ascending, *searchText, *valueText;
NSComparisonResult ordering; NSComparisonResult ordering;
WORequest *rq;
if (!contactInfos) if (!contactInfos)
{ {
folder = [self clientObject]; folder = [self clientObject];
rq = [context request];
ascending = [self queryParameterForKey: @"asc"]; ascending = [rq formValueForKey: @"asc"];
ordering = ((![ascending length] || [ascending boolValue]) ordering = ((![ascending length] || [ascending boolValue])
? NSOrderedAscending : NSOrderedDescending); ? NSOrderedAscending : NSOrderedDescending);
searchText = [self queryParameterForKey: @"search"]; searchText = [rq formValueForKey: @"search"];
if ([searchText length] > 0) if ([searchText length] > 0)
valueText = [self queryParameterForKey: @"value"]; valueText = [rq formValueForKey: @"value"];
else else
valueText = nil; valueText = nil;
@ -121,9 +117,21 @@
return contactInfos; return contactInfos;
} }
- (NSString *) currentContactClasses /**
* Retrieve the addressbook contacts with respect to the sort and
* search criteria.
* @return a JSON array of dictionaries representing the contacts.
*/
- (id <WOActionResults>) contactsListAction
{ {
return [[currentContact objectForKey: @"c_component"] lowercaseString]; id <WOActionResults> result;
NSArray *contactsList;
contactsList = [self contactInfos];
result = [self responseWithStatus: 200
andString: [contactsList jsonRepresentation]];
return result;
} }
- (id <WOActionResults>) contactSearchAction - (id <WOActionResults>) contactSearchAction
@ -136,11 +144,12 @@
NSMutableDictionary *uniqueContacts; NSMutableDictionary *uniqueContacts;
unsigned int i; unsigned int i;
NSSortDescriptor *commonNameDescriptor; NSSortDescriptor *commonNameDescriptor;
WORequest *rq;
searchText = [self queryParameterForKey: @"search"]; rq = [context request];
searchText = [rq formValueForKey: @"search"];
if ([searchText length] > 0) if ([searchText length] > 0)
{ {
folder = nil;
NS_DURING NS_DURING
folder = [self clientObject]; folder = [self clientObject];
NS_HANDLER NS_HANDLER
@ -176,9 +185,8 @@
data = [NSDictionary dictionaryWithObjectsAndKeys: searchText, @"searchText", data = [NSDictionary dictionaryWithObjectsAndKeys: searchText, @"searchText",
sortedContacts, @"contacts", nil]; sortedContacts, @"contacts", nil];
result = [self responseWithStatus: 200]; result = [self responseWithStatus: 200
andString: [data jsonRepresentation]];
[(WOResponse*)result appendContentString: [data jsonRepresentation]];
} }
else else
result = [NSException exceptionWithHTTPStatus: 400 result = [NSException exceptionWithHTTPStatus: 400
@ -187,13 +195,4 @@
return result; return result;
} }
@end /* UIxContactsListActions */
/* actions */
- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq
inContext: (WOContext*) _c
{
return YES;
}
@end /* UIxContactsListView */

View File

@ -71,11 +71,12 @@
methods = { methods = {
view = { view = {
protectedBy = "View"; protectedBy = "View";
pageName = "UIxContactsListView"; actionClass = "UIxContactsListActions";
actionName = "contactsList";
}; };
contactSearch = { contactSearch = {
protectedBy = "<public>"; protectedBy = "<public>";
pageName = "UIxContactsListView"; actionClass = "UIxContactsListActions";
actionName = "contactSearch"; actionName = "contactSearch";
}; };
newcontact = { newcontact = {
@ -90,7 +91,7 @@
}; };
mailer-contacts = { mailer-contacts = {
protectedBy = "View"; protectedBy = "View";
pageName = "UIxContactsListView"; pageName = "UIxContactFoldersView";
actionName = "mailerContacts"; actionName = "mailerContacts";
}; };
export = { export = {
@ -130,7 +131,8 @@
methods = { methods = {
view = { view = {
protectedBy = "<public>"; protectedBy = "<public>";
pageName = "UIxContactsListView"; actionClass = "UIxContactsListActions";
actionName = "contactsList";
}; };
newcontact = { newcontact = {
protectedBy = "<public>"; protectedBy = "<public>";
@ -139,7 +141,7 @@
}; };
mailer-contacts = { mailer-contacts = {
protectedBy = "<public>"; protectedBy = "<public>";
pageName = "UIxContactsListView"; pageName = "UIxContactFoldersView";
actionName = "mailerContacts"; actionName = "mailerContacts";
}; };
canAccessContent = { canAccessContent = {

View File

@ -138,11 +138,47 @@
<div id="rightPanel"> <div id="rightPanel">
<var:component className="UIxContactsFilterPanel" qualifier="qualifier" /> <var:component className="UIxContactsFilterPanel" qualifier="qualifier" />
<div id="contactsListContent"><!-- space --></div> <div id="contactsListContent">
<table id="contactsList" cellspacing="0">
<thead>
<tr class="tableview">
<!-- localize -->
<td class="tbtv_headercell sortableTableHeader" id="nameHeader"
><img id="messageSortImage" class="sortImage" rsrc:src="arrow-up.png"
/><var:string label:value="Name"
/></td
><td class="tbtv_headercell sortableTableHeader" id="mailHeader"
><var:string label:value="Email"/></td
><td class="tbtv_headercell sortableTableHeader" id="screenNameHeader"
><var:string label:value="Screen Name" /></td
><td class="tbtv_headercell sortableTableHeader" id="orgHeader"
><var:string label:value="Organization" /></td
><td class="tbtv_headercell sortableTableHeader" id="phoneHeader"
><var:string label:value="Preferred Phone" /></td
></tr>
</thead>
<tbody id="contactsListTbody">
<var:foreach list="personalContactInfos" item="currentContact">
<tr var:class="currentContactClasses"
var:categories="currentContact.c_categories"
var:id="currentContact.c_name"
var:contactname="currentContact.c_cn">
<td class="displayName"><var:string value="currentContact.c_cn" const:escapeHTML="YES" /></td>
<td><var:string value="currentContact.c_mail"/></td>
<td><var:string value="currentContact.c_screenname"/></td>
<td><var:string value="currentContact.c_o"/></td>
<td><var:string value="currentContact.c_telephonenumber"/></td>
</tr>
</var:foreach>
</tbody>
</table>
</div>
<div class="dragHandle" id="rightDragHandle"><!-- space --></div> <div class="dragHandle" id="rightDragHandle"><!-- space --></div>
<div id="contactView" onmousedown="return false;"><!-- space --></div> <div id="contactView"><!-- space --></div>
</div> </div>
<var:string value="errorAlertJavaScript" const:escapeHTML="NO" /> <var:string value="errorAlertJavaScript" const:escapeHTML="NO" />

View File

@ -7,7 +7,7 @@ var usersRightsWindowHeight = 179;
var usersRightsWindowWidth = 450; var usersRightsWindowWidth = 450;
var Contact = { var Contact = {
currentAddressBook: null, currentAddressBook: "/personal",
currentContact: null, currentContact: null,
deleteContactsRequestCount: null deleteContactsRequestCount: null
}; };
@ -55,8 +55,6 @@ function openContactsFolder(contactsFolder, reload, idx) {
var contactsList = $("contactsList"); var contactsList = $("contactsList");
if (contactsList) if (contactsList)
selection = contactsList.getSelectedRowsId(); selection = contactsList.getSelectedRowsId();
// else
// window.alert("no contactsList");
} }
else else
selection = null; selection = null;
@ -70,18 +68,6 @@ function openContactsFolder(contactsFolder, reload, idx) {
} }
} }
function openContactsFolderAtIndex(element) {
var idx = element.getAttribute("idx");
var url = URLForFolderID(Contact.currentAddressBook) + "/view?noframe=1&idx=" + idx;
if (document.contactsListAjaxRequest) {
document.contactsListAjaxRequest.aborted = true;
document.contactsListAjaxRequest.abort();
}
document.contactsListAjaxRequest
= triggerAjaxRequest(url, contactsListCallback);
}
function contactsListCallback(http) { function contactsListCallback(http) {
if (http.readyState == 4) { if (http.readyState == 4) {
if (http.status == 200) { if (http.status == 200) {
@ -89,33 +75,76 @@ function contactsListCallback(http) {
var div = $("contactsListContent"); var div = $("contactsListContent");
var table = $("contactsList"); var table = $("contactsList");
if (table) { var tbody = table.tBodies[0];
// Update table var rows = tbody.getElementsByTagName("TR");
var data = http.responseText; var data = [];
var html = data.replace(/^(.*\n)*.*(<table(.*\n)*)$/, "$2"); if (http.responseText.length > 0)
var tbody = table.tBodies[0]; data = http.responseText.evalJSON(true);
var tmp = document.createElement('div');
$(tmp).update(html); tbody.deselectAll();
table.replaceChild($(tmp).select("table tbody")[0], tbody);
div.scrollTop = 0;
if (data.length > 0) {
// Replace existing rows
for (var i = 0; i < data.length && i < rows.length; i++) {
var contact = data[i];
var row = rows[i];
row.className = contact["c_component"];
row.setAttribute("id", contact["c_name"]);
row.setAttribute("categories", contact["c_categories"]);
row.setAttribute("contactname", contact["c_cn"]);
var cells = row.getElementsByTagName("TD");
$(cells[0]).update(contact["c_cn"]);
$(cells[1]).update(contact["c_mail"]);
$(cells[2]).update(contact["c_screenname"]);
$(cells[3]).update(contact["c_o"]);
$(cells[4]).update(contact["c_telephonenumber"]);
}
// Add extra rows
for (var j = i; j < data.length; j++) {
var contact = data[j];
var row = createElement("tr",
contact["c_name"],
contact["c_component"],
null,
{ categories: contact["c_categories"],
contactname: contact["c_cn"] },
tbody);
var cell = createElement("td",
null,
( "displayName" ),
null,
null,
row);
cell.appendChild(document.createTextNode(contact["c_cn"]));
cell = document.createElement("td");
row.appendChild(cell);
if (contact["c_mail"])
cell.appendChild(document.createTextNode(contact["c_mail"]));
cell = document.createElement("td");
row.appendChild(cell);
if (contact["c_screenname"])
cell.appendChild(document.createTextNode(contact["c_screenname"]));
cell = document.createElement("td");
row.appendChild(cell);
if (contact["c_o"])
cell.appendChild(document.createTextNode(contact["c_o"]));
cell = document.createElement("td");
row.appendChild(cell);
if (contact["c_telephonenumber"])
cell.appendChild(document.createTextNode(contact["c_telephonenumber"]));
}
} }
else {
// Add table // Remove unnecessary rows
div.update(http.responseText); for (i = rows.length - 1; i >= data.length; i--) {
table = $("contactsList"); tbody.removeChild(rows[i]);
table.multiselect = true;
table.observe("mousedown", onContactSelectionChange);
configureSortableTableHeaders(table);
TableKit.Resizable.init(table, {'trueResize' : true, 'keepWidth' : true});
configureDraggables();
resetCategoriesMenu();
}
var rows = table.tBodies[0].rows;
for (var i = 0; i < rows.length; i++) {
var row = $(rows[i]);
row.observe("mousedown", onRowClick);
row.observe("dblclick", onContactRowDblClick);
row.observe("selectstart", listRowMouseDownHandler);
row.observe("contextmenu", onContactContextMenu);
} }
if (sorting["attribute"] && sorting["attribute"].length > 0) { if (sorting["attribute"] && sorting["attribute"].length > 0) {
@ -142,26 +171,30 @@ function contactsListCallback(http) {
var sortImage = createElement("img", "messageSortImage", "sortImage"); var sortImage = createElement("img", "messageSortImage", "sortImage");
sortHeader.insertBefore(sortImage, sortHeader.firstChild); sortHeader.insertBefore(sortImage, sortHeader.firstChild);
if (sorting["ascending"]) if (sorting["ascending"])
sortImage.src = ResourcesURL + "/arrow-down.png";
else
sortImage.src = ResourcesURL + "/arrow-up.png"; sortImage.src = ResourcesURL + "/arrow-up.png";
else
sortImage.src = ResourcesURL + "/arrow-down.png";
} }
} }
var selected = http.callbackData; // Restore selection and scroll to first selected node
if (selected) { tbody.refreshSelectionByIds();
for (var i = 0; i < selected.length; i++) { var selection = http.callbackData;
var row = $(selected[i]); if (selection) {
for (var i = 0; i < selection.length; i++) {
var row = $(selection[i]);
if (row) { if (row) {
var rowPosition = row.rowIndex * row.getHeight(); var rowPosition = row.rowIndex * row.getHeight();
if (div.getHeight() < rowPosition) if (div.getHeight() < rowPosition)
div.scrollTop = rowPosition; // scroll to selected contact div.scrollTop = rowPosition; // scroll to selected contact
row.selectElement(); break;
} }
} }
} }
} }
else { else {
// No more access to this address book; empty the list
var table = $("contactsList"); var table = $("contactsList");
if (table) { if (table) {
var sortImages = $(table.tHead).select(".sortImage"); var sortImages = $(table.tHead).select(".sortImage");
@ -349,7 +382,8 @@ function moveTo(uri) {
/* contact menu entries */ /* contact menu entries */
function onContactRowDblClick(event) { function onContactRowDblClick(event) {
var cname = this.getAttribute('id'); var t = getTarget(event);
var cname = t.parentNode.getAttribute('id');
openContactWindow(URLForFolderID(Contact.currentAddressBook) openContactWindow(URLForFolderID(Contact.currentAddressBook)
+ "/" + cname + "/edit", cname); + "/" + cname + "/edit", cname);
@ -358,7 +392,13 @@ function onContactRowDblClick(event) {
} }
function onContactSelectionChange(event) { function onContactSelectionChange(event) {
var rows = this.getSelectedRowsId(); if (event) {
// Update rows selection
var t = getTarget(event);
onRowClick(event, t);
}
var rows = this.parentNode.getSelectedRowsId();
if (rows.length == 1) { if (rows.length == 1) {
var node = $(rows[0]); var node = $(rows[0]);
@ -446,8 +486,9 @@ function onToolbarDeleteSelectedContactsConfirm(dialogId) {
var contactsList = $('contactsList'); var contactsList = $('contactsList');
var rows = contactsList.getSelectedRowsId(); var rows = contactsList.getSelectedRowsId();
for (var i = 0; i < rows.length; i++) { for (var i = 0; i < rows.length; i++) {
// hide row? var row = $(rows[i]);
$(rows[i]).hide(); row.deselect();
row.hide();
delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]]; delete cachedContacts[Contact.currentAddressBook + "/" + rows[i]];
var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/" var urlstr = (URLForFolderID(Contact.currentAddressBook) + "/"
+ rows[i] + "/delete"); + rows[i] + "/delete");
@ -478,7 +519,6 @@ function onContactDeleteEventCallback(http) {
loadContact(Contact.currentContact); loadContact(Contact.currentContact);
} }
} }
row.deselect();
row.parentNode.removeChild(row); row.parentNode.removeChild(row);
} }
else if (parseInt(http.status) == 403) { else if (parseInt(http.status) == 403) {
@ -587,7 +627,7 @@ function onConfirmContactSelection(event) {
currentAddressBookName, cid); currentAddressBookName, cid);
} }
else { else {
var cname = '' + rows[i].getAttribute("contactname"); var cname = '' + rows[i].readAttribute("contactname");
var email = '' + rows[i].cells[1].innerHTML; var email = '' + rows[i].cells[1].innerHTML;
window.opener.addContact(tag, currentAddressBookName + '/' + cname, window.opener.addContact(tag, currentAddressBookName + '/' + cname,
@ -901,9 +941,8 @@ function configureAddressBooks() {
lookupDeniedFolders(); lookupDeniedFolders();
configureDroppables(); configureDroppables();
var personalFolder = $("/personal"); // Select initial addressbook
personalFolder.selectElement(); $(Contact.currentAddressBook).selectElement();
openContactsFolder("/personal");
} }
} }
@ -1207,7 +1246,7 @@ function onDocumentKeydown(event) {
viewPort.scrollTop += rowBottom - divBottom; viewPort.scrollTop += rowBottom - divBottom;
else if (rowScrollOffset.top > rowPosition.top) else if (rowScrollOffset.top > rowPosition.top)
viewPort.scrollTop -= rowScrollOffset.top - rowPosition.top; viewPort.scrollTop -= rowScrollOffset.top - rowPosition.top;
// Select and load the next message // Select and load the next message
nextRow.selectElement(); nextRow.selectElement();
loadContact(nextRow.readAttribute("id")); loadContact(nextRow.readAttribute("id"));
@ -1254,15 +1293,19 @@ function initContacts(event) {
var table = $("contactsList"); var table = $("contactsList");
if (table) { if (table) {
// Initialize contacts table // Initialize event delegation on contacts table
table.multiselect = true; table.multiselect = true;
table.observe("mousedown", onContactSelectionChange); var tbody = table.tBodies[0];
tbody.on("mousedown", onContactSelectionChange);
tbody.on("dblclick", onContactRowDblClick);
tbody.on("selectstart", listRowMouseDownHandler);
tbody.on("contextmenu", onContactContextMenu);
configureSortableTableHeaders(table); configureSortableTableHeaders(table);
TableKit.Resizable.init(table, {'trueResize' : true, 'keepWidth' : true}); TableKit.Resizable.init(table, {'trueResize' : true, 'keepWidth' : true});
resetCategoriesMenu(); resetCategoriesMenu();
} }
onWindowResize.defer(); onWindowResize.defer();
Event.observe(window, "resize", onWindowResize); Event.observe(window, "resize", onWindowResize);
@ -1287,8 +1330,7 @@ function resetCategoriesMenu() {
var catName = UserDefaults["SOGoContactsCategories"][i]; var catName = UserDefaults["SOGoContactsCategories"][i];
if (catName.length > 0) { if (catName.length > 0) {
var menuLI = createElement("li"); var menuLI = createElement("li");
var bound = onCategoriesMenuItemClick.bindAsEventListener(menuLI); menuLI.observe("click", onCategoriesMenuItemClick);
menuLI.observe("click", bound);
menuLI.category = catName; menuLI.category = catName;
menuLI.appendChild(document.createTextNode(catName)); menuLI.appendChild(document.createTextNode(catName));
menuUL.appendChild(menuLI); menuUL.appendChild(menuLI);
@ -1307,7 +1349,7 @@ function onCategoriesMenuPrepareVisibility() {
if (contactsList) { if (contactsList) {
var rows = contactsList.getSelectedRows(); var rows = contactsList.getSelectedRows();
if (rows.length > 0) { if (rows.length > 0) {
var catList = rows[0].getAttribute("categories"); var catList = rows[0].readAttribute("categories");
var catsArray; var catsArray;
if (catList && catList.length > 0) { if (catList && catList.length > 0) {
catsArray = catList.split(","); catsArray = catList.split(",");
@ -1315,7 +1357,6 @@ function onCategoriesMenuPrepareVisibility() {
else { else {
catsArray = []; catsArray = [];
} }
var menu = $("categoriesMenu"); var menu = $("categoriesMenu");
var ul = menu.down("ul"); var ul = menu.down("ul");
var listElements = ul.select("li"); var listElements = ul.select("li");
@ -1365,7 +1406,7 @@ function onCategoriesMenuItemCallback(http) {
&& contact.id == Contact.currentContact) && contact.id == Contact.currentContact)
loadContact(Contact.currentContact); loadContact(Contact.currentContact);
} }
else { else if (parseInt(http.status) == 403) {
log("onCategoriesMenuItemCallback failed: error " + http.status + " (" + http.responseText + ")"); log("onCategoriesMenuItemCallback failed: error " + http.status + " (" + http.responseText + ")");
} }
} }