mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-23 04:15:26 +00:00
Monotone-Parent: 19b0008e26816c6e8c6ccdb5329120a04a33f229
Monotone-Revision: 1a3634c32194a58102b74c15f2af08066e7d4e91 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2010-05-17T15:29:57 Monotone-Branch: ca.inverse.sogo
This commit is contained in:
@@ -1,5 +1,29 @@
|
||||
2010-05-17 Wolfgang Sourdeau <wsourdeau@inverse.ca>
|
||||
|
||||
* UI/WebServerResources/UIxAttendeesEditor.js
|
||||
(redisplayEventSpans): "event spans" are now stored in a cache to
|
||||
improve performance.
|
||||
(availabilityController): new class designed to handle the
|
||||
interaction with the availability widgets.
|
||||
(availabilitySession): new class, used by the above, for
|
||||
triggering availability searchs in the available freebusys.
|
||||
Listeners should implement the "onRequestComplete" method.
|
||||
(freeBusyRequest): new class design to asynchronosly fetch a user
|
||||
freebusy between specific dates. Makes use of an internal cache
|
||||
and may trigger ajax requests covering more that the specified
|
||||
range in order to take advantage of that cache. Listeners should
|
||||
implement the "onRequestComplete" method.
|
||||
(_freeBusyCacheEntry): new class, used internally by
|
||||
"freeBusyRequests" for handling cache entries.
|
||||
(initializeTimeSlotWidgets): new method that initializes the new
|
||||
widgets that controls the time range behaviour in the freebusy
|
||||
searches.
|
||||
|
||||
* UI/Scheduler/UIxCalListingActions.m (-findPossibleSlotAction):
|
||||
removed action method and submethods since the slot resolution is
|
||||
now performed directly in javascript to benefit from the freebusy
|
||||
cache and to improve performance.
|
||||
|
||||
* UI/MainUI/SOGoUserHomePage.m (-readFreeBusyAction): simplified
|
||||
method by using -[self responseWithStatus:andString:].
|
||||
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "A data que você informou ocorre antes da data ini
|
||||
"Next slot" = "Próximo espaço";
|
||||
"Previous hour" = "Hora anterior";
|
||||
"Next hour" = "Próxima hora";
|
||||
"Only office hours" = "Somente horário comercial";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Título";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "Zadané datum konce je před začátkem události.
|
||||
"Next slot" = "Následující místo";
|
||||
"Previous hour" = "Předchozí hodina";
|
||||
"Next hour" = "Následující hodina";
|
||||
"Only office hours" = "Pouze pracovní hodiny";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Název";
|
||||
|
||||
@@ -482,7 +482,10 @@ validate_endbeforestart = "Het begin vindt plaats vóór het einde.";
|
||||
"Next slot" = "Volgende";
|
||||
"Previous hour" = "Vorig uur";
|
||||
"Next hour" = "Volgend uur";
|
||||
"Only office hours" = "Only office hours";
|
||||
"Work days only" = "Slechts werkdagen";
|
||||
"The whole day" = "De hele dag";
|
||||
"During specified hours" = "Tijdens gespecificeerde uren";
|
||||
"and" = "en";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Titel";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "The end date that you entered occurs before the st
|
||||
"Next slot" = "Next slot";
|
||||
"Previous hour" = "Previous hour";
|
||||
"Next hour" = "Next hour";
|
||||
"Only office hours" = "Only office hours";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Title";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "La date de fin est avant la date de début.";
|
||||
"Next slot" = "Prochain";
|
||||
"Previous hour" = "Heure précédente";
|
||||
"Next hour" = "Prochaine heure";
|
||||
"Only office hours" = "Heures de bureau seulement";
|
||||
"Work days only" = "Seulement les jours ouvrables";
|
||||
"The whole day" = "La journée complète";
|
||||
"During office hours" = "Pendant les heures de bureau";
|
||||
"During specified hours" = "Pendant les heures spécifiées";
|
||||
"and" = "et";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Titre";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "Ihr Beginn ist nach dem Ende";
|
||||
"Next slot" = "Nächster";
|
||||
"Previous hour" = "Vorherige Stunde";
|
||||
"Next hour" = "Nächste Stunde";
|
||||
"Only office hours" = "Nur Arbeitszeit";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Titel";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "A megadott befejező dátum korábbi, mint a kezd
|
||||
"Next slot" = "Következő ablak";
|
||||
"Previous hour" = "Előző óra";
|
||||
"Next hour" = "Következő óra";
|
||||
"Only office hours" = "Csak munkaidő";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Cím";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "La data finale specificata è precedente alla data
|
||||
"Next slot" = "Successivo";
|
||||
"Previous hour" = "Ora precedente";
|
||||
"Next hour" = "Ora successiva";
|
||||
"Only office hours" = "Only office hours";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Titolo";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "The end date that you entered occurs before the st
|
||||
"Next slot" = "Next slot";
|
||||
"Previous hour" = "Previous hour";
|
||||
"Next hour" = "Next hour";
|
||||
"Only office hours" = "Only office hours";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Title";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "Su fecha/hora de comienzo es posterio a la de fina
|
||||
"Next slot" = "Siguiente ranura";
|
||||
"Previous hour" = "Hora anterior";
|
||||
"Next hour" = "Hora siguiente";
|
||||
"Only office hours" = "Only office hours";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Título";
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "Angivet slutdatumet inträffar före angivet start
|
||||
"Next slot" = "Nästa tid";
|
||||
"Previous hour" = "Föregående timme";
|
||||
"Next hour" = "Nästa timme";
|
||||
"Only office hours" = "Bara kontorstid";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Titel";
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
- (WOResponse *) alarmsListAction;
|
||||
- (WOResponse *) eventsListAction;
|
||||
- (WOResponse *) tasksListAction;
|
||||
- (WOResponse *) findPossibleSlotAction;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1068,325 +1068,4 @@ _computeBlocksPosition (NSArray *blocks)
|
||||
return [self _responseWithData: filteredTasks];
|
||||
}
|
||||
|
||||
|
||||
- (void) _fillFreeBusy: (unsigned int *) fb
|
||||
forUid: (NSString *) uid
|
||||
fromDate: (NSCalendarDate *) start
|
||||
{
|
||||
SOGoUser *user;
|
||||
SOGoFreeBusyObject *fbObject;
|
||||
NSCalendarDate *end;
|
||||
NSArray *records;
|
||||
NSDictionary *record;
|
||||
NSArray *emails, *partstates;
|
||||
NSCalendarDate *currentDate;
|
||||
unsigned int recordCount, recordMax;
|
||||
int count, startInterval, endInterval, i, type;
|
||||
int itemCount = (offsetBlocks + maxBlocks);
|
||||
|
||||
user = [SOGoUser userWithLogin: uid];
|
||||
fbObject
|
||||
= [[user homeFolderInContext: context] freeBusyObject: @"freebusy.ifb"
|
||||
inContext: context];
|
||||
|
||||
end = [start addTimeInterval: (itemCount * quarterLength)];
|
||||
|
||||
records = [fbObject fetchFreeBusyInfosFrom: start to: end];
|
||||
recordMax = [records count];
|
||||
|
||||
for (recordCount = 0; recordCount < recordMax; recordCount++)
|
||||
{
|
||||
record = [records objectAtIndex: recordCount];
|
||||
if ([[record objectForKey: @"c_isopaque"] boolValue])
|
||||
{
|
||||
type = 0;
|
||||
|
||||
// If the event has NO organizer (which means it's the user that has created it) OR
|
||||
// If we are the organizer of the event THEN we are automatically busy
|
||||
if ([[record objectForKey: @"c_orgmail"] length] == 0 ||
|
||||
[user hasEmail: [record objectForKey: @"c_orgmail"]])
|
||||
{
|
||||
type = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We check if the user has accepted/declined or needs action
|
||||
// on the current event.
|
||||
emails = [[record objectForKey: @"c_partmails"] componentsSeparatedByString: @"\n"];
|
||||
|
||||
for (i = 0; i < [emails count]; i++)
|
||||
{
|
||||
if ([user hasEmail: [emails objectAtIndex: i]])
|
||||
{
|
||||
// We now fetch the c_partstates array and get the participation
|
||||
// status of the user for the event
|
||||
partstates = [[record objectForKey: @"c_partstates"] componentsSeparatedByString: @"\n"];
|
||||
|
||||
if (i < [partstates count])
|
||||
{
|
||||
type = ([[partstates objectAtIndex: i] intValue] < 2 ? 1 : 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentDate = [record objectForKey: @"startDate"];
|
||||
if ([currentDate earlierDate: start] == currentDate)
|
||||
startInterval = 0;
|
||||
else
|
||||
startInterval = ([currentDate timeIntervalSinceDate: start]
|
||||
/ quarterLength);
|
||||
|
||||
currentDate = [record objectForKey: @"endDate"];
|
||||
if ([currentDate earlierDate: end] == end)
|
||||
endInterval = itemCount - 1;
|
||||
else
|
||||
endInterval = ([currentDate timeIntervalSinceDate: start]
|
||||
/ quarterLength);
|
||||
|
||||
if (type == 1)
|
||||
for (count = startInterval; count < endInterval; count++)
|
||||
*(fb + count) = 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (unsigned int **) _loadFreeBusyForUsers: (NSArray *) uids
|
||||
fromDate: (NSCalendarDate *) start
|
||||
{
|
||||
unsigned int **fbData, **node;
|
||||
int count, max;
|
||||
|
||||
max = [uids count];
|
||||
fbData = calloc (max, sizeof (unsigned int *));
|
||||
for (count = 0; count < max; count++)
|
||||
{
|
||||
node = fbData + count;
|
||||
*node = calloc (offsetBlocks + maxBlocks, sizeof (unsigned int *));
|
||||
[self _fillFreeBusy: *node forUid: [uids objectAtIndex: count]
|
||||
fromDate: start];
|
||||
}
|
||||
|
||||
return fbData;
|
||||
}
|
||||
|
||||
- (BOOL) _possibleBlock: (int) block
|
||||
forUsers: (int) users
|
||||
freeBusy: (unsigned int **) fbData
|
||||
interval: (int) blocks
|
||||
{
|
||||
unsigned int *node;
|
||||
int bCount, uCount;
|
||||
BOOL rc = YES;
|
||||
|
||||
for (uCount = 0; uCount < users; uCount++)
|
||||
{
|
||||
node = *(fbData + uCount);
|
||||
for (bCount = block; bCount < block + blocks; bCount++)
|
||||
{
|
||||
if (*(node+bCount) != 0)
|
||||
{
|
||||
rc = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (NSCalendarDate *) _parseDateField: (NSString *) dateF
|
||||
timeField: (NSString *) timeF
|
||||
{
|
||||
NSString *buffer;
|
||||
NSCalendarDate *rc;
|
||||
|
||||
buffer = [NSString stringWithFormat: @"%@ %@",
|
||||
[[context request] formValueForKey: dateF],
|
||||
[[context request] formValueForKey: timeF]];
|
||||
rc = [NSCalendarDate dateWithString: buffer
|
||||
calendarFormat: @"%Y%m%d %H:%M"];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *) _makeValidResponseFrom: (NSCalendarDate *) nStart
|
||||
to: (NSCalendarDate *) nEnd
|
||||
{
|
||||
NSMutableDictionary *rc;
|
||||
|
||||
rc = [NSMutableDictionary dictionaryWithCapacity: 512];
|
||||
[rc setObject: [nStart descriptionWithCalendarFormat: @"%Y%m%d"]
|
||||
forKey: @"startDate"];
|
||||
[rc setObject: [nStart descriptionWithCalendarFormat: @"%H"]
|
||||
forKey: @"startHour"];
|
||||
[rc setObject: [nStart descriptionWithCalendarFormat: @"%M"]
|
||||
forKey: @"startMinute"];
|
||||
|
||||
[rc setObject: [nEnd descriptionWithCalendarFormat: @"%Y%m%d"]
|
||||
forKey: @"endDate"];
|
||||
[rc setObject: [nEnd descriptionWithCalendarFormat: @"%H"]
|
||||
forKey: @"endHour"];
|
||||
[rc setObject: [nEnd descriptionWithCalendarFormat: @"%M"]
|
||||
forKey: @"endMinute"];
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (BOOL) _validateStart: (NSCalendarDate *) start
|
||||
andEnd: (NSCalendarDate *) end
|
||||
against: (NSArray *) limits
|
||||
{
|
||||
NSCalendarDate *sFrom, *sTo, *eFrom, *eTo;
|
||||
BOOL rc = YES;
|
||||
|
||||
sFrom = [NSCalendarDate dateWithYear: [start yearOfCommonEra]
|
||||
month: [start monthOfYear]
|
||||
day: [start dayOfMonth]
|
||||
hour: [[limits objectAtIndex: 0] hourOfDay]
|
||||
minute: [[limits objectAtIndex: 0] minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [start timeZone]];
|
||||
sTo = [NSCalendarDate dateWithYear: [start yearOfCommonEra]
|
||||
month: [start monthOfYear]
|
||||
day: [start dayOfMonth]
|
||||
hour: [[limits objectAtIndex: 1] hourOfDay]
|
||||
minute: [[limits objectAtIndex: 1] minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [start timeZone]];
|
||||
|
||||
eFrom = [NSCalendarDate dateWithYear: [end yearOfCommonEra]
|
||||
month: [end monthOfYear]
|
||||
day: [end dayOfMonth]
|
||||
hour: [[limits objectAtIndex: 0] hourOfDay]
|
||||
minute: [[limits objectAtIndex: 0] minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [end timeZone]];
|
||||
eTo = [NSCalendarDate dateWithYear: [end yearOfCommonEra]
|
||||
month: [end monthOfYear]
|
||||
day: [end dayOfMonth]
|
||||
hour: [[limits objectAtIndex: 1] hourOfDay]
|
||||
minute: [[limits objectAtIndex: 1] minuteOfHour]
|
||||
second: 0
|
||||
timeZone: [end timeZone]];
|
||||
|
||||
// start < sFrom
|
||||
if ([sFrom compare: start] == NSOrderedDescending)
|
||||
rc = NO;
|
||||
// start > sTo
|
||||
if ([sTo compare: start] == NSOrderedAscending)
|
||||
rc = NO;
|
||||
|
||||
// end > eTo
|
||||
if ([eTo compare: end] == NSOrderedAscending)
|
||||
rc = NO;
|
||||
// end < eFrom
|
||||
if ([eFrom compare: end] == NSOrderedDescending)
|
||||
rc = NO;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
- (NSArray *) _loadScheduleLimitsForUsers: (NSArray *) users
|
||||
{
|
||||
SOGoUserDefaults *ud;
|
||||
NSCalendarDate *from, *to, *maxFrom, *maxTo;
|
||||
int count;
|
||||
|
||||
maxFrom = [NSCalendarDate dateWithString: @"00:01"
|
||||
calendarFormat: @"%H:%M"];
|
||||
maxTo = [NSCalendarDate dateWithString: @"23:59"
|
||||
calendarFormat: @"%H:%M"];
|
||||
|
||||
for (count = 0; count < [users count]; count++)
|
||||
{
|
||||
ud = [[SOGoUser userWithLogin: [users objectAtIndex: count]
|
||||
roles: nil] userDefaults];
|
||||
if (ud)
|
||||
{
|
||||
from = [NSCalendarDate dateWithString: [ud dayStartTime]
|
||||
calendarFormat: @"%H:%M"];
|
||||
to = [NSCalendarDate dateWithString: [ud dayEndTime]
|
||||
calendarFormat: @"%H:%M"];
|
||||
maxFrom = (NSCalendarDate *)[from laterDate: maxFrom];
|
||||
maxTo = (NSCalendarDate *)[to earlierDate: maxTo];
|
||||
}
|
||||
}
|
||||
|
||||
return [NSArray arrayWithObjects: maxFrom, maxTo, nil];
|
||||
}
|
||||
|
||||
- (WOResponse *) findPossibleSlotAction
|
||||
{
|
||||
WORequest *r;
|
||||
NSCalendarDate *nStart, *nEnd;
|
||||
NSArray *uids, *limits;
|
||||
NSMutableDictionary *rc;
|
||||
int direction, count, blockDuration, step;
|
||||
unsigned int **fbData;
|
||||
BOOL isAllDay = NO, onlyOfficeHours = YES;
|
||||
|
||||
r = [context request];
|
||||
rc = nil;
|
||||
|
||||
uids = [[r formValueForKey: @"uids"] componentsSeparatedByString: @","];
|
||||
if ([uids count] > 0)
|
||||
{
|
||||
isAllDay = [[r formValueForKey: @"isAllDay"] boolValue];
|
||||
onlyOfficeHours = [[r formValueForKey: @"onlyOfficeHours"] boolValue];
|
||||
direction = [[r formValueForKey: @"direction"] intValue];
|
||||
limits = [self _loadScheduleLimitsForUsers: uids];
|
||||
|
||||
if (isAllDay)
|
||||
step = direction * 96;
|
||||
else
|
||||
step = direction;
|
||||
|
||||
nStart = [[self _parseDateField: @"startDate"
|
||||
timeField: @"startTime"]
|
||||
addTimeInterval: quarterLength * step];
|
||||
nEnd = [[self _parseDateField: @"endDate"
|
||||
timeField: @"endTime"]
|
||||
addTimeInterval: quarterLength * step];
|
||||
blockDuration = [nEnd timeIntervalSinceDate: nStart] / quarterLength;
|
||||
|
||||
fbData = [self _loadFreeBusyForUsers: uids
|
||||
fromDate: [nStart addTimeInterval: -offsetSeconds]];
|
||||
|
||||
for (count = offsetBlocks;
|
||||
(count < offsetBlocks + maxBlocks) && count >= 0;
|
||||
count += step)
|
||||
{
|
||||
//NSLog (@"Trying %@ -> %@", nStart, nEnd);
|
||||
if ((onlyOfficeHours && [self _validateStart: nStart
|
||||
andEnd: nEnd
|
||||
against: limits])
|
||||
|| !onlyOfficeHours)
|
||||
{
|
||||
//NSLog (@"valid");
|
||||
if ([self _possibleBlock: count
|
||||
forUsers: [uids count]
|
||||
freeBusy: fbData
|
||||
interval: blockDuration])
|
||||
{
|
||||
//NSLog (@"possible");
|
||||
rc = [self _makeValidResponseFrom: nStart
|
||||
to: nEnd];
|
||||
break;
|
||||
}
|
||||
}
|
||||
nStart = [nStart addTimeInterval: quarterLength * step];
|
||||
nEnd = [nEnd addTimeInterval: quarterLength * step];
|
||||
}
|
||||
|
||||
for (count = 0; count < [uids count]; count++)
|
||||
free (*(fbData+count));
|
||||
free (fbData);
|
||||
}
|
||||
|
||||
return [self _responseWithData: [NSArray arrayWithObjects: rc, nil]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -482,7 +482,11 @@ validate_endbeforestart = "Mae'r dyddiad gorffen sydd wedi'i roi yn digwydd c
|
||||
"Next slot" = "Slot nesaf";
|
||||
"Previous hour" = "Awr flaenorol";
|
||||
"Next hour" = "Awr nesaf";
|
||||
"Only office hours" = "Only office hours";
|
||||
"Work days only" = "Work days only";
|
||||
"The whole day" = "The whole day";
|
||||
"During office hours" = "During office hours";
|
||||
"During specified hours" = "During specified hours";
|
||||
"and" = "and";
|
||||
|
||||
/* apt list */
|
||||
"Title" = "Teitl";
|
||||
|
||||
@@ -84,11 +84,6 @@
|
||||
actionClass = "UIxCalListingActions";
|
||||
actionName = "tasksList";
|
||||
};
|
||||
findPossibleSlot = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxCalListingActions";
|
||||
actionName = "findPossibleSlot";
|
||||
};
|
||||
dayview = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxCalDayView";
|
||||
|
||||
@@ -19,15 +19,6 @@
|
||||
var dayEndHour = <var:string value="dayEndHour"/>;
|
||||
</script>
|
||||
<div id="attendeesView">
|
||||
<div id="freeBusyViewButtons">
|
||||
<var:string label:value="Suggest time slot:"/>
|
||||
<span class="buttons"><a id="nextSlot" href="#" class="button"
|
||||
><span><var:string label:value="Next slot" /></span></a>
|
||||
<a id="previousSlot" href="#" class="button"
|
||||
><span><var:string label:value="Previous slot" /></span></a></span>
|
||||
<input class="checkbox" type="checkbox"
|
||||
checked="1" id="onlyOfficeHours" /><var:string label:value="Only office hours" />
|
||||
</div>
|
||||
<div id="freeBusyView">
|
||||
<table id="freeBusy" cellspacing="0" cellpadding="0">
|
||||
<thead>
|
||||
@@ -97,6 +88,35 @@
|
||||
><var:string label:value="No free-busy information" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
<form const:href=""
|
||||
><div id="freeBusyViewButtons">
|
||||
<var:string label:value="Suggest time slot:"/><br/>
|
||||
<label><input type="checkbox" const:id="workDaysOnly"
|
||||
const:checked="YES"
|
||||
/><var:string label:value="Work days only"/></label>
|
||||
<select name="timeSlotLimits" const:id="timeSlotLimits">
|
||||
<option value="whole-day"
|
||||
><var:string label:value="The whole day"
|
||||
/></option>
|
||||
<option value="office-hours"
|
||||
const:selected="yes"
|
||||
><var:string label:value="During office hours"
|
||||
/></option>
|
||||
<option value="range"
|
||||
><var:string label:value="During specified hours"/></option>
|
||||
</select><br/>
|
||||
<select const:id="timeSlotStartLimitHour"><!-- space --></select>
|
||||
<select const:id="timeSlotStartLimitMinute"><!-- space --></select>
|
||||
<var:string label:value="and"/>
|
||||
<select const:id="timeSlotEndLimitHour"><!-- space --></select>
|
||||
<select const:id="timeSlotEndLimitMinute"><!-- space --></select>
|
||||
<br/>
|
||||
<a id="nextSlot" href="#" class="button"
|
||||
><span><var:string label:value="Next slot" /></span></a>
|
||||
<a id="previousSlot" href="#" class="button"
|
||||
><span><var:string label:value="Previous slot" /></span></a>
|
||||
</div
|
||||
></form>
|
||||
<div id="freeBusyReplicas">
|
||||
<div><span><var:string label:value="Start:"
|
||||
/></span><var:component className="UIxTimeDateControl"
|
||||
@@ -114,7 +134,6 @@
|
||||
/></div>
|
||||
</div>
|
||||
<div id="windowButtons">
|
||||
<hr />
|
||||
<a id="okButton" href="#" class="button actionButton"
|
||||
><span><var:string label:value="OK"/></span></a>
|
||||
<a id="cancelButton" href="#" class="button"
|
||||
|
||||
@@ -12,9 +12,8 @@ DIV#attendeesView
|
||||
DIV#freeBusyView
|
||||
{ background-color: #fff;
|
||||
position: absolute;
|
||||
margin-top: 0.5em;
|
||||
top: 2em;
|
||||
bottom: 14.5em;
|
||||
top: 5px;
|
||||
bottom: 135px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
overflow: hidden;
|
||||
@@ -189,7 +188,7 @@ TABLE#freeBusyData TD SPAN.freeBusyZoneElement
|
||||
|
||||
DIV#freeBusyFooter
|
||||
{ position: absolute;
|
||||
height: 14em;
|
||||
height: 130px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
@@ -239,24 +238,18 @@ SPAN.colorBox.noFreeBusy,
|
||||
TABLE#freeBusyData TD.noFreeBusy
|
||||
{ background-color: #e09ebd; }
|
||||
|
||||
DIV#freeBusyViewButtons
|
||||
{ position: absolute;
|
||||
padding: 0px 3px 3px 3px;
|
||||
line-height: 2em;
|
||||
top: 0px;
|
||||
right: 0;
|
||||
padding-right: 16em; /* leave space for the next/previous buttons */
|
||||
text-align: right;
|
||||
left: auto; }
|
||||
#freeBusyViewButtons
|
||||
{ border: 1px solid #aaa;
|
||||
float: left;
|
||||
width: 320px;
|
||||
margin: 0px;
|
||||
padding: 5px;
|
||||
text-align: left; }
|
||||
|
||||
DIV#freeBusyViewButtons SPAN.buttons
|
||||
{ position: absolute;
|
||||
top: 0px;
|
||||
left: auto;
|
||||
right: 0px; }
|
||||
|
||||
DIV#freeBusyViewButtons INPUT
|
||||
{ clear: none; }
|
||||
#freeBusyViewButtons > DIV.buttons
|
||||
{ margin: 0px;
|
||||
margin-top: 4px;
|
||||
background-color: #000; }
|
||||
|
||||
DIV#freeBusyZoomButtons
|
||||
{ position: absolute;
|
||||
@@ -266,7 +259,7 @@ DIV#freeBusyZoomButtons
|
||||
|
||||
DIV#freeBusyReplicas
|
||||
{ position: absolute;
|
||||
top: 5em;
|
||||
top: 2px;
|
||||
right: 0px;
|
||||
width: 30em;
|
||||
height: 4em; }
|
||||
@@ -279,7 +272,7 @@ DIV#windowButtons
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
height: 4em;
|
||||
height: 24px;
|
||||
text-align: right; }
|
||||
|
||||
.officeHour
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/* TODO:
|
||||
- set work days from preferences */
|
||||
|
||||
var OwnerLogin = "";
|
||||
|
||||
var resultsDiv;
|
||||
var address;
|
||||
var additionalDays = 2;
|
||||
|
||||
var availability;
|
||||
|
||||
var isAllDay = parent$("isAllDay").checked + 0;
|
||||
var displayStartHour = 0;
|
||||
@@ -15,7 +19,7 @@ var attendeesEditor = {
|
||||
selectedIndex: -1
|
||||
};
|
||||
|
||||
function handleAllDay () {
|
||||
function handleAllDay() {
|
||||
window.timeWidgets['end']['hour'].value = 17;
|
||||
window.timeWidgets['end']['minute'].value = 0;
|
||||
window.timeWidgets['start']['hour'].value = 9;
|
||||
@@ -221,7 +225,7 @@ function performSearchCallback(http) {
|
||||
var node = createElement('li');
|
||||
list.appendChild(node);
|
||||
node.address = completeEmail;
|
||||
log("node.address: " + node.address);
|
||||
// log("node.address: " + node.address);
|
||||
node.uid = contact["c_uid"];
|
||||
node.isList = isList;
|
||||
if (isList) {
|
||||
@@ -339,17 +343,9 @@ function onAttendeeResultClick(event) {
|
||||
this.parentNode.input = null;
|
||||
}
|
||||
|
||||
function resetFreeBusyZone() {
|
||||
var table = $("freeBusyHeader");
|
||||
var row = table.rows[2];
|
||||
for (var i = 0; i < row.cells.length; i++) {
|
||||
var nodes = $(row.cells[i]).childNodesWithTag("span");
|
||||
for (var j = 0; j < nodes.length; j++)
|
||||
nodes[j].removeClassName("busy");
|
||||
}
|
||||
}
|
||||
function redisplayEventSpans() {
|
||||
// log("redisplayEventSpans");
|
||||
|
||||
function redisplayFreeBusyZone() {
|
||||
var table = $("freeBusyHeader");
|
||||
var row = table.rows[2];
|
||||
var stDay = $("startTime_date").valueAsDate();
|
||||
@@ -385,9 +381,19 @@ function redisplayFreeBusyZone() {
|
||||
var currentCell = row.cells[currentCellNbr];
|
||||
var currentSpanNbr = stMinute;
|
||||
var spans = $(currentCell).childNodesWithTag("span");
|
||||
resetFreeBusyZone();
|
||||
|
||||
/* we first reset the cache of busy spans */
|
||||
if (row.busySpans) {
|
||||
for (var i = 0; i < row.busySpans.length; i++) {
|
||||
row.busySpans[i].removeClassName("busy");
|
||||
}
|
||||
}
|
||||
row.busySpans = [];
|
||||
|
||||
/* now we mark the spans corresponding to our event */
|
||||
while (deltaSpans > 0) {
|
||||
var currentSpan = spans[currentSpanNbr];
|
||||
row.busySpans.push(currentSpan);
|
||||
currentSpan.addClassName("busy");
|
||||
currentSpanNbr++;
|
||||
if (currentSpanNbr > 3) {
|
||||
@@ -528,31 +534,501 @@ function onInputBlur(event) {
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME: any other way to repeat an object? */
|
||||
var _fullFreeDay = [];
|
||||
for (var i = 0; i < 96; i++) {
|
||||
_fullFreeDay.push('0');
|
||||
}
|
||||
|
||||
function availabilitySession(uids, direction, start, end, listener) {
|
||||
this.mDirection = direction;
|
||||
if (direction > 0) {
|
||||
this._findDate = this._forwardFindDate;
|
||||
this._adjustCurrentStart = this._forwardAdjustCurrentStart;
|
||||
}
|
||||
else {
|
||||
this._findDate = this._backwardFindDate;
|
||||
this._adjustCurrentStart = this._backwardAdjustCurrentStart;
|
||||
}
|
||||
this.mStart = start;
|
||||
|
||||
this.mStartLimit = 0;
|
||||
this.mEndLimit = 24 * 4;
|
||||
this.mWorkDaysOnly = false;
|
||||
|
||||
/* The duration of the range covering the start and end of the event, in
|
||||
quarters.
|
||||
|
||||
15 minutes * 60 secs * 1000 ms = 900000 ms */
|
||||
this.mDuration = Math.ceil((end.getTime() - start.getTime()) / 900000);
|
||||
this.mUids = uids;
|
||||
this.mListener = listener;
|
||||
}
|
||||
|
||||
availabilitySession.prototype = {
|
||||
mStart: null,
|
||||
mDirection: null,
|
||||
mStartLimit: 0,
|
||||
mEndLimit: 0,
|
||||
mWorkDaysOnly: 0,
|
||||
|
||||
mListener: null,
|
||||
mUids: null,
|
||||
|
||||
mCurrentStart: null,
|
||||
mFirstStep: false,
|
||||
|
||||
mCurrentEntries: null,
|
||||
mActiveRequests: 0,
|
||||
|
||||
setLimits: function aS_setLimits(start, end) {
|
||||
this.mStartLimit = start;
|
||||
this.mEndLimit = end;
|
||||
},
|
||||
|
||||
setWorkDaysOnly: function aS_setWorkDaysOnly(workDaysOnly) {
|
||||
this.mWorkDaysOnly = workDaysOnly;
|
||||
},
|
||||
|
||||
_step: function aS__step() {
|
||||
// log("currentStart: " + this.mCurrentStart);
|
||||
this.mCurrentEntries = null;
|
||||
var max = this.mUids.length;
|
||||
if (max > 0) {
|
||||
this.mActiveRequests = max;
|
||||
for (var i = 0; i < max; i++) {
|
||||
// log("request start");
|
||||
var fbRequest = new freeBusyRequest(this.mCurrentStart,
|
||||
this.mCurrentStart,
|
||||
this.mUids[i],
|
||||
this);
|
||||
fbRequest.start();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.mActiveRequests = 1;
|
||||
this.onRequestComplete(null, true, _fullFreeDay);
|
||||
}
|
||||
},
|
||||
|
||||
start: function aS_start() {
|
||||
this.mCurrentStart = this.mStart.clone();
|
||||
this.mCurrentStart.setHours(0);
|
||||
this.mCurrentStart.setMinutes(0);
|
||||
if (this.mWorkDaysOnly) {
|
||||
this._adjustCurrentStart();
|
||||
}
|
||||
this.mFirstStep = true;
|
||||
this._step();
|
||||
},
|
||||
|
||||
onRequestComplete: function aS_onRequestComplete(request, success, entries) {
|
||||
this.mActiveRequests--;
|
||||
this._mergeEntries(entries);
|
||||
if (this.mActiveRequests == 0) {
|
||||
// log("requests done");
|
||||
var foundDate = this._findDate();
|
||||
if (foundDate) {
|
||||
var foundEndDate = foundDate.clone();
|
||||
foundEndDate.setTime(foundDate.getTime()
|
||||
+ this.mDuration * 900000);
|
||||
this.mListener.onRequestComplete(this, foundDate, foundEndDate);
|
||||
}
|
||||
else {
|
||||
if (this.mDirection > 0) {
|
||||
this.mCurrentStart.addDays(1);
|
||||
}
|
||||
else {
|
||||
this.mCurrentStart.addDays(-1);
|
||||
}
|
||||
if (this.mWorkDaysOnly) {
|
||||
this._adjustCurrentStart();
|
||||
}
|
||||
// log("found no date, new start: " + this.mCurrentStart);
|
||||
this._step();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_forwardAdjustCurrentStart: function aS__forwardAdjustCurrentStart() {
|
||||
var day = this.mCurrentStart.getDay();
|
||||
if (day == 0) {
|
||||
this.mCurrentStart.addDays(1);
|
||||
}
|
||||
else if (day == 6) {
|
||||
this.mCurrentStart.addDays(2);
|
||||
}
|
||||
},
|
||||
_backwardAdjustCurrentStart: function aS__backwardAdjustCurrentStart() {
|
||||
var day = this.mCurrentStart.getDay();
|
||||
if (day == 0) {
|
||||
this.mCurrentStart.addDays(-2);
|
||||
}
|
||||
else if (day == 6) {
|
||||
this.mCurrentStart.addDays(-1);
|
||||
}
|
||||
},
|
||||
|
||||
_mergeEntries: function aS__mergeEntries(entries) {
|
||||
if (this.mCurrentEntries) {
|
||||
var currentIndex = 0;
|
||||
while (currentIndex > -1) {
|
||||
this.mCurrentEntries[currentIndex] = entries[currentIndex];
|
||||
currentIndex = entries.indexOf('1', currentIndex + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.mCurrentEntries = entries;
|
||||
}
|
||||
},
|
||||
|
||||
_forwardFindDate: function aS__forwardFindDate() {
|
||||
var foundDate = null;
|
||||
|
||||
var maxOffset = this.mEndLimit - this.mDuration;
|
||||
var offset = 0;
|
||||
if (this.mFirstStep) {
|
||||
offset = Math.floor(this.mStart.getHours() * 4
|
||||
+ this.mStart.getMinutes() / 15) + 1;
|
||||
this.mFirstStep = false;
|
||||
}
|
||||
else {
|
||||
offset = this.mCurrentEntries.indexOf('0');
|
||||
}
|
||||
if (offset > -1 && offset < this.mStartLimit) {
|
||||
offset = this.mStartLimit;
|
||||
}
|
||||
while (!foundDate && offset > -1 && offset <= maxOffset) {
|
||||
var testDuration = 0;
|
||||
while (this.mCurrentEntries[offset] == '0'
|
||||
&& testDuration < this.mDuration) {
|
||||
testDuration++;
|
||||
offset++;
|
||||
}
|
||||
if (testDuration == this.mDuration) {
|
||||
foundDate = new Date();
|
||||
var foundTime = (this.mCurrentStart.getTime()
|
||||
+ (offset - testDuration) * 900000);
|
||||
foundDate.setTime(foundTime);
|
||||
}
|
||||
else {
|
||||
offset = this.mCurrentEntries.indexOf('0', offset + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return foundDate;
|
||||
},
|
||||
_backwardFindDate: function aS__backwardFindDate() {
|
||||
var foundDate = null;
|
||||
|
||||
var maxOffset = this.mEndLimit - this.mDuration;
|
||||
var offset;
|
||||
if (this.mFirstStep) {
|
||||
offset = Math.floor(this.mStart.getHours() * 4
|
||||
+ this.mStart.getMinutes() / 15) - 1;
|
||||
this.mFirstStep = false;
|
||||
}
|
||||
else {
|
||||
offset = this.mCurrentEntries.lastIndexOf('0');
|
||||
}
|
||||
if (offset > maxOffset) {
|
||||
offset = maxOffset;
|
||||
}
|
||||
while (!foundDate
|
||||
&& offset >= this.mStartLimit) {
|
||||
var testDuration = 0;
|
||||
var testOffset = offset;
|
||||
while (this.mCurrentEntries[testOffset] == '0'
|
||||
&& testDuration < this.mDuration) {
|
||||
testDuration++;
|
||||
testOffset++;
|
||||
}
|
||||
if (testDuration == this.mDuration) {
|
||||
foundDate = new Date();
|
||||
var foundTime = (this.mCurrentStart.getTime()
|
||||
+ offset * 900000);
|
||||
foundDate.setTime(foundTime);
|
||||
}
|
||||
else {
|
||||
offset = this.mCurrentEntries.lastIndexOf('0', offset - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return foundDate;
|
||||
}
|
||||
};
|
||||
|
||||
function availabilityController(previousSlotButton, nextSlotButton) {
|
||||
this.previousSlotButton = previousSlotButton;
|
||||
this.nextSlotButton = nextSlotButton;
|
||||
|
||||
var boundCallback = this.onPreviousSlotClick.bindAsEventListener(this);
|
||||
previousSlotButton.observe("click", boundCallback, false);
|
||||
boundCallback = this.onNextSlotClick.bindAsEventListener(this);
|
||||
$("nextSlot").observe("click", boundCallback, false);
|
||||
}
|
||||
|
||||
availabilityController.prototype = {
|
||||
previousSlotButton: null,
|
||||
nextSlotButton: null,
|
||||
|
||||
onPreviousSlotClick: function ac_onPreviousSlotClick(event) {
|
||||
this._findSlot(-1);
|
||||
this.previousSlotButton.blur();
|
||||
},
|
||||
onNextSlotClick: function aC_onNextSlotClick(event) {
|
||||
this._findSlot(1);
|
||||
this.nextSlotButton.blur();
|
||||
},
|
||||
_findSlot: function aC__findSlot(direction) {
|
||||
var uids = [];
|
||||
|
||||
var inputs = $("freeBusy").getElementsByTagName("input");
|
||||
for (var i = 0; i < inputs.length - 1; i++) {
|
||||
if (inputs[i].uid) {
|
||||
uids.push(inputs[i].uid);
|
||||
}
|
||||
}
|
||||
|
||||
var start = window.timeWidgets['start']['date'].valueAsDate();
|
||||
start.setHours(window.timeWidgets['start']['hour'].value);
|
||||
start.setMinutes(window.timeWidgets['start']['minute'].value);
|
||||
|
||||
var end = window.timeWidgets['end']['date'].valueAsDate();
|
||||
end.setHours(window.timeWidgets['end']['hour'].value);
|
||||
end.setMinutes(window.timeWidgets['end']['minute'].value);
|
||||
var session = new availabilitySession(uids, direction,
|
||||
start, end,
|
||||
this);
|
||||
var limits = $("timeSlotLimits");
|
||||
if (limits.value == "office-hours") {
|
||||
var start = dayStartHour * 4;
|
||||
var end = dayEndHour * 4;
|
||||
session.setLimits(start, end);
|
||||
}
|
||||
else if (limits.value == "range") {
|
||||
var start = (parseInt($("timeSlotStartLimitHour").value)
|
||||
+ parseInt($("timeSlotStartLimitMinute").value));
|
||||
var end = (parseInt($("timeSlotEndLimitHour").value)
|
||||
+ parseInt($("timeSlotEndLimitMinute").value));
|
||||
session.setLimits(start, end);
|
||||
}
|
||||
session.setWorkDaysOnly($("workDaysOnly").checked);
|
||||
session.start();
|
||||
},
|
||||
onRequestComplete: function aC_onRequestComplete(session, start, end) {
|
||||
window.timeWidgets['start']['date'].setValueAsDate(start);
|
||||
window.timeWidgets['start']['hour'].value = start.getHours();
|
||||
window.timeWidgets['start']['minute'].value = start.getMinutes();
|
||||
|
||||
window.timeWidgets['end']['date'].setValueAsDate(end);
|
||||
window.timeWidgets['end']['hour'].value = end.getHours();
|
||||
window.timeWidgets['end']['minute'].value = end.getMinutes();
|
||||
|
||||
if (start.getDay() != session.mStart.getDay()) {
|
||||
onTimeDateWidgetChange();
|
||||
}
|
||||
else {
|
||||
redisplayEventSpans();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* freebusy cache, used internally by freeBusyRequest below */
|
||||
var _fbCache = {};
|
||||
|
||||
function _freeBusyCacheEntry() {
|
||||
}
|
||||
|
||||
_freeBusyCacheEntry.prototype = {
|
||||
startDate: null,
|
||||
entries: null,
|
||||
|
||||
getEntries: function fBCE_getEntries(sd, ed) {
|
||||
var entries = null;
|
||||
|
||||
var adjustedSd = sd.beginOfDay();
|
||||
|
||||
if (this.startDate && this.startDate.getTime() <= adjustedSd.getTime()) {
|
||||
var offset = this.startDate.deltaDays(adjustedSd) * 96;
|
||||
if (this.entries.length > offset) {
|
||||
var adjustedEd = ed.beginOfDay();
|
||||
var nbrDays = adjustedSd.deltaDays(adjustedEd) + 1;
|
||||
var nbrQu = nbrDays * 96;
|
||||
var offsetEnd = offset + nbrQu;
|
||||
if (this.entries.length >= offsetEnd) {
|
||||
entries = this.entries.slice(offset, offsetEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
},
|
||||
|
||||
getFetchRanges: function fBCE_getFetchRanges(sd, ed) {
|
||||
var fetchDates;
|
||||
|
||||
var adjustedSd = sd.beginOfDay();
|
||||
var adjustedEd = ed.beginOfDay();
|
||||
var nbrDays = adjustedSd.deltaDays(adjustedEd) + 1;
|
||||
if (this.startDate) {
|
||||
fetchDates = [];
|
||||
|
||||
if (adjustedSd.getTime() < this.startDate.getTime()) {
|
||||
var start = adjustedSd.clone();
|
||||
start.addDays(-7);
|
||||
var end = start.clone();
|
||||
end.addDays(-1);
|
||||
fetchDates.push({ start: start, end: end });
|
||||
}
|
||||
|
||||
var currentNbrDays = this.entries.length / 96;
|
||||
var nextDate = this.startDate.clone();
|
||||
nextDate.addDays(currentNbrDays);
|
||||
if (adjustedEd.getTime() >= nextDate.getTime()) {
|
||||
var end = nextDate.clone();
|
||||
end.addDays(7);
|
||||
fetchDates.push({ start: nextDate, end: end });
|
||||
}
|
||||
}
|
||||
else {
|
||||
var start = adjustedSd.clone();
|
||||
start.addDays(-7);
|
||||
var end = adjustedEd.clone();
|
||||
end.addDays(7);
|
||||
fetchDates = [ { start: start, end: end } ];
|
||||
}
|
||||
|
||||
return fetchDates;
|
||||
},
|
||||
|
||||
integrateEntries: function fBCE_integrateEntries(entries, start, end) {
|
||||
if (this.startDate) {
|
||||
if (start.getTime() < this.startDate) {
|
||||
var days = start.deltaDays(this.startDate);
|
||||
if (entries.length == (days * 96)) {
|
||||
this.startDate = start;
|
||||
this.entries = entries.concat(this.entries);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.entries = this.entries.concat(entries);
|
||||
}
|
||||
} else {
|
||||
this.startDate = start;
|
||||
this.entries = entries;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function freeBusyRequest(start, end, uid, listener) {
|
||||
this.mStart = start.beginOfDay();
|
||||
this.mEnd = end.beginOfDay();
|
||||
this.mUid = uid;
|
||||
this.mListener = listener;
|
||||
this.mPendingRequests = 0;
|
||||
}
|
||||
|
||||
freeBusyRequest.prototype = {
|
||||
mStart: null,
|
||||
mEnd: null,
|
||||
mUid: null,
|
||||
mListener: null,
|
||||
|
||||
mCacheEntry: null,
|
||||
|
||||
mPendingRequests: 0,
|
||||
|
||||
start: function fBR_start() {
|
||||
this.mCacheEntry = _fbCache[this.mUid];
|
||||
if (!this.mCacheEntry) {
|
||||
this.mCacheEntry = new _freeBusyCacheEntry();
|
||||
_fbCache[this.mUid] = this.mCacheEntry;
|
||||
}
|
||||
var entries = this.mCacheEntry.getEntries(this.mStart, this.mEnd);
|
||||
if (entries) {
|
||||
this.mListener.onRequestComplete(this, true, entries);
|
||||
}
|
||||
else {
|
||||
if (this.mPendingRequests == 0) {
|
||||
var fetchRanges = this.mCacheEntry.getFetchRanges(this.mStart, this.mEnd);
|
||||
this.mPendingRequests = fetchRanges.length;
|
||||
for (var i = 0; i < fetchRanges.length; i++) {
|
||||
var fetchRange = fetchRanges[i];
|
||||
this._performAjaxRequest(fetchRange.start, fetchRange.end);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* a nearly impossible condition that we want to handle */
|
||||
log("freebusy request is already active");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_performAjaxRequest: function fBR__performAjaxRequest(rqStart, rqEnd) {
|
||||
var urlstr = (UserFolderURL + "../" + this.mUid
|
||||
+ "/freebusy.ifb/ajaxRead?"
|
||||
+ "sday=" + rqStart.getDayString()
|
||||
+ "&eday=" + rqEnd.getDayString());
|
||||
|
||||
var thisRequest = this;
|
||||
var callback = function fBR__performAjaxRequest_cb(http) {
|
||||
if (http.readyState == 4) {
|
||||
thisRequest.onRequestComplete(http, rqStart, rqEnd);
|
||||
thisRequest = null;
|
||||
}
|
||||
};
|
||||
|
||||
triggerAjaxRequest(urlstr, callback);
|
||||
},
|
||||
|
||||
onRequestComplete: function fBR_onRequestComplete(http, rqStart, rqEnd) {
|
||||
this.mPendingRequests--;
|
||||
if (http.status == 200 && http.responseText) {
|
||||
var newEntries = http.responseText.split(",");
|
||||
var cacheEntry = this.mCacheEntry;
|
||||
cacheEntry.integrateEntries(newEntries, rqStart, rqEnd);
|
||||
if (this.mPendingRequests == 0) {
|
||||
var entries = this.mCacheEntry.getEntries(this.mStart,
|
||||
this.mEnd);
|
||||
this.mListener.onRequestComplete(this, true, entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function displayFreeBusyForNode(input) {
|
||||
var rowIndex = input.parentNode.parentNode.sectionRowIndex;
|
||||
var nodes = $("freeBusyData").tBodies[0].rows[rowIndex].cells;
|
||||
log ("displayFreeBusyForNode index " + rowIndex + " (" + nodes.length + " cells)");
|
||||
var row = $("freeBusyData").tBodies[0].rows[rowIndex];
|
||||
var nodes = row.cells;
|
||||
// log ("displayFreeBusyForNode index " + rowIndex + " (" + nodes.length + " cells)");
|
||||
if (input.uid) {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = $(nodes[i]);
|
||||
node.removeClassName("noFreeBusy");
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
for (var j = 0; j < 4; j++) {
|
||||
createElement("span", null, "freeBusyZoneElement",
|
||||
null, null, node);
|
||||
if (!input.hasfreebusy) {
|
||||
// log("forcing draw of nodes");
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = $(nodes[i]);
|
||||
node.removeClassName("noFreeBusy");
|
||||
while (node.firstChild) {
|
||||
node.removeChild(node.firstChild);
|
||||
}
|
||||
for (var j = 0; j < 4; j++) {
|
||||
createElement("span", null, "freeBusyZoneElement",
|
||||
null, null, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
var sd = $('startTime_date').valueAsShortDateString();
|
||||
var ed = $('endTime_date').valueAsShortDateString();
|
||||
var urlstr = (UserFolderURL + "../" + input.uid
|
||||
+ "/freebusy.ifb/ajaxRead?"
|
||||
+ "sday=" + sd + "&eday=" + ed + "&additional=" +
|
||||
additionalDays);
|
||||
triggerAjaxRequest(urlstr,
|
||||
updateFreeBusyDataCallback,
|
||||
input);
|
||||
|
||||
var sd = $('startTime_date').valueAsDate();
|
||||
var ed = $('endTime_date').valueAsDate();
|
||||
var listener = {
|
||||
onRequestComplete: function(request, success, entries) {
|
||||
if (success) {
|
||||
drawFbData(input, entries);
|
||||
}
|
||||
}
|
||||
};
|
||||
var rq = new freeBusyRequest(sd, ed, input.uid, listener);
|
||||
rq.start();
|
||||
} else {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = $(nodes[i]);
|
||||
@@ -564,45 +1040,54 @@ function displayFreeBusyForNode(input) {
|
||||
}
|
||||
}
|
||||
|
||||
function setSlot(tds, nbr, status) {
|
||||
var tdnbr = Math.floor(nbr / 4);
|
||||
var spannbr = nbr - (tdnbr * 4);
|
||||
var days = 0;
|
||||
if (tdnbr > 24) {
|
||||
days = Math.floor(tdnbr / 24);
|
||||
tdnbr -= (days * 24);
|
||||
function setSpanStatus(span, status) {
|
||||
var currentClass = span.freeBusyClass;
|
||||
if (!currentClass)
|
||||
currentClass = "";
|
||||
var newClass;
|
||||
if (status == '1') {
|
||||
newClass = "busy";
|
||||
}
|
||||
if (tdnbr > (displayStartHour - 1) && tdnbr < (displayEndHour + 1)) {
|
||||
var i = (days * (displayEndHour - displayStartHour + 1) + tdnbr - (displayStartHour - 1));
|
||||
var td = tds[i - 1];
|
||||
var spans = $(td).childNodesWithTag("span");
|
||||
if (status == '2')
|
||||
$(spans[spannbr]).addClassName("maybe-busy");
|
||||
else
|
||||
$(spans[spannbr]).addClassName("busy");
|
||||
else if (status == '2') {
|
||||
newClass = "maybe-busy";
|
||||
}
|
||||
else {
|
||||
newClass = "";
|
||||
}
|
||||
if (newClass != currentClass) {
|
||||
if (currentClass.length > 0) {
|
||||
span.removeClassName(currentClass);
|
||||
}
|
||||
if (newClass.length > 0) {
|
||||
span.addClassName(newClass);
|
||||
}
|
||||
span.freeBusyClass = newClass;
|
||||
}
|
||||
}
|
||||
|
||||
function updateFreeBusyDataCallback(http) {
|
||||
if (http.readyState == 4) {
|
||||
if (http.status == 200) {
|
||||
var input = http.callbackData;
|
||||
var slots = http.responseText.split(",");
|
||||
var rowIndex = input.parentNode.parentNode.sectionRowIndex;
|
||||
var nodes = $("freeBusyData").tBodies[0].rows[rowIndex].cells;
|
||||
// log ("received " + slots.length + " slots for " + rowIndex + " with " + nodes.length + " cells");
|
||||
for (var i = 0; i < slots.length; i++) {
|
||||
if (slots[i] != '0')
|
||||
setSlot(nodes, i, slots[i]);
|
||||
function drawFbData(input, slots) {
|
||||
var rowIndex = input.parentNode.parentNode.sectionRowIndex;
|
||||
|
||||
var slotNbr = 0;
|
||||
var tds = $("freeBusyData").tBodies[0].rows[rowIndex].cells;
|
||||
if (tds.length * 4 == slots.length) {
|
||||
for (var i = 0; i < tds.length; i++) {
|
||||
var spans = tds[i].childNodesWithTag("span");
|
||||
for (var j = 0; j < spans.length; j++) {
|
||||
setSpanStatus(spans[j], slots[slotNbr]);
|
||||
slotNbr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log("inconsistency between freebusy results and"
|
||||
+ " the number of cells");
|
||||
log(" expecting: " + tds.length + " received: " + slots.length);
|
||||
}
|
||||
}
|
||||
|
||||
function resetAllFreeBusys() {
|
||||
var table = $("freeBusy");
|
||||
var inputs = table.getElementsByTagName("input");
|
||||
|
||||
var inputs = $("freeBusy").getElementsByTagName("input");
|
||||
for (var i = 0; i < inputs.length - 1; i++) {
|
||||
var currentInput = inputs[i];
|
||||
currentInput.hasfreebusy = false;
|
||||
@@ -610,50 +1095,54 @@ function resetAllFreeBusys() {
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTimeSlotWidgets() {
|
||||
availability = new availabilityController($("previousSlot"),
|
||||
$("nextSlot"));
|
||||
|
||||
var hourWidgets = [ "timeSlotStartLimitHour",
|
||||
"timeSlotEndLimitHour" ];
|
||||
for (var i = 0; i < hourWidgets.length; i++) {
|
||||
var hourWidget = $(hourWidgets[i]);
|
||||
for (var h = 0; h < 24; h++) {
|
||||
var option = createElement("option", null, null,
|
||||
{ value: h * 4 });
|
||||
var text = (h < 10) ? ("0" + h) : ("" + h);
|
||||
option.appendChild(document.createTextNode(text));
|
||||
hourWidget.appendChild(option);
|
||||
}
|
||||
}
|
||||
var limitWidget = $("timeSlotStartLimitHour");
|
||||
limitWidget.value = parseInt($("startTime_time_hour").value) * 4;
|
||||
limitWidget = $("timeSlotEndLimitHour");
|
||||
limitWidget.value = parseInt($("endTime_time_hour").value) * 4;
|
||||
|
||||
var minuteWidgets = [ "timeSlotStartLimitMinute",
|
||||
"timeSlotEndLimitMinute" ];
|
||||
for (var i = 0; i < minuteWidgets.length; i++) {
|
||||
var minuteWidget = $(minuteWidgets[i]);
|
||||
for (var h = 0; h < 4; h++) {
|
||||
var option = createElement("option", null, null,
|
||||
{ value: h });
|
||||
var quValue = h * 15;
|
||||
var text = (h == 0) ? "00" : ("" + quValue);
|
||||
option.appendChild(document.createTextNode(text));
|
||||
minuteWidget.appendChild(option);
|
||||
}
|
||||
}
|
||||
var limitWidget = $("timeSlotStartLimitMinute");
|
||||
limitWidget.value = Math.floor(parseInt($("startTime_time_minute").value)
|
||||
/ 15);
|
||||
limitWidget = $("timeSlotEndLimitMinute");
|
||||
limitWidget.value = Math.floor(parseInt($("endTime_time_minute").value)
|
||||
/ 15);
|
||||
}
|
||||
|
||||
function initializeWindowButtons() {
|
||||
var okButton = $("okButton");
|
||||
var cancelButton = $("cancelButton");
|
||||
|
||||
okButton.observe("click", onEditorOkClick, false);
|
||||
cancelButton.observe("click", onEditorCancelClick, false);
|
||||
|
||||
$("previousSlot").observe ("click", onPreviousSlotClick, false);
|
||||
$("nextSlot").observe ("click", onNextSlotClick, false);
|
||||
}
|
||||
|
||||
function findSlot(direction) {
|
||||
var userList = UserLogin;
|
||||
var table = $("freeBusy");
|
||||
var inputs = table.getElementsByTagName("input");
|
||||
var sd = window.timeWidgets['start']['date'].valueAsShortDateString();
|
||||
var st = window.timeWidgets['start']['hour'].value
|
||||
+ ":" + window.timeWidgets['start']['minute'].value;
|
||||
var ed = window.timeWidgets['end']['date'].valueAsShortDateString();
|
||||
var et = window.timeWidgets['end']['hour'].value
|
||||
+ ":" + window.timeWidgets['end']['minute'].value;
|
||||
|
||||
for (var i = 0; i < inputs.length - 1; i++) {
|
||||
if (inputs[i].uid)
|
||||
userList += "," + inputs[i].uid;
|
||||
}
|
||||
|
||||
// Abort any pending request
|
||||
if (document.findSlotAjaxRequest) {
|
||||
document.findSlotAjaxRequest.aborted = true;
|
||||
document.findSlotAjaxRequest.abort();
|
||||
}
|
||||
var urlstr = (ApplicationBaseURL
|
||||
+ "findPossibleSlot?direction=" + direction
|
||||
+ "&uids=" + escape(userList)
|
||||
+ "&startDate=" + escape(sd)
|
||||
+ "&startTime=" + escape(st)
|
||||
+ "&endDate=" + escape(ed)
|
||||
+ "&endTime=" + escape(et)
|
||||
+ "&isAllDay=" + isAllDay
|
||||
+ "&onlyOfficeHours=" + ($("onlyOfficeHours").checked + 0));
|
||||
document.findSlotAjaxRequest = triggerAjaxRequest(urlstr,
|
||||
updateSlotDisplayCallback,
|
||||
userList);
|
||||
}
|
||||
|
||||
function cleanInt(data) {
|
||||
@@ -696,7 +1185,7 @@ function updateSlotDisplayCallback(http) {
|
||||
var data = http.responseText.evalJSON(true);
|
||||
var start = new Date();
|
||||
var end = new Date();
|
||||
var cb = redisplayFreeBusyZone;
|
||||
var cb = redisplayEventSpans;
|
||||
|
||||
start.setFullYear(parseInt (data[0]['startDate'].substr(0, 4)),
|
||||
parseInt (data[0]['startDate'].substr(4, 2)) - 1,
|
||||
@@ -721,16 +1210,6 @@ function updateSlotDisplayCallback(http) {
|
||||
cb();
|
||||
}
|
||||
|
||||
function onPreviousSlotClick(event) {
|
||||
findSlot(-1);
|
||||
this.blur(); // required by IE
|
||||
}
|
||||
|
||||
function onNextSlotClick(event) {
|
||||
findSlot(1);
|
||||
this.blur(); // required by IE
|
||||
}
|
||||
|
||||
function onEditorOkClick(event) {
|
||||
preventDefault(event);
|
||||
|
||||
@@ -811,7 +1290,7 @@ function updateParentDateFields(srcWidgetName, dstWidgetName) {
|
||||
}
|
||||
|
||||
function onTimeWidgetChange() {
|
||||
redisplayFreeBusyZone();
|
||||
redisplayEventSpans();
|
||||
}
|
||||
|
||||
function onTimeDateWidgetChange() {
|
||||
@@ -833,7 +1312,7 @@ function onTimeDateWidgetChange() {
|
||||
|
||||
prepareTableHeaders();
|
||||
prepareTableRows();
|
||||
redisplayFreeBusyZone();
|
||||
redisplayEventSpans();
|
||||
resetAllFreeBusys();
|
||||
}
|
||||
|
||||
@@ -843,7 +1322,7 @@ function prepareTableHeaders() {
|
||||
|
||||
var endTimeDate = $("endTime_date");
|
||||
var endDate = endTimeDate.valueAsDate();
|
||||
endDate.setTime(endDate.getTime() + (additionalDays * 86400000));
|
||||
endDate.setTime(endDate.getTime());
|
||||
|
||||
var rows = $("freeBusyHeader").rows;
|
||||
var days = startDate.daysUpTo(endDate);
|
||||
@@ -861,7 +1340,7 @@ function prepareTableHeaders() {
|
||||
var text = hour + ":00";
|
||||
if (hour < 10)
|
||||
text = "0" + text;
|
||||
if (hour >= dayStartHour && hour <= dayEndHour)
|
||||
if (hour >= dayStartHour && hour < dayEndHour)
|
||||
$(header2).addClassName ("officeHour");
|
||||
header2.appendChild(document.createTextNode(text));
|
||||
rows[1].appendChild(header2);
|
||||
@@ -883,16 +1362,17 @@ function prepareTableRows() {
|
||||
|
||||
var endTimeDate = $("endTime_date");
|
||||
var endDate = endTimeDate.valueAsDate();
|
||||
endDate.setTime(endDate.getTime() + (additionalDays * 86400000));
|
||||
endDate.setTime(endDate.getTime());
|
||||
|
||||
var rows = $("freeBusyData").tBodies[0].rows;
|
||||
var days = startDate.daysUpTo(endDate);
|
||||
var width = $('freeBusyHeader').getWidth();
|
||||
$("freeBusyData").setStyle({ width: width + 'px' });
|
||||
|
||||
for (var i = 0; i < days.length; i++)
|
||||
for (var rowNbr = 0; rowNbr < rows.length; rowNbr++)
|
||||
for (var hour = displayStartHour; hour < (displayEndHour + 1); hour++)
|
||||
rows[rowNbr].appendChild(document.createElement("td"));
|
||||
rows[rowNbr].appendChild(createElement("td"));
|
||||
}
|
||||
|
||||
function prepareAttendees() {
|
||||
@@ -946,8 +1426,8 @@ function prepareAttendees() {
|
||||
input.setAttribute("name", "");
|
||||
input.modified = false;
|
||||
input.observe("blur", onInputBlur);
|
||||
input.observe("keydown", onContactKeydown.bindAsEventListener(input)
|
||||
);
|
||||
input.observe("keydown",
|
||||
onContactKeydown.bindAsEventListener(input));
|
||||
|
||||
row = $(modelData.cloneNode(true));
|
||||
tbodyData.insertBefore(row, newDataRow);
|
||||
@@ -986,29 +1466,29 @@ function onScroll(event) {
|
||||
}
|
||||
|
||||
function onFreeBusyLoadHandler() {
|
||||
OwnerLogin = window.opener.getOwnerLogin();
|
||||
|
||||
var widgets = {'start': {'date': $("startTime_date"),
|
||||
'hour': $("startTime_time_hour"),
|
||||
'minute': $("startTime_time_minute")},
|
||||
'end': {'date': $("endTime_date"),
|
||||
'hour': $("endTime_time_hour"),
|
||||
'minute': $("endTime_time_minute")}};
|
||||
|
||||
OwnerLogin = window.opener.getOwnerLogin();
|
||||
|
||||
synchronizeWithParent("startTime", "startTime");
|
||||
synchronizeWithParent("endTime", "endTime");
|
||||
|
||||
initTimeWidgets(widgets);
|
||||
|
||||
initializeTimeSlotWidgets();
|
||||
initializeWindowButtons();
|
||||
prepareTableHeaders();
|
||||
prepareTableRows();
|
||||
redisplayFreeBusyZone();
|
||||
redisplayEventSpans();
|
||||
prepareAttendees();
|
||||
onWindowResize(null);
|
||||
Event.observe(window, "resize", onWindowResize);
|
||||
$$('TABLE#freeBusy TD.freeBusyData DIV').first().observe("scroll", onScroll);
|
||||
scrollToEvent ();
|
||||
toggleOfficeHours ();
|
||||
scrollToEvent();
|
||||
toggleOfficeHours();
|
||||
}
|
||||
|
||||
document.observe("dom:loaded", onFreeBusyLoadHandler);
|
||||
|
||||
Reference in New Issue
Block a user