- improved restoration of drag handles state
- improved contextual menu handling of Address Book module
- fixed various bugs occuring with Safari 3.1
+- monthly events would not be returned properly
+- bi-weekly events would appear every week instead
+- weekly events with specified days of week would not appear on the correct days
0.9.0-20080208 (1.0 rc5)
------------------------
+2008-03-26 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * iCalRecurrenceRule.m ([iCalRecurrenceRule -byMonthDay]): check
+ whether the "bymonthday" value is empty and returns an array only if
+ not, otherwise returns nil.
+
+ * iCalRecurrenceCalculator.m ([iCalRecurrenceCalculator
+ +recurrenceRangesWithinCalendarDateRange:_rfirstInstanceCalendarDateRange:_firrecurrenceRules:_rRulesexceptionRules:_exRulesexceptionDates:_exDates]):
+ split method in many submethods for clarity.
+
2008-03-10 Wolfgang Sourdeau <wsourdeau@inverse.ca>
* iCalTimeZonePeriod.m ([iCalTimeZonePeriod
#import <Foundation/NSArray.h>
#import <Foundation/NSString.h>
+#import "NSString+NGCards.h"
#import "NGCardsSaxHandler.h"
#import "CardGroup.h"
/* check whether the range to be processed is beyond the 'until' date */
- if (until != nil) {
+ if (until) {
if ([until compare:rStart] == NSOrderedAscending) /* until before start */
return nil;
if ([until compare:rEnd] == NSOrderedDescending) /* end before until */
/* precalculate month days (same for all instances) */
- if (byMonthDay != nil) {
+ if (byMonthDay) {
#if HEAVY_DEBUG
NSLog(@"byMonthDay: %@", byMonthDay);
#endif
didByFill = NO;
- if (byMonthDay != nil) { /* list of days in the month */
+ if (byMonthDay) { /* list of days in the month */
NGMonthDaySet_copyOrUnion(&monthDays, &byMonthDaySet, !didByFill);
didByFill = YES;
}
@implementation iCalRecurrenceCalculator
static Class NSCalendarDateClass = Nil;
+static Class NSStringClass = Nil;
static Class iCalRecurrenceRuleClass = Nil;
static Class dailyCalcClass = Nil;
static Class weeklyCalcClass = Nil;
static Class monthlyCalcClass = Nil;
static Class yearlyCalcClass = Nil;
-+ (void)initialize {
++ (void) initialize
+{
static BOOL didInit = NO;
if (didInit) return;
didInit = YES;
NSCalendarDateClass = [NSCalendarDate class];
+ NSStringClass = [NSString class];
iCalRecurrenceRuleClass = [iCalRecurrenceRule class];
dailyCalcClass = NSClassFromString(@"iCalDailyRecurrenceCalculator");
/* complex calculation convenience */
-+ (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r
- firstInstanceCalendarDateRange:(NGCalendarDateRange *)_fir
- recurrenceRules:(NSArray *)_rRules
- exceptionRules:(NSArray *)_exRules
- exceptionDates:(NSArray *)_exDates
++ (void) _fillRanges: (NSMutableArray *) ranges
+ fromRules: (NSArray *) rrules
+ withinRange: (NGCalendarDateRange *) limits
+ startingWithDate: (NGCalendarDateRange *) first
{
- id rule;
+ NSEnumerator *rules;
+ iCalRecurrenceRule *currentRule;
iCalRecurrenceCalculator *calc;
- NSMutableArray *ranges;
- NSMutableArray *exDates;
- unsigned i, count, rCount;
-
- ranges = [NSMutableArray arrayWithCapacity:64];
-
- for (i = 0, count = [_rRules count]; i < count; i++) {
- NSArray *rs;
-
- rule = [_rRules objectAtIndex:i];
- if (![rule isKindOfClass:iCalRecurrenceRuleClass])
- rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
-
- calc = [self recurrenceCalculatorForRecurrenceRule:rule
- withFirstInstanceCalendarDateRange:_fir];
-
- rs = [calc recurrenceRangesWithinCalendarDateRange:_r];
- [ranges addObjectsFromArray:rs];
- }
-
- if ([ranges count] == 0)
- return nil;
-
- /* test if any exceptions do match */
-
- for (i = 0, count = [_exRules count]; i < count; i++) {
- NSArray *rs;
-
- rule = [_exRules objectAtIndex:i];
- if (![rule isKindOfClass:iCalRecurrenceRuleClass])
- rule = [iCalRecurrenceRule recurrenceRuleWithICalRepresentation:rule];
-
- calc = [self recurrenceCalculatorForRecurrenceRule:rule
- withFirstInstanceCalendarDateRange:_fir];
- rs = [calc recurrenceRangesWithinCalendarDateRange:_r];
- [ranges removeObjectsInArray:rs];
- }
-
- if (![ranges isNotEmpty])
- return nil;
-
- /* exception dates */
- if ((count = [_exDates count]) == 0)
- return ranges;
-
- /* sort out exDates not within range */
+ rules = [rrules objectEnumerator];
+ while ((currentRule = [rules nextObject]))
+ {
+ if ([currentRule isKindOfClass: NSStringClass])
+ currentRule =
+ [iCalRecurrenceRule
+ recurrenceRuleWithICalRepresentation: (NSString *) currentRule];
+
+ calc = [self recurrenceCalculatorForRecurrenceRule: currentRule
+ withFirstInstanceCalendarDateRange: first];
+ [ranges addObjectsFromArray:
+ [calc recurrenceRangesWithinCalendarDateRange: limits]];
+ }
+}
- exDates = [NSMutableArray arrayWithCapacity:count];
- for (i = 0; i < count; i++) {
- id exDate;
++ (void) _removeExceptionsFromRanges: (NSMutableArray *) ranges
+ withRules: (NSArray *) exrules
+ withinRange: (NGCalendarDateRange *) limits
+ startingWithDate: (NGCalendarDateRange *) first
+{
+ NSEnumerator *rules;
+ iCalRecurrenceRule *currentRule;
+ iCalRecurrenceCalculator *calc;
- exDate = [_exDates objectAtIndex:i];
- if (![exDate isKindOfClass:NSCalendarDateClass])
- exDate = [NSCalendarDate calendarDateWithICalRepresentation:exDate];
-
- if ([_r containsDate:exDate])
- [exDates addObject:exDate];
- }
+ rules = [exrules objectEnumerator];
+ while ((currentRule = [rules nextObject]))
+ {
+ if ([currentRule isKindOfClass: NSStringClass])
+ currentRule =
+ [iCalRecurrenceRule
+ recurrenceRuleWithICalRepresentation: (NSString *) currentRule];
+
+ calc = [self recurrenceCalculatorForRecurrenceRule: currentRule
+ withFirstInstanceCalendarDateRange: first];
+ [ranges removeObjectsInArray:
+ [calc recurrenceRangesWithinCalendarDateRange: limits]];
+ }
+}
- /* remove matching exDates from ranges */
++ (NSArray *) _dates: (NSArray *) dateList
+ withinRange: (NGCalendarDateRange *) limits
+{
+ NSMutableArray *newDates;
+ NSEnumerator *dates;
+ NSCalendarDate *currentDate;
- if ((count = [exDates count]) == 0)
- return ranges;
-
- for (i = 0, rCount = [ranges count]; i < count; i++) {
- NSCalendarDate *exDate;
- NGCalendarDateRange *r;
- unsigned k;
-
- exDate = [exDates objectAtIndex:i];
-
- for (k = 0; k < rCount; k++) {
- unsigned rIdx;
-
- rIdx = (rCount - k) - 1;
- r = [ranges objectAtIndex:rIdx];
- if ([r containsDate:exDate]) {
- [ranges removeObjectAtIndex:rIdx];
- rCount--;
- break; /* this is safe because we know that ranges don't overlap */
+ newDates = [NSMutableArray array];
+
+ dates = [dateList objectEnumerator];
+ while ((currentDate = [dates nextObject]))
+ {
+ if ([currentDate isKindOfClass: NSStringClass])
+ currentDate
+ = [NSCalendarDate
+ calendarDateWithICalRepresentation: (NSString *) currentDate];
+ if ([limits containsDate: currentDate])
+ [newDates addObject: currentDate];
+ }
+
+ return newDates;
+}
+
++ (void) _removeExceptionDatesFromRanges: (NSMutableArray *) ranges
+ withDates: (NSArray *) exdates
+ withinRange: (NGCalendarDateRange *) limits
+ startingWithDate: (NGCalendarDateRange *) first
+{
+ NSEnumerator *dates;
+ NSCalendarDate *currentDate;
+ NGCalendarDateRange *currentRange;
+ int count, maxRanges;
+
+ maxRanges = [ranges count];
+ dates = [[self _dates: exdates withinRange: limits] objectEnumerator];
+ while ((currentDate = [dates nextObject]))
+ for (count = (maxRanges - 1); count > -1; count++)
+ {
+ currentRange = [ranges objectAtIndex: count];
+ if ([currentRange containsDate: currentDate])
+ {
+ [ranges removeObjectAtIndex: count];
+ maxRanges--;
+ }
}
+}
+
++ (NSArray *)
+ recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
+ firstInstanceCalendarDateRange: (NGCalendarDateRange *) _fir
+ recurrenceRules: (NSArray *) _rRules
+ exceptionRules: (NSArray *) _exRules
+ exceptionDates: (NSArray *) _exDates
+{
+ NSMutableArray *ranges;
+
+ ranges = [NSMutableArray arrayWithCapacity: 64];
+
+ if ([_rRules count] > 0)
+ {
+ [self _fillRanges: ranges fromRules: _rRules
+ withinRange: _r startingWithDate: _fir];
+ [self _removeExceptionsFromRanges: ranges withRules: _exRules
+ withinRange: _r startingWithDate: _fir];
+ [self _removeExceptionDatesFromRanges: ranges withDates: _exDates
+ withinRange: _r startingWithDate: _fir];
}
- }
+
return ranges;
}
- (iCalWeekDay) weekDayForJulianNumber: (long)_jn
{
- unsigned int day;
- iCalWeekDay weekDay;
iCalWeekDay weekDays[] = {iCalWeekDaySunday, iCalWeekDayMonday,
iCalWeekDayTuesday, iCalWeekDayWednesday,
iCalWeekDayThursday, iCalWeekDayFriday,
iCalWeekDaySaturday};
- if (day < 7)
- weekDay = weekDays[day];
- else
- {
- [self errorWithFormat:
- @"got unexpected weekday: %d (falling back on sunday)", day];
- weekDay = iCalWeekDaySunday;
- }
-
- return weekDay;
+ return weekDays[[self offsetFromSundayForJulianNumber: _jn]];
}
/* calculation */
- (NSArray *) byMonthDay
{
- return [[self namedValue: @"bymonthday"] componentsSeparatedByString: @","];
+ NSArray *byMonthDay;
+ NSString *byMonthDayStr;
+
+ byMonthDayStr = [self namedValue: @"bymonthday"];
+ if ([byMonthDayStr length])
+ byMonthDay = [byMonthDayStr componentsSeparatedByString: @","];
+ else
+ byMonthDay = nil;
+
+ return byMonthDay;
}
- (BOOL) isInfinite
foundDay = iCalWeekDayTuesday;
else if (chars[1] == 'H')
foundDay = iCalWeekDayThursday;
+ break;
case 'S':
if (chars[1] == 'A')
foundDay = iCalWeekDaySaturday;
else if (chars[1] == 'U')
foundDay = iCalWeekDaySunday;
+ break;
}
}
@interface iCalRecurrenceCalculator (PrivateAPI)
-- (NSCalendarDate *)lastInstanceStartDate;
+- (NSCalendarDate *) lastInstanceStartDate;
-- (unsigned)offsetFromSundayForJulianNumber:(long)_jn;
-- (unsigned)offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay;
-- (unsigned)offsetFromSundayForCurrentWeekStart;
+- (unsigned) offsetFromSundayForJulianNumber:(long)_jn;
+- (unsigned) offsetFromSundayForWeekDay:(iCalWeekDay)_weekDay;
+- (unsigned) offsetFromSundayForCurrentWeekStart;
-- (iCalWeekDay)weekDayForJulianNumber:(long)_jn;
+- (iCalWeekDay) weekDayForJulianNumber:(long)_jn;
@end
*/
@implementation iCalWeeklyRecurrenceCalculator
-- (NSArray *)recurrenceRangesWithinCalendarDateRange:(NGCalendarDateRange *)_r {
+- (NSArray *)
+ recurrenceRangesWithinCalendarDateRange: (NGCalendarDateRange *) _r
+{
NSMutableArray *ranges;
NSCalendarDate *firStart;
long i, jnFirst, jnStart, jnEnd, startEndCount;
</tr>
<tr class="mailer_fieldrow">
- <td class="mailer_fieldname"><var:string label:value="To"/>:</td>
+ <td class="mailer_fieldname"><img rsrc:src="plus.png" class="collapse" style="display: none;" /><var:string label:value="To"/>:</td>
<td class="mailer_fieldvalue">
- <var:foreach list="clientObject.toEnvelopeAddresses"
+ <span class="collapse"><var:foreach list="clientObject.toEnvelopeAddresses"
item="currentAddress"
><a var:href="currentAddressLink">
<var:string value="currentAddress"
formatter="context.mailEnvelopeFullAddressFormatter"
/></a>
- </var:foreach>
+ </var:foreach></span>
</td>
</tr>
<var:if condition="hasCC">
<tr class="mailer_fieldrow">
- <td class="mailer_fieldname"><var:string label:value="CC"/>:</td>
+ <td class="mailer_fieldname"><img rsrc:src="plus.png" class="collapse" style="display: none;" /><var:string label:value="CC"/>:</td>
<td class="mailer_fieldvalue">
- <var:foreach list="clientObject.ccEnvelopeAddresses"
+ <span class="collapse"><var:foreach list="clientObject.ccEnvelopeAddresses"
item="currentAddress">
<a var:href="currentAddressLink"
><var:string value="currentAddress"
formatter="context.mailEnvelopeFullAddressFormatter"
/></a>
<!-- TODO: better to use li+CSS -->
- </var:foreach>
+ </var:foreach></span>
</td>
</tr>
</var:if>
><var:string label:value="Next hour" /></a>
</div>
<div id="freeBusyReplicas">
- <div><var:string label:value="From"
+ <div><var:string label:value="Start:"
/><var:component className="UIxTimeDateControl"
const:controlID="startTime"
const:dayStartHour="0"
const:dayEndHour="23"
/></div>
- <div><var:string label:value="To"
+ <div><var:string label:value="End:"
/><var:component className="UIxTimeDateControl"
const:controlID="endTime"
const:dayStartHour="0"
table-layout: auto;
}
+/* collapsable header */
+TD.mailer_fieldname IMG
+{ cursor: pointer;
+ padding-right: 5px; }
+TD.mailer_fieldvalue SPAN.collapse
+{ white-space: nowrap; }
+TD.mailer_fieldvalue SPAN.expand
+{ white-space: normal; }
+
TD.mailer_fieldname
{
white-space: nowrap;
}
TD.mailer_fieldvalue
-{
- vertical-align: top;
-}
+{ vertical-align: top; }
TD.mailer_subjectfieldvalue
{
contentDiv.setStyle({ 'top':
(Element.getHeight(headerTable) + headerTable.offsetTop) + 'px' });
+
+ // Show expand buttons if necessary
+ var spans = $$("TABLE TR.mailer_fieldrow TD.mailer_fieldvalue SPAN");
+ spans.each(function(span) {
+ var row = span.up("TR");
+ if (span.getWidth() > row.getWidth()) {
+ var cell = row.select("TD.mailer_fieldname").first();
+ var link = cell.down("img");
+ link.show();
+ link.observe("click", toggleDisplayHeader);
+ }
+ });
+}
+
+function toggleDisplayHeader(event) {
+ var row = this.up("TR");
+ var span = row.down("SPAN");
+
+ if (this.hasClassName("collapse")) {
+ this.writeAttribute("src", ResourcesURL + '/minus.png');
+ this.writeAttribute("class", "expand");
+ span.writeAttribute("class", "expand");
+ }
+ else {
+ this.writeAttribute("src", ResourcesURL + '/plus.png');
+ this.writeAttribute("class", "collapse");
+ span.writeAttribute("class", "collapse");
+ }
+ resizeMailContent();
+
+ preventDefault(event);
+ return false;
}
function onMessageContentMenu(event) {
var id = currentNode.getAttribute("id");
var number = parseInt(id.substr(2));
if (number > 0) {
- var cn = mailboxTree.aNodes[number];
- mailboxTree.nodeStatus(1, number, cn._ls);
+ var cn = mailboxTree.aNodes[number];
+ mailboxTree.nodeStatus(1, number, cn._ls);
}
}
currentNode = currentNode.parentNode;
if ($(cell).hasClassName("tbtv_navcell")) {
var anchors = $(cell).childNodesWithTag("a");
for (var i = 0; i < anchors.length; i++)
- Event.observe(anchors[i], "click",
- openMailboxAtIndex.bindAsEventListener(anchors[i]));
+ Event.observe(anchors[i], "click",
+ openMailboxAtIndex.bindAsEventListener(anchors[i]));
}
rows = table.tBodies[0].rows;
if (userName.length > 0) {
var url = $("connectForm").getAttribute("action");
- var parameters = ("userName=" + userName + "&password=" + password);
+ var parameters = ("userName=" + encodeURI(userName) + "&password=" + encodeURI(password));
document.cookie = "";
triggerAjaxRequest(url, onLoginCallback, null, parameters,
{ "Content-type": "application/x-www-form-urlencoded",
if (isHttpStatus204(http.status)) {
var userName = $("userName").value;
var address = "" + window.location.href;
- var baseAddress = ApplicationBaseURL + $("userName").value;
+ var baseAddress = ApplicationBaseURL + encodeURI(userName);
var altBaseAddress;
if (baseAddress[0] == "/") {
var parts = address.split("/");
// Change the status of a node(open or closed)
dTree.prototype.nodeStatus = function(status, id, bottom) {
eDiv = document.getElementById('d' + this.obj + id);
- eJoin = document.getElementById('j' + this.obj + id);
- if (this.config.useIcons) {
- eIcon = document.getElementById('i' + this.obj + id);
- eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[id].icon;
+ if (eDiv) {
+ eJoin = document.getElementById('j' + this.obj + id);
+ if (this.config.useIcons) {
+ eIcon = document.getElementById('i' + this.obj + id);
+ eIcon.src = (status) ? this.aNodes[id].iconOpen : this.aNodes[id].icon;
+ }
+ eJoin.src = (this.config.useLines)?
+ ((status)?((bottom)?this.icon.minusBottom:this.icon.minus):((bottom)?this.icon.plusBottom:this.icon.plus)):
+ ((status)?this.icon.nlMinus:this.icon.nlPlus);
+ eDiv.style.display = (status) ? 'block': 'none';
}
- eJoin.src = (this.config.useLines)?
- ((status)?((bottom)?this.icon.minusBottom:this.icon.minus):((bottom)?this.icon.plusBottom:this.icon.plus)):
- ((status)?this.icon.nlMinus:this.icon.nlPlus);
- eDiv.style.display = (status) ? 'block': 'none';
};
var list = node.parentNode;
var items = list.childNodesWithTag("li");
for (var i = 0; i < items.length; i++) {
- if (items[i] == node) {
- rowIndex = i;
- break;
+ if (items[i] == node) {
+ rowIndex = i;
+ break;
}
}
}