+2007-12-12 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * SoObjects/SOGo/NSString+Utilities.m ([NSString
+ -stringByDetectingURLs]): we now go back until the real start of
+ the found url.
+
+ * SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
+ -fetchAttachmentIds]): new method that wanders through the mail
+ structure to collect the attachment content ids and to associate
+ them with their url.
+
+ * UI/MailPartViewers/UIxMailPartHTMLViewer.m
+ ([_UIxHTMLMailContentHandler
+ -startElement:_localNamenamespace:_nsrawName:_rawNameattributes:_attributes]):
+ the content-ids are now enclosed between "<>" before retrieval
+ from the attachment dictionary.
+ ([UIxMailPartHTMLViewer -cssContent])
+ ([UIxMailPartHTMLViewer -flatContentAsString]): the content-ids
+ are now fetch from the clientobject (an instance of
+ SOGoMailObject) with the new "fetchAttachmentIds" method.
+
+2007-12-12 Francis Lachapelle <flachapelle@inverse.ca>
+
+ * UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions
+ -eventsListAction]): added the state of the calendar's owner with
+ respect to the current event.
+
+ * UI/Scheduler/UIxComponentEditor.m ([UIxComponentEditor
+ -_loadAttendees]): added the retrieval of attendees state on
+ current event.
+
2007-12-12 Ludovic Marcotte <ludovic@inverse.ca>
* UI/MailPartViewers/UIxMailRenderingContext.{h,m}
- (NSDictionary *) fetchPlainTextParts;
- (NSDictionary *) fetchPlainTextStrings:(NSArray *)_fetchKeys;
+- (NSDictionary *) fetchAttachmentIds;
+
/* flags */
- (NSException *) addFlags:(id)_f;
#import <Foundation/NSDictionary.h>
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSString.h>
+#import <Foundation/NSURL.h>
#import <Foundation/NSUserDefaults.h>
#import <Foundation/NSValue.h>
return [self fetchPlainTextParts: [self plainTextContentFetchKeys]];
}
+- (NSString *) _urlToPart: (NSDictionary *) infos
+ withPrefix: (NSString *) urlPrefix
+{
+ NSDictionary *parameters;
+ NSString *urlToPart, *filename;
+
+ parameters = [infos objectForKey: @"parameterList"];
+ filename = [parameters objectForKey: @"name"];
+ if (!filename)
+ {
+ parameters = [[infos objectForKey: @"disposition"]
+ objectForKey: @"parameterList"];
+ filename = [parameters objectForKey: @"filename"];
+ }
+
+ if ([filename length])
+ urlToPart = [NSString stringWithFormat: @"%@/%@", urlPrefix, filename];
+ else
+ urlToPart = nil;
+
+ return urlToPart;
+}
+
+- (void) _feedAttachmentIds: (NSMutableDictionary *) attachmentIds
+ withInfos: (NSDictionary *) infos
+ andPrefix: (NSString *) prefix
+{
+ NSArray *parts;
+ NSDictionary *currentPart;
+ unsigned int count, max;
+ NSString *url, *cid;
+
+ cid = [infos objectForKey: @"bodyId"];
+ if ([cid length])
+ {
+ url = [self _urlToPart: infos withPrefix: prefix];
+ if (url)
+ [attachmentIds setObject: url forKey: cid];
+ }
+
+ parts = [infos objectForKey: @"parts"];
+ max = [parts count];
+ for (count = 0; count < max; count++)
+ {
+ currentPart = [parts objectAtIndex: count];
+ [self _feedAttachmentIds: attachmentIds
+ withInfos: currentPart
+ andPrefix: [NSString stringWithFormat: @"%@/%d",
+ prefix, count + 1]];
+ }
+}
+
+- (NSDictionary *) fetchAttachmentIds
+{
+ NSMutableDictionary *attachmentIds;
+
+ attachmentIds = [NSMutableDictionary dictionary];
+
+ [self fetchCoreInfos];
+ [self _feedAttachmentIds: attachmentIds
+ withInfos: [coreInfos objectForKey: @"body"]
+ andPrefix: [[self soURL] absoluteString]];
+
+ return attachmentIds;
+}
+
/* convert parts to strings */
- (NSString *) stringForData: (NSData *) _data
partInfo: (NSDictionary *) _info
static NSMutableCharacterSet *urlNonEndingChars = nil;
static NSMutableCharacterSet *urlAfterEndingChars = nil;
+static NSMutableCharacterSet *urlStartChars = nil;
@implementation NSString (SOGoURLExtension)
if (!urlNonEndingChars)
{
urlNonEndingChars = [NSMutableCharacterSet new];
- [urlNonEndingChars addCharactersInString: @">=,.:;\t \r\n"];
+ [urlNonEndingChars addCharactersInString: @"=,.:;\t \r\n"];
}
if (!urlAfterEndingChars)
{
urlAfterEndingChars = [NSMutableCharacterSet new];
- [urlAfterEndingChars addCharactersInString: @"[]\t \r\n"];
+ [urlAfterEndingChars addCharactersInString: @"&;[]\t \r\n"];
}
start = refRange.location;
NSRange httpRange, currentURL, rest;
NSString *urlText, *newUrlText;
unsigned int length, matchLength;
+ int startLocation;
+ if (!urlStartChars)
+ {
+ urlStartChars = [NSMutableCharacterSet new];
+ [urlStartChars addCharactersInString: @"abcdefghijklmnopqrstuvwxyz"
+ @"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ @"0123456789:@"];
+ }
matchLength = [match length];
httpRange = [selfCopy rangeOfString: match];
+ if (httpRange.location != NSNotFound)
+ {
+ startLocation = httpRange.location;
+ while (startLocation > -1
+ && [urlStartChars characterIsMember:
+ [selfCopy characterAtIndex: startLocation]])
+ startLocation--;
+ httpRange.location = startLocation + 1;
+ }
while (httpRange.location != NSNotFound)
{
if ([ranges hasRangeIntersection: httpRange])
#include <libxml/encoding.h>
+#import <SoObjects/Mailer/SOGoMailObject.h>
+
#import "UIxMailPartHTMLViewer.h"
#if 0
attributes: (id <SaxAttributes>) _attributes
{
unsigned int count, max;
- NSString *name, *value;
+ NSString *name, *value, *cid;
NSMutableString *resultPart;
BOOL skipAttribute;
value = [_attributes valueAtIndex: count];
if ([value hasPrefix: @"cid:"])
{
- value = [attachmentIds
- objectForKey: [value substringFromIndex: 4]];
+ cid = [NSString stringWithFormat: @"<%@>",
+ [value substringFromIndex: 4]];
+ value = [attachmentIds objectForKey: cid];
skipAttribute = (value == nil);
}
else
[super dealloc];
}
-- (void) _convertReferencesForPart: (NSDictionary *) part
- withCount: (unsigned int) count
- andBaseURL: (NSString *) url
- intoDictionary: (NSMutableDictionary *) attachmentIds
-{
- NSString *bodyId, *filename;
- NSMutableString *attachmentURL;
-
- bodyId = [part objectForKey: @"bodyId"];
- if ([bodyId length] > 0)
- {
- filename = [[part objectForKey: @"parameterList"] objectForKey: @"name"];
- if (!filename)
- filename = [[[part objectForKey: @"disposition"]
- objectForKey: @"parameterList"]
- objectForKey: @"filename"];
- if ([bodyId hasPrefix: @"<"])
- bodyId = [bodyId substringFromIndex: 1];
- if ([bodyId hasSuffix: @">"])
- bodyId = [bodyId substringToIndex: [bodyId length] - 1];
- attachmentURL = [NSMutableString stringWithString: url];
- [attachmentURL appendFormat: @"/%d", count];
- if ([filename length])
- [attachmentURL appendFormat: @"/%@", filename];
- [attachmentIds setObject: attachmentURL forKey: bodyId];
- }
-}
-
-- (NSDictionary *) _attachmentIds
-{
- NSMutableDictionary *attachmentIds;
- UIxMailPartViewer *parent;
- unsigned int count, max;
-// NSMutableString *url;
- NSString *baseURL;
- NSArray *parts;
-
- attachmentIds = [NSMutableDictionary new];
- [attachmentIds autorelease];
-
- parent = [self parent];
- if ([NSStringFromClass ([parent class])
- isEqualToString: @"UIxMailPartAlternativeViewer"])
- {
- baseURL = [[self clientObject] baseURLInContext: context];
-// url = [NSMutableString new];
-// [url appendString: baseURL];
-// [url appendFormat: @"/%@", [partPath componentsJoinedByString: @"/"]];
-// [url deleteCharactersInRange: NSMakeRange([url length] - 4, 4)];
- parts = [[[parent parent] bodyInfo] objectForKey: @"parts"];
- max = [parts count];
- for (count = 0; count < max; count++)
- [self _convertReferencesForPart: [parts objectAtIndex: count]
- withCount: count + 1
- andBaseURL: baseURL
- intoDictionary: attachmentIds];
-// [url release];
- }
-
- return attachmentIds;
-}
-
- (xmlCharEncoding) _xmlCharsetForCharset: (NSString *) charset
{
struct { NSString *name; xmlCharEncoding encoding; } xmlEncodings[] = {
{
NSObject <SaxXMLReader> *parser;
NSData *preparsedContent;
+ SOGoMailObject *mail;
+
+ mail = [self clientObject];
preparsedContent = [super decodedFlatContent];
parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
createXMLReaderForMimeType: @"text/html"];
handler = [_UIxHTMLMailContentHandler new];
- [handler setAttachmentIds: [self _attachmentIds]];
+ [handler setAttachmentIds: [mail fetchAttachmentIds]];
[handler setContentEncoding: [self _xmlCharEncoding]];
[parser setContentHandler: handler];
[parser parseFromSource: preparsedContent];
superContent = [[super flatContentAsString] stringByEscapingHTMLString];
- return [[superContent stringByDetectingURLs] stringByConvertingCRLNToHTML];
+ return [[superContent stringByDetectingURLs]
+ stringByConvertingCRLNToHTML];
}
@end /* UIxMailPartTextViewer */
"Create Filter From Message..." = "Filter aus Nachricht erstellen...";
/* Image Popup menu */
-"Save Image" = "Save Image";
+"Save Image" = "Bild speichern";
/* Mailbox popup menus */
"Open in New Mail Window" = "In neuem Fenster öffnen";
#import <NGObjWeb/WORequest.h>
#import <NGObjWeb/WOResponse.h>
#import <NGExtensions/NSCalendarDate+misc.h>
+#import <NGCards/iCalPerson.h>
#import <SoObjects/SOGo/SOGoUser.h>
#import <SoObjects/SOGo/SOGoDateFormatter.h>
- (WOResponse *) eventsListAction
{
- NSArray *fields, *oldEvent;
+ NSArray *fields, *oldEvent, *participants, *states;
NSEnumerator *events;
NSMutableArray *newEvents, *newEvent;
- unsigned int interval;
+ unsigned int interval, i;
BOOL isAllDay;
- NSString *sort, *ascending;
+ NSString *sort, *ascending, *participant, *state;
+ SOGoUser *user;
[self _setupContext];
newEvents = [NSMutableArray array];
fields = [NSArray arrayWithObjects: @"c_name", @"c_folder", @"c_status",
@"c_title", @"c_startdate", @"c_enddate", @"c_location",
- @"c_isallday", @"c_classification", nil];
+ @"c_isallday", @"c_classification", @"c_partmails", @"c_partstates", nil];
events = [[self _fetchFields: fields
forComponentOfType: @"vevent"] objectEnumerator];
+ user = [[self context] activeUser];
oldEvent = [events nextObject];
while (oldEvent)
{
forAllDay: isAllDay]];
[newEvent addObject: [self _formattedDateForSeconds: interval
forAllDay: isAllDay]];
+
+ participants = state = nil;
+ if ([[oldEvent objectAtIndex: 9] length] > 0 &&
+ [[oldEvent objectAtIndex: 10] length] > 0) {
+ participants = [[oldEvent objectAtIndex: 9] componentsSeparatedByString: @"\n"];
+ states = [[oldEvent objectAtIndex: 10] componentsSeparatedByString: @"\n"];
+ for (i = 0; i < [participants count]; i++) {
+ participant = [participants objectAtIndex: i];
+ if ([user hasEmail: participant]) {
+ switch ([[states objectAtIndex: i] intValue]) {
+ case iCalPersonPartStatNeedsAction:
+ state = @"needs-action";
+ break;
+ case iCalPersonPartStatAccepted:
+ state = @"accepted";
+ break;
+ case iCalPersonPartStatDeclined:
+ state = @"declined";
+ break;
+ }
+ [newEvent replaceObjectAtIndex: 9 withObject: state];
+ break;
+ }
+ }
+ }
+ if (participants == nil || i == [participants count])
+ [newEvent replaceObjectAtIndex: 9 withObject: @""];
+ [newEvent removeObjectAtIndex: 10];
+
[newEvents addObject: newEvent];
oldEvent = [events nextObject];
NSString *attendeesNames;
NSString *attendeesUIDs;
NSString *attendeesEmails;
+ NSString *attendeesStates;
}
- (NSString *) toolbar;
attendeesNames = nil;
attendeesUIDs = nil;
attendeesEmails = nil;
+ attendeesStates = nil;
calendarList = nil;
}
[attendeesNames release];
[attendeesUIDs release];
[attendeesEmails release];
+ [attendeesStates release];
[calendarList release];
[component release];
{
NSEnumerator *attendees;
iCalPerson *currentAttendee;
- NSMutableString *names, *uids, *emails;
+ NSMutableString *names, *uids, *emails, *states;
NSString *uid;
LDAPUserManager *um;
names = [NSMutableString new];
uids = [NSMutableString new];
emails = [NSMutableString new];
+ states = [NSMutableString new];
um = [LDAPUserManager sharedUserManager];
attendees = [[component attendees] objectEnumerator];
[uids appendFormat: @"%@,", uid];
else
[uids appendString: @","];
+ [states appendFormat: @"%@,", [[currentAttendee partStat] lowercaseString]];
currentAttendee = [attendees nextObject];
}
ASSIGN (attendeesUIDs, [uids substringToIndex: [uids length] - 1]);
ASSIGN (attendeesEmails,
[emails substringToIndex: [emails length] - 1]);
+ ASSIGN (attendeesStates, [states substringToIndex: [states length] - 1]);
}
[names release];
return attendeesEmails;
}
+- (void) setAttendeesStates: (NSString *) newAttendeesStates
+{
+ ASSIGN (attendeesStates, newAttendeesStates);
+}
+
+- (NSString *) attendeesStates
+{
+ return attendeesStates;
+}
+
- (void) setLocation: (NSString *) _value
{
ASSIGN (location, _value);
var:value="attendeesUIDs"/>
<input type="hidden" name="attendeesEmails" id="attendeesEmails"
var:value="attendeesEmails"/>
+ <input type="hidden" name="attendeesStates" id="attendeesStates"
+ var:value="attendeesStates"/>
<input type="hidden" name="calendarFoldersList"
id="calendarFoldersList"
var:value="calendarsFoldersList"/>
DIV.event DIV.text
{ font-size: 92%; }
+DIV.event.needs-action DIV.text
+{ background-image: url("needs-action.png");
+ background-repeat: no-repeat;
+ background-position: 98% 95%; }
+
+DIV.event.accepted DIV.text
+{ background-image: url("accepted.png");
+ background-repeat: no-repeat;
+ background-position: 98% 95%; }
+
+DIV.event.declined DIV.text
+{ background-image: url("declined.png");
+ background-repeat: no-repeat;
+ background-position: 98% 95%; }
+
+DIV.event.tentative DIV.text
+{ background-image: url("tentative.png");
+ background-repeat: no-repeat;
+ background-position: 98% 95%; }
+
DIV#daysView DIV[class~="event"].starts0
{ top: 0.000000%; }
null, null, title);
siblings.push(eventDiv);
eventDiv.siblings = siblings;
+ if (eventData[9].length > 0)
+ eventDiv.addClassName(eventData[9]);
var dayString = days[i].getDayString();
// log("day: " + dayString);
var parentDiv = null;
top: 2em;
bottom: 14.5em;
left: 13em;
+ right: 0px;
overflow: auto;
border-top: 2px solid #222;
border-left: 2px solid #222;
top: 0.5em; }
TABLE#freeBusy TD.attendees INPUT
-{ background-image: url('/SOGo.woa/WebServerResources/abcard.gif');
+{ background-image: url("abcard.gif");
background-repeat: no-repeat;
background-position: 2px center;
width: 11.5em;
padding-left: 24px;
margin-left: 5px; }
+TABLE#freeBusy TR.needs-action TD.attendees
+{ background-image: url("needs-action.png");
+ background-repeat: no-repeat;
+ background-position: 5px center; }
+
+TABLE#freeBusy TR.declined TD.attendees
+{ background-image: url("declined.png");
+ background-repeat: no-repeat;
+ background-position: 5px center; }
+
+TABLE#freeBusy TR.accepted TD.attendees
+{ background-image: url("accepted.png");
+ background-repeat: no-repeat;
+ background-position: 5px center; }
+
+TABLE#freeBusy TR.needs-action INPUT,
+TABLE#freeBusy TR.accepted INPUT,
+TABLE#freeBusy TR.declined INPUT
+{ margin-left: 1.5em;
+ width: 10em; }
+
TABLE#freeBusy TR.futureAttendee INPUT
{ background-image: none; }
var resultsDiv;
-var running = false;
var address;
-var delay = 500;
-var requestField;
-var searchField;
+var delayedSearch = false;
+var currentField;
var awaitingFreeBusyRequests = new Array();
var additionalDays = 2;
var dayStartHour = 8;
var dayEndHour = 18;
-var attendeesNames;
-var attendeesUIDs;
-var attendeesEmails;
+var attendeesEditor = {
+ delay: 500,
+ names: null,
+ UIDs: null,
+ emails: null,
+ states: null
+};
function onContactKeydown(event) {
if (event.ctrlKey || event.metaKey) {
preventDefault(event);
if (this.confirmedValue)
this.value = this.confirmedValue;
- var row = this.parentNode.parentNode.nextSibling;
- while (row && row.tagName != 'TR')
- row = row.nextSibling;
- this.blur();
- var input = $(row.cells[0]).childNodesWithTag("input")[0];
+ this.hasfreebusy = false;
+ var row = $(this).up("tr").next();
+ this.blur(); // triggers checkAttendee function call
+ var input = row.down("input");
if (input.readOnly)
newAttendee(null);
else {
input.activate();
}
}
- else if (!running) {
- if (event.keyCode == 0
+ else if (event.keyCode == 0
|| event.keyCode == 8 // Backspace
|| event.keyCode == 32 // Space
|| event.keyCode > 47) {
- running = true;
- requestField = this;
- requestField.setAttribute("modified", "1");
- if (searchField) {
- searchField.confirmedValue = null;
- searchField.uid = null;
+ this.setAttribute("modified", "1");
+ this.confirmedValue = null;
+ this.uid = null;
+ this.hasfreebusy = false;
+ currentField = this;
+ if (this.value.length > 0 && !delayedSearch) {
+ delayedSearch = true;
+ setTimeout("performSearch()", attendeesEditor.delay);
}
- setTimeout("triggerRequest()", delay);
- }
- else if (this.confirmedValue) {
- if (event.keyCode == 13) { // Enter
- $(this).setCaretTo(this.value.length);
- }
- }
}
+ else if (this.confirmedValue)
+ if (event.keyCode == 13) // Enter
+ $(this).setCaretTo(this.value.length);
}
-function triggerRequest() {
- if (requestField) {
+function performSearch() {
+ if (currentField) {
if (document.contactLookupAjaxRequest) {
- document.contactLookupAjaxRequest.aborted = yes;
+ // Abort any pending request
+ document.contactLookupAjaxRequest.aborted = true;
document.contactLookupAjaxRequest.abort();
}
- var urlstr = ( UserFolderURL + "Contacts/contactSearch?search="
- + escape(requestField.value) );
- document.contactLookupAjaxRequest = triggerAjaxRequest(urlstr,
- updateResults,
- requestField);
+ if (currentField.value.trim().length > 0) {
+ var urlstr = ( UserFolderURL + "Contacts/contactSearch?search="
+ + escape(currentField.value) ); log (urlstr);
+ document.contactLookupAjaxRequest =
+ triggerAjaxRequest(urlstr, performSearchCallback, currentField);
+ }
}
+ delayedSearch = false;
}
-function updateResults(http) {
+function performSearchCallback(http) {
if (http.readyState == 4) {
var menu = $('attendeesMenu');
var list = menu.down("ul");
-
- searchField = http.callbackData; // requestField
- searchField.hasfreebusy = false;
- searchField.setAttribute("uid", null);
+
+ var input = http.callbackData;
if (http.status == 200) {
- var start = searchField.value.length;
+ var start = input.value.length;
var data = http.responseText.evalJSON(true);
if (data.length > 1) {
$(list.childNodesWithTag("li")).each(function(item) {
var completeEmail = contact["name"] + " <" + contact["email"] + ">";
var node = document.createElement("li");
list.appendChild(node);
- node.setAttribute("uid", contact["uid"]);
+ node.uid = contact["uid"];
node.appendChild(document.createTextNode(completeEmail));
$(node).observe("mousedown", onAttendeeResultClick);
}
// Show popup menu
var offset;
if (isSafari())
- offset = Position.positionedOffset(searchField);
+ offset = Position.positionedOffset(currentField);
else
- offset = Position.cumulativeOffset(searchField);
+ offset = Position.cumulativeOffset(currentField);
var top = offset[1] + node.offsetHeight + 3;
var height = 'auto';
if (data.length > 5) {
menu.setStyle({ top: top + "px",
left: offset[0] + "px",
height: height,
- visibility: "visible" });
+ visibility: "visible" });
menu.scrollTop = 0;
document.currentPopupMenu = menu;
hideMenu(document.currentPopupMenu);
if (data.length == 1) {
+ // Single result
var contact = data[0];
if (contact["uid"].length > 0)
- searchField.setAttribute("uid", contact["uid"]);
+ input.uid = contact["uid"];
var completeEmail = contact["name"] + " <" + contact["email"] + ">";
- if (contact["name"].substring(0, searchField.value.length).toUpperCase()
- == searchField.value.toUpperCase())
- searchField.value = completeEmail;
- else {
- searchField.value += ' >> ' + completeEmail;
- }
- searchField.confirmedValue = completeEmail;
- if (searchField.focussed) {
- var end = searchField.value.length;
- $(searchField).selectText(start, end);
+ if (contact["name"].substring(0, input.value.length).toUpperCase()
+ == input.value.toUpperCase())
+ input.value = completeEmail;
+ else
+ // The result matches email address, not user name
+ input.value += ' >> ' + completeEmail;
+ input.confirmedValue = completeEmail;
+ if (input.focussed) {
+ var end = input.value.length;
+ $(input).selectText(start, end);
}
else
- searchField.value = contact["name"];
+ input.value = contact["name"];
}
}
}
else
if (document.currentPopupMenu)
hideMenu(document.currentPopupMenu);
- running = false;
document.contactLookupAjaxRequest = null;
}
}
function onAttendeeResultClick(event) {
- if (searchField) {
- searchField.setAttribute("uid", this.getAttribute("uid"));
- searchField.value = this.firstChild.nodeValue.trim();
- searchField.confirmedValue = searchField.value;
- searchField.blur(); // triggers checkAttendee function call
+ if (currentField) {
+ currentField.uid = this.uid;
+ currentField.value = this.firstChild.nodeValue.trim();
+ currentField.confirmedValue = currentField.value;
+ currentField.blur(); // triggers checkAttendee function call
}
}
var newRow = model.cloneNode(true);
tbody.insertBefore(newRow, newAttendeeRow);
- $(newRow).className = "";
-
- var input = $(newRow.cells[0]).childNodesWithTag("input")[0];
- input.setAttribute("autocomplete", "off");
- Event.observe(input, "keydown", onContactKeydown.bindAsEventListener(input));
- Event.observe(input, "blur", checkAttendee.bindAsEventListener(input));
+ $(newRow).removeClassName("attendeeModel");
+
+ var input = $(newRow).down("input");
+ input.observe("keydown", onContactKeydown);
+ input.observe("blur", checkAttendee);
input.focussed = true;
input.activate();
if (visible)
return;
}
-
+
this.focussed = false;
var row = this.parentNode.parentNode;
var tbody = row.parentNode;
if (tbody && this.value.trim().length == 0)
tbody.removeChild(row);
- else if (!this.hasfreebusy) {
- if (this.confirmedValue)
- this.value = this.confirmedValue;
- displayFreeBusyForNode(this);
- this.hasfreebusy = true;
+ else if (this.readAttribute("modified") == "1") {
+ if (!$(row).hasClassName("needs-action")) {
+ $(row).addClassName("needs-action");
+ $(row).removeClassName("declined");
+ $(row).removeClassName("accepted");
+ }
+ if (!this.hasfreebusy) {
+ if (this.uid && this.confirmedValue)
+ this.value = this.confirmedValue;
+ displayFreeBusyForNode(this);
+ this.hasfreebusy = true;
+ }
+ this.setAttribute("modified", "0");
}
-
- requestField = null;
- searchField = null;
+
+ currentField = null;
}
function displayFreeBusyForNode(node) {
var nodes = node.parentNode.parentNode.cells;
- if (node.getAttribute("uid")) {
+ if (node.uid) {
if (document.contactFreeBusyAjaxRequest)
awaitingFreeBusyRequests.push(node);
else {
+ '<span class="freeBusyZoneElement"></span>');
}
if (document.contactFreeBusyAjaxRequest) {
+ // Abort any pending request
document.contactFreeBusyAjaxRequest.aborted = true;
document.contactFreeBusyAjaxRequest.abort();
}
var sd = $('startTime_date').valueAsShortDateString();
var ed = $('endTime_date').valueAsShortDateString();
- var urlstr = ( UserFolderURL + "../" + node.getAttribute("uid") + "/freebusy.ifb/ajaxRead?"
+ var urlstr = ( UserFolderURL + "../" + node.uid + "/freebusy.ifb/ajaxRead?"
+ "sday=" + sd + "&eday=" + ed + "&additional=" +
additionalDays );
document.contactFreeBusyAjaxRequest
= triggerAjaxRequest(urlstr,
- updateFreeBusyData,
+ updateFreeBusyDataCallback,
node);
}
} else {
}
}
-function updateFreeBusyData(http) {
+function updateFreeBusyDataCallback(http) {
if (http.readyState == 4) {
if (http.status == 200) {
var node = http.callbackData;
function onEditorOkClick(event) {
preventDefault(event);
- attendeesNames = new Array();
- attendeesUIDs = new Array();
- attendeesEmails = new Array();
-
+ attendeesEditor.names = new Array();
+ attendeesEditor.UIDs = new Array();
+ attendeesEditor.emails = new Array();
+ attendeesEditor.states = new Array();
+
var table = $("freeBusy");
var inputs = table.getElementsByTagName("input");
for (var i = 0; i < inputs.length - 2; i++) {
+ var row = $(inputs[i]).up("tr");
var name = extractEmailName(inputs[i].value);
var email = extractEmailAddress(inputs[i].value);
var uid = "";
- if (inputs[i].getAttribute("uid"))
- uid = inputs[i].getAttribute("uid");
+ if (inputs[i].uid)
+ uid = inputs[i].uid;
if (!(name && name.length > 0))
if (inputs[i].uid)
name = inputs[i].uid;
else
name = email;
- var pos = attendeesEmails.indexOf(email);
+ var state = "needs-action";
+ if (row.hasClassName("accepted"))
+ state = "accepted";
+ else if (row.hasClassName("declined"))
+ state = "declined";
+ var pos = attendeesEditor.emails.indexOf(email);
if (pos == -1)
- pos = attendeesEmails.length;
- attendeesNames[pos] = name;
- attendeesUIDs[pos] = uid;
- attendeesEmails[pos] = email;
+ pos = attendeesEditor.emails.length;
+ attendeesEditor.names[pos] = name;
+ attendeesEditor.UIDs[pos] = uid;
+ attendeesEditor.emails[pos] = email;
+ attendeesEditor.states[pos] = state;
}
- parent$("attendeesNames").value = attendeesNames.join(",");
- parent$("attendeesUIDs").value = attendeesUIDs.join(",");
- parent$("attendeesEmails").value = attendeesEmails.join(",");
+ parent$("attendeesNames").value = attendeesEditor.names.join(",");
+ parent$("attendeesUIDs").value = attendeesEditor.UIDs.join(",");
+ parent$("attendeesEmails").value = attendeesEditor.emails.join(",");
+ parent$("attendeesStates").value = attendeesEditor.states.join(",");
window.opener.refreshAttendees();
updateParentDateFields("startTime", "startTime");
var value = parent$("attendeesNames").value;
var table = $("freeBusy");
if (value.length > 0) {
- attendeesNames = parent$("attendeesNames").value.split(",");
- attendeesUIDs = parent$("attendeesUIDs").value.split(",");
- attendeesEmails = parent$("attendeesEmails").value.split(",");
+ attendeesEditor.names = parent$("attendeesNames").value.split(",");
+ attendeesEditor.UIDs = parent$("attendeesUIDs").value.split(",");
+ attendeesEditor.emails = parent$("attendeesEmails").value.split(",");
+ attendeesEditor.states = parent$("attendeesStates").value.split(",");
var tbody = table.tBodies[0];
var model = tbody.rows[tbody.rows.length - 1];
var newAttendeeRow = tbody.rows[tbody.rows.length - 2];
- for (var i = 0; i < attendeesNames.length; i++) {
+ for (var i = 0; i < attendeesEditor.names.length; i++) {
var row = model.cloneNode(true);
tbody.insertBefore(row, newAttendeeRow);
$(row).removeClassName("attendeeModel");
+ $(row).addClassName(attendeesEditor.states[i]);
var input = $(row).down("input");
var value = "";
- if (attendeesNames[i].length > 0 && attendeesNames[i] != attendeesEmails[i])
- value += attendeesNames[i] + " ";
- value += "<" + attendeesEmails[i] + ">";
+ if (attendeesEditor.names[i].length > 0 && attendeesEditor.names[i] != attendeesEditor.emails[i])
+ value += attendeesEditor.names[i] + " ";
+ value += "<" + attendeesEditor.emails[i] + ">";
input.value = value;
- if (attendeesUIDs[i].length > 0)
- input.setAttribute("uid", attendeesUIDs[i]);
+ if (attendeesEditor.UIDs[i].length > 0)
+ input.uid = attendeesEditor.UIDs[i];
input.setAttribute("name", "");
input.setAttribute("modified", "0");
input.observe("blur", checkAttendee);
}
}
else {
- attendeesNames = new Array();
- attendeesUIDs = new Array();
- attendeesEmails = new Array();
+ attendeesEditor.names = new Array();
+ attendeesEditor.UIDs = new Array();
+ attendeesEditor.emails = new Array();
}
var inputs = table.getElementsByTagName("input");
var contactSelectorAction = 'mailer-contacts';
var signatureLength = 0;
+
+var attachmentCount = 0;
var MailEditor = {
addressBook: null
};
onWindowResize(null);
}
var inputs = area.getElementsByTagName("input");
- var attachmentName = "attachment" + inputs.length;
+ var attachmentName = "attachment" + attachmentCount;
var newAttachment = createElement("input", attachmentName,
"currentAttachment", null,
{ type: "file",
name: attachmentName },
area);
+ attachmentCount++;
Event.observe(newAttachment, "change",
onAttachmentChange.bindAsEventListener(newAttachment));
}
TABLE#freeBusy TD.attendees INPUT
{ border-bottom: 1px solid #ccc;
- border-right: 1px solid #ccc; }
\ No newline at end of file
+ border-right: 1px solid #ccc; }
+
+TABLE#freeBusy TR.needs-action INPUT,
+TABLE#freeBusy TR.accepted INPUT,
+TABLE#freeBusy TR.declined INPUT
+{ margin-left: 2.0em;
+ width: 10.0em; }