Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { calItemBase, makeMemberAttrProperty } from "resource:///modules/CalItemBase.sys.mjs";
/**
* Constructor for `calITodo` objects.
*
* @class
* @implements {calITodo}
* @param {string} [icalString] - Optional iCal string for initializing existing todos.
*/
export function CalTodo(icalString) {
this.initItemBase();
this.todoPromotedProps = {
DTSTART: true,
DTEND: true,
DUE: true,
COMPLETED: true,
__proto__: this.itemBasePromotedProps,
};
if (icalString) {
this.icalString = icalString;
}
// Set a default percentComplete if the icalString didn't already set it.
if (!this.percentComplete) {
this.percentComplete = 0;
}
}
var calTodoClassID = Components.ID("{7af51168-6abe-4a31-984d-6f8a3989212d}");
var calTodoInterfaces = [Ci.calIItemBase, Ci.calITodo, Ci.calIInternalShallowCopy];
CalTodo.prototype = {
__proto__: calItemBase.prototype,
classID: calTodoClassID,
QueryInterface: cal.generateQI(["calIItemBase", "calITodo", "calIInternalShallowCopy"]),
classInfo: cal.generateCI({
classID: calTodoClassID,
contractID: "@mozilla.org/calendar/todo;1",
classDescription: "Calendar Todo",
interfaces: calTodoInterfaces,
}),
cloneShallow(aNewParent) {
const cloned = new CalTodo();
this.cloneItemBaseInto(cloned, aNewParent);
return cloned;
},
createProxy(aRecurrenceId) {
cal.ASSERT(!this.mIsProxy, "Tried to create a proxy for an existing proxy!", true);
const proxy = new CalTodo();
// override proxy's DTSTART/DUE/RECURRENCE-ID
// before master is set (and item might get immutable):
const duration = this.duration;
if (duration) {
const dueDate = aRecurrenceId.clone();
dueDate.addDuration(duration);
proxy.dueDate = dueDate;
}
proxy.entryDate = aRecurrenceId;
proxy.initializeProxy(this, aRecurrenceId);
proxy.mDirty = false;
return proxy;
},
makeImmutable() {
this.makeItemBaseImmutable();
},
isTodo() {
return true;
},
get isCompleted() {
return this.completedDate != null || this.percentComplete == 100 || this.status == "COMPLETED";
},
set isCompleted(completed) {
if (completed) {
if (!this.completedDate) {
this.completedDate = cal.dtz.jsDateToDateTime(new Date());
}
this.status = "COMPLETED";
this.percentComplete = 100;
} else {
this.deleteProperty("COMPLETED");
this.deleteProperty("STATUS");
this.deleteProperty("PERCENT-COMPLETE");
}
},
get duration() {
const dur = this.getProperty("DURATION");
// pick up duration if available, otherwise calculate difference
// between start and enddate
if (dur) {
return cal.createDuration(dur);
}
if (!this.entryDate || !this.dueDate) {
return null;
}
return this.dueDate.subtractDate(this.entryDate);
},
set duration(value) {
this.setProperty("DURATION", value);
},
get recurrenceStartDate() {
// DTSTART is optional for VTODOs, so it's unclear if RRULE is allowed then,
// so fallback to DUE if no DTSTART is present:
return this.entryDate || this.dueDate;
},
icsEventPropMap: [
{ cal: "DTSTART", ics: "startTime" },
{ cal: "DUE", ics: "dueTime" },
{ cal: "COMPLETED", ics: "completedTime" },
],
set icalString(value) {
this.icalComponent = cal.icsService.parseICS(value);
},
get icalString() {
const calcomp = cal.icsService.createIcalComponent("VCALENDAR");
cal.item.setStaticProps(calcomp);
calcomp.addSubcomponent(this.icalComponent);
return calcomp.serializeToICS();
},
get icalComponent() {
const icalcomp = cal.icsService.createIcalComponent("VTODO");
this.fillIcalComponentFromBase(icalcomp);
this.mapPropsToICS(icalcomp, this.icsEventPropMap);
for (const [name, value] of this.properties) {
try {
// When deleting a property of an occurrence, the property is not actually deleted
// but instead set to null, so we need to prevent adding those properties.
const wasReset = this.mIsProxy && value === null;
if (!this.todoPromotedProps[name] && !wasReset) {
const icalprop = cal.icsService.createIcalProperty(name);
icalprop.value = value;
const propBucket = this.mPropertyParams[name];
if (propBucket) {
for (const paramName in propBucket) {
try {
icalprop.setParameter(paramName, propBucket[paramName]);
} catch (e) {
if (e.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
// Illegal values should be ignored, but we could log them if
// the user has enabled logging.
cal.LOG(
"Warning: Invalid todo parameter value " +
paramName +
"=" +
propBucket[paramName]
);
} else {
throw e;
}
}
}
}
icalcomp.addProperty(icalprop);
}
} catch (e) {
cal.ERROR("failed to set " + name + " to " + value + ": " + e + "\n");
}
}
return icalcomp;
},
todoPromotedProps: null,
set icalComponent(todo) {
this.modify();
if (todo.componentType != "VTODO") {
todo = todo.getFirstSubcomponent("VTODO");
if (!todo) {
throw Components.Exception("", Cr.NS_ERROR_INVALID_ARG);
}
}
this.mDueDate = undefined;
this.setItemBaseFromICS(todo);
this.mapPropsFromICS(todo, this.icsEventPropMap);
this.importUnpromotedProperties(todo, this.todoPromotedProps);
// Importing didn't really change anything
this.mDirty = false;
},
isPropertyPromoted(name) {
// avoid strict undefined property warning
return this.todoPromotedProps[name] || false;
},
set entryDate(value) {
this.modify();
// We're about to change the start date of an item which probably
// could break the associated calIRecurrenceInfo. We're calling
// the appropriate method here to adjust the internal structure in
// order to free clients from worrying about such details.
if (this.parentItem == this) {
const rec = this.recurrenceInfo;
if (rec) {
rec.onStartDateChange(value, this.entryDate);
}
}
this.setProperty("DTSTART", value);
},
get entryDate() {
return this.getProperty("DTSTART");
},
mDueDate: undefined,
get dueDate() {
let dueDate = this.mDueDate;
if (dueDate === undefined) {
dueDate = this.getProperty("DUE");
if (!dueDate) {
const entryDate = this.entryDate;
const dur = this.getProperty("DURATION");
if (entryDate && dur) {
// If there is a duration set on the todo, calculate the right end time.
dueDate = entryDate.clone();
dueDate.addDuration(cal.createDuration(dur));
}
}
this.mDueDate = dueDate;
}
return dueDate;
},
set dueDate(value) {
this.deleteProperty("DURATION"); // setting dueDate once removes DURATION
this.setProperty("DUE", value);
this.mDueDate = value;
},
};
makeMemberAttrProperty(CalTodo, "COMPLETED", "completedDate");
makeMemberAttrProperty(CalTodo, "PERCENT-COMPLETE", "percentComplete");