mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
1109 lines
38 KiB
JavaScript
1109 lines
38 KiB
JavaScript
/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Oracle Corporation code.
|
|
*
|
|
* The Initial Developer of the Original Code is Oracle Corporation
|
|
* Portions created by the Initial Developer are Copyright (C) 2005
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Stuart Parmenter <stuart.parmenter@oracle.com>
|
|
* Joey Minta <jminta@gmail.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
var gReadOnlyMode = false;
|
|
|
|
// If the user clicks 'More', then we set this to true. Otherwise, we don't
|
|
// load/check the stuff in this area, because it hasn't changed.
|
|
var gDetailsShown = false;
|
|
|
|
var gItemDuration;
|
|
|
|
/* dialog stuff */
|
|
function onLoad()
|
|
{
|
|
var args = window.arguments[0];
|
|
|
|
window.onAcceptCallback = args.onOk;
|
|
window.calendarItem = args.calendarEvent;
|
|
window.mode = args.mode;
|
|
window.recurrenceInfo = null;
|
|
|
|
if (window.calendarItem.calendar && window.calendarItem.calendar.readOnly) {
|
|
gReadOnlyMode = true;
|
|
}
|
|
|
|
/* add calendars to the calendar menulist */
|
|
var calendarList = document.getElementById("item-calendar");
|
|
var calendars = getCalendarManager().getCalendars({});
|
|
for (i in calendars) {
|
|
var calendar = calendars[i];
|
|
var menuitem = calendarList.appendItem(calendar.name, i);
|
|
menuitem.calendar = calendar;
|
|
}
|
|
|
|
loadDialog(window.calendarItem);
|
|
|
|
// figure out what the title of the dialog should be and set it
|
|
updateTitle();
|
|
|
|
// hide rows based on if this is an event or todo
|
|
updateStyle();
|
|
|
|
// update the accept button
|
|
updateAccept();
|
|
|
|
// update datetime pickers
|
|
updateDueDate();
|
|
updateEntryDate();
|
|
|
|
// update datetime pickers
|
|
updateAllDay();
|
|
|
|
// update recurrence button
|
|
updateRecurrence();
|
|
|
|
// update our size!
|
|
window.sizeToContent();
|
|
|
|
document.getElementById("item-title").focus();
|
|
|
|
opener.setCursor("auto");
|
|
|
|
self.focus();
|
|
}
|
|
|
|
function onAccept()
|
|
{
|
|
// if this event isn't mutable, we need to clone it like a sheep
|
|
var originalItem = window.calendarItem;
|
|
var item = (originalItem.isMutable) ? originalItem : originalItem.clone();
|
|
|
|
saveDialog(item);
|
|
|
|
var calendar = document.getElementById("item-calendar").selectedItem.calendar;
|
|
|
|
window.onAcceptCallback(item, calendar, originalItem);
|
|
|
|
return true;
|
|
}
|
|
|
|
function onCancel()
|
|
{
|
|
|
|
}
|
|
|
|
function loadDialog(item)
|
|
{
|
|
var kDefaultTimezone = calendarDefaultTimezone();
|
|
|
|
setElementValue("item-title", item.title);
|
|
setElementValue("item-location", item.getProperty("LOCATION"));
|
|
|
|
/* event specific properties */
|
|
if (isEvent(item)) {
|
|
var startDate = item.startDate.getInTimezone(kDefaultTimezone);
|
|
var endDate = item.endDate.getInTimezone(kDefaultTimezone);
|
|
gItemDuration = endDate.subtractDate(startDate);
|
|
|
|
// Check if an all-day event has been passed in (to adapt endDate).
|
|
if (startDate.isDate) {
|
|
setElementValue("event-all-day", true, "checked");
|
|
endDate.day -= 1;
|
|
gItemDuration.days -= 1;
|
|
|
|
// The date/timepicker uses jsDate internally. Because jsDate does
|
|
// not know the concept of dates we end up displaying times unequal
|
|
// to 00:00 for all-day events depending on local timezone setting.
|
|
// Calling normalize() recalculates that times to represent 00:00
|
|
// in local timezone.
|
|
endDate.normalize();
|
|
startDate.normalize();
|
|
}
|
|
|
|
setElementValue("event-starttime", startDate.jsDate);
|
|
setElementValue("event-endtime", endDate.jsDate);
|
|
document.getElementById("component-type").selectedIndex = 0;
|
|
}
|
|
|
|
/* todo specific properties */
|
|
if (isToDo(item)) {
|
|
var hasEntryDate = (item.entryDate != null);
|
|
setElementValue("todo-has-entrydate", hasEntryDate, "checked");
|
|
if (hasEntryDate)
|
|
setElementValue("todo-entrydate", item.entryDate.jsDate);
|
|
|
|
var hasDueDate = (item.dueDate != null);
|
|
setElementValue("todo-has-duedate", hasDueDate, "checked");
|
|
if (hasDueDate)
|
|
setElementValue("todo-duedate", item.dueDate.jsDate);
|
|
if (hasEntryDate && hasDueDate) {
|
|
gItemDuration = item.dueDate.subtractDate(item.entryDate);
|
|
}
|
|
document.getElementById("component-type").selectedIndex = 1;
|
|
}
|
|
|
|
/* item default calendar */
|
|
// If this is a new item, it might not have a calendar, but a default
|
|
// option could well have been passed in.
|
|
var calendarToUse = item.calendar || window.arguments[0].calendar
|
|
if (calendarToUse) {
|
|
var calendarList = document.getElementById("item-calendar");
|
|
var calendars = getCalendarManager().getCalendars({});
|
|
for (i in calendars) {
|
|
if (calendarToUse.uri.equals(calendars[i].uri))
|
|
calendarList.selectedIndex = i;
|
|
}
|
|
} else {
|
|
// no calendar attached to item
|
|
// select first entry in calendar list as default
|
|
document.getElementById("item-calendar").selectedIndex = 0;
|
|
}
|
|
|
|
/* Categories */
|
|
try {
|
|
var categoriesString = getLocalizedPref("calendar.categories.names");
|
|
var categoriesList = categoriesString.split( "," );
|
|
|
|
// insert the category already in the menulist so it doesn't get lost
|
|
var itemCategory = item.getProperty("CATEGORIES");
|
|
if (itemCategory) {
|
|
if (categoriesString.indexOf(itemCategory) == -1)
|
|
categoriesList[categoriesList.length] = itemCategory;
|
|
}
|
|
categoriesList.sort();
|
|
|
|
var oldMenulist = document.getElementById("item-categories");
|
|
while (oldMenulist.hasChildNodes()) {
|
|
oldMenulist.removeChild(oldMenulist.lastChild);
|
|
}
|
|
|
|
var categoryMenuList = document.getElementById("item-categories");
|
|
var indexToSelect = 0;
|
|
|
|
// Add a 'none' option to allow users to cancel the category
|
|
var noneItem = categoryMenuList.appendItem(calGetString("calendar", "None"), "NONE");
|
|
|
|
for (var i in categoriesList) {
|
|
var catItem = categoryMenuList.appendItem(categoriesList[i], categoriesList[i]);
|
|
catItem.value = categoriesList[i];
|
|
if (itemCategory && categoriesList[i] == itemCategory) {
|
|
indexToSelect = parseInt(i)+1; // Add 1 because of 'None'
|
|
}
|
|
}
|
|
var newCategory = calGetString("calendar", "newCategory");
|
|
categoryMenuList.appendItem(newCategory, "##NEW");
|
|
categoryMenuList.selectedIndex = indexToSelect;
|
|
gOldCatIndex = indexToSelect;
|
|
|
|
} catch (ex) {
|
|
// The app using this dialog doesn't support categories
|
|
document.getElementById("categories-box").collapsed = true;
|
|
}
|
|
|
|
|
|
/* recurrence */
|
|
/* if the item is a proxy occurrence/instance, a few things aren't valid:
|
|
* - Setting recurrence on the item
|
|
* - changing the calendar
|
|
*/
|
|
if (item.parentItem != item) {
|
|
setElementValue("item-recurrence", "true", "disabled");
|
|
setElementValue("set-recurrence", "true", "disabled");
|
|
setElementValue("item-calendar", "true", "disabled");
|
|
|
|
// don't allow to revoke the entrydate of recurring todo's.
|
|
disableElement("todo-has-entrydate");
|
|
} else if (item.recurrenceInfo)
|
|
setElementValue("item-recurrence", "true", "checked");
|
|
|
|
var detailsButton = document.getElementById("calendar-event-dialog").getButton("disclosure");
|
|
var detailsElements = document.getElementsByAttribute("details", "true");
|
|
|
|
if (document.getElementById("description-row").getAttribute("collapsed") != "true") {
|
|
detailsButton.setAttribute("label", lessLabel);
|
|
for each (elem in detailsElements) {
|
|
elem.collapsed = false;
|
|
}
|
|
loadDetails();
|
|
} else {
|
|
for each (elem in detailsElements) {
|
|
elem.collapsed = true;
|
|
}
|
|
detailsButton.setAttribute("label", moreLabel);
|
|
}
|
|
}
|
|
|
|
function saveDialog(item)
|
|
{
|
|
setItemProperty(item, "title", getElementValue("item-title"));
|
|
setItemProperty(item, "LOCATION", getElementValue("item-location"));
|
|
|
|
var kDefaultTimezone = calendarDefaultTimezone();
|
|
|
|
if (isEvent(item)) {
|
|
var startDate = jsDateToDateTime(getElementValue("event-starttime"));
|
|
var endDate = jsDateToDateTime(getElementValue("event-endtime"));
|
|
startDate = startDate.getInTimezone(kDefaultTimezone);
|
|
endDate = endDate.getInTimezone(kDefaultTimezone);
|
|
|
|
var isAllDay = getElementValue("event-all-day", "checked");
|
|
if (isAllDay) {
|
|
startDate.isDate = true;
|
|
startDate.normalize();
|
|
|
|
endDate.isDate = true;
|
|
endDate.day += 1;
|
|
endDate.normalize();
|
|
}
|
|
|
|
setItemProperty(item, "startDate", startDate);
|
|
setItemProperty(item, "endDate", endDate);
|
|
}
|
|
|
|
if (isToDo(item)) {
|
|
var entryDate = getElementValue("todo-has-entrydate", "checked") ?
|
|
jsDateToDateTime(getElementValue("todo-entrydate")) : null;
|
|
if (entryDate) {
|
|
entryDate = entryDate.getInTimezone(kDefaultTimezone);
|
|
} else {
|
|
// no entrydate, no recurrence
|
|
item.recurrenceInfo = null;
|
|
window.recurrenceInfo = null;
|
|
}
|
|
setItemProperty(item, "entryDate", entryDate);
|
|
|
|
var dueDate = getElementValue("todo-has-duedate", "checked") ?
|
|
jsDateToDateTime(getElementValue("todo-duedate")) : null;
|
|
if (dueDate) {
|
|
dueDate = dueDate.getInTimezone(kDefaultTimezone);
|
|
}
|
|
setItemProperty(item, "dueDate", dueDate);
|
|
|
|
var percentCompleteInteger = 0;
|
|
if (getElementValue("percent-complete-textbox") != "") {
|
|
percentCompleteInteger = parseInt(getElementValue("percent-complete-textbox"));
|
|
}
|
|
if (percentCompleteInteger < 0) {
|
|
percentCompleteInteger = 0;
|
|
} else if (percentCompleteInteger > 100) {
|
|
percentCompleteInteger = 100;
|
|
}
|
|
setItemProperty(item, "PERCENT-COMPLETE", percentCompleteInteger);
|
|
}
|
|
|
|
/* recurrence */
|
|
if (getElementValue("item-recurrence", "checked")) {
|
|
if (window.recurrenceInfo) {
|
|
item.recurrenceInfo = window.recurrenceInfo;
|
|
}
|
|
} else {
|
|
item.recurrenceInfo = null;
|
|
}
|
|
|
|
/* Category */
|
|
var category = getElementValue("item-categories");
|
|
|
|
if (category != "NONE") {
|
|
setItemProperty(item, "CATEGORIES", category);
|
|
} else {
|
|
item.deleteProperty("CATEGORIES");
|
|
}
|
|
|
|
if (!gDetailsShown) {
|
|
// We never showed the items in the 'More' box. That means that clone()
|
|
// took care of it, so just return now
|
|
dump(item.icalString + '\n');
|
|
return;
|
|
}
|
|
|
|
setItemProperty(item, "URL", getElementValue("item-url"));
|
|
setItemProperty(item, "DESCRIPTION", getElementValue("item-description"));
|
|
|
|
var status;
|
|
if (isEvent(item)) {
|
|
status = getElementValue("event-status");
|
|
} else {
|
|
status = getElementValue("todo-status");
|
|
if (status != "COMPLETED") {
|
|
item.completedDate = null;
|
|
}
|
|
}
|
|
|
|
setItemProperty(item, "STATUS", status);
|
|
setItemProperty(item, "PRIORITY", getElementValue("priority-levels"));
|
|
setItemProperty(item, "CLASS", getElementValue("privacy-menulist"));
|
|
|
|
if (item.status == "COMPLETED" && isToDo(item)) {
|
|
item.completedDate = jsDateToDateTime(getElementValue("completed-date-picker"));
|
|
}
|
|
|
|
/* attendence */
|
|
item.removeAllAttendees();
|
|
var attendeeListBox = document.getElementById("attendees-listbox");
|
|
for each (kid in attendeeListBox.childNodes) {
|
|
if (kid.attendee) {
|
|
item.addAttendee(kid.attendee);
|
|
}
|
|
}
|
|
|
|
/* alarms */
|
|
var hasAlarm = (getElementValue("item-alarm") != "none");
|
|
if (!hasAlarm) {
|
|
item.alarmOffset = null;
|
|
item.alarmLastAck = null;
|
|
item.alarmRelated = null;
|
|
} else {
|
|
var alarmLength = getElementValue("alarm-length-field");
|
|
var alarmUnits = document.getElementById("alarm-length-units").selectedItem.value;
|
|
if (document.getElementById("alarm-trigger-relation").selectedItem.value == "START") {
|
|
item.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_START;
|
|
} else {
|
|
item.alarmRelated = Components.interfaces.calIItemBase.ALARM_RELATED_END;
|
|
}
|
|
var duration = Components.classes["@mozilla.org/calendar/duration;1"]
|
|
.createInstance(Components.interfaces.calIDuration);
|
|
if (item.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_START) {
|
|
duration.isNegative = true;
|
|
}
|
|
duration[alarmUnits] = alarmLength;
|
|
duration.normalize();
|
|
|
|
item.alarmOffset = duration;
|
|
}
|
|
|
|
dump(item.icalString + "\n");
|
|
}
|
|
|
|
function updateTitle()
|
|
{
|
|
var isNew = window.calendarItem.isMutable;
|
|
if (isEvent(window.calendarItem)) {
|
|
if (isNew)
|
|
document.title = calGetString("calendar", "newEventDialog");
|
|
else
|
|
document.title = calGetString("calendar", "editEventDialog");
|
|
} else if (isToDo(window.calendarItem)) {
|
|
if (isNew)
|
|
document.title = calGetString("calendar", "newTaskDialog");
|
|
else
|
|
document.title = calGetString("calendar", "editTaskDialog");
|
|
}
|
|
}
|
|
|
|
function updateComponentType() {
|
|
//XXX We still can't properly convert from event <-> task via QI
|
|
return;
|
|
}
|
|
|
|
function updateStyle()
|
|
{
|
|
const kDialogStylesheet = "chrome://calendar/content/calendar-event-dialog.css";
|
|
|
|
for each(var stylesheet in document.styleSheets) {
|
|
if (stylesheet.href == kDialogStylesheet) {
|
|
if (isEvent(window.calendarItem))
|
|
stylesheet.insertRule(".todo-only { display: none; }", 0);
|
|
else if (isToDo(window.calendarItem))
|
|
stylesheet.insertRule(".event-only { display: none; }", 0);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
function onStartTimeChange()
|
|
{
|
|
if (!gItemDuration) {
|
|
return;
|
|
}
|
|
var startWidgetId;
|
|
var endWidgetId;
|
|
if (isEvent(window.calendarItem)) {
|
|
startWidgetId = "event-starttime";
|
|
endWidgetId = "event-endtime";
|
|
} else {
|
|
if (!getElementValue("todo-has-entrydate", "checked") || !getElementValue("todo-has-duedate", "checked")) {
|
|
gItemDuration = null;
|
|
return;
|
|
}
|
|
startWidgetId = "todo-entrydate";
|
|
endWidgetId = "todo-duedate";
|
|
}
|
|
var start = jsDateToDateTime(getElementValue(startWidgetId));
|
|
start.addDuration(gItemDuration);
|
|
setElementValue(endWidgetId, start.getInTimezone(calendarDefaultTimezone()).jsDate);
|
|
updateAccept();
|
|
}
|
|
|
|
function onEndTimeChange()
|
|
{
|
|
var startWidgetId;
|
|
var endWidgetId;
|
|
if (isEvent(window.calendarItem)) {
|
|
startWidgetId = "event-starttime";
|
|
endWidgetId = "event-endtime";
|
|
} else {
|
|
if (!getElementValue("todo-has-entrydate", "checked") ||
|
|
!getElementValue("todo-has-duedate", "checked")) {
|
|
gItemDuration = null;
|
|
return;
|
|
}
|
|
startWidgetId = "todo-entrydate";
|
|
endWidgetId = "todo-duedate";
|
|
}
|
|
var start = jsDateToDateTime(getElementValue(startWidgetId));
|
|
var end = jsDateToDateTime(getElementValue(endWidgetId));
|
|
gItemDuration = end.subtractDate(start);
|
|
updateAccept();
|
|
}
|
|
function updateAccept()
|
|
{
|
|
var enableAccept = true;
|
|
|
|
var kDefaultTimezone = calendarDefaultTimezone();
|
|
|
|
var title = getElementValue("item-title");
|
|
if (title.length == 0)
|
|
enableAccept = false;
|
|
|
|
// don't allow for end dates to be before start dates
|
|
var startDate;
|
|
var endDate;
|
|
if (isEvent(window.calendarItem)) {
|
|
startDate = jsDateToDateTime(getElementValue("event-starttime"));
|
|
endDate = jsDateToDateTime(getElementValue("event-endtime"));
|
|
|
|
// For all-day events we are not interested in times and compare only dates.
|
|
if (getElementValue("event-all-day", "checked")) {
|
|
// jsDateToDateTime returnes the values in UTC. Depending on the local
|
|
// timezone and the values selected in datetimepicker the date in UTC
|
|
// might be shifted to the previous or next day.
|
|
// For example: The user (with local timezone GMT+05) selected
|
|
// Feb 10 2006 00:00:00. The corresponding value in UTC is
|
|
// Feb 09 2006 19:00:00. If we now set isDate to true we end up with
|
|
// a date of Feb 09 2006 instead of Feb 10 2006 resulting in errors
|
|
// during the following comparison.
|
|
// Calling getInTimezone() ensures that we use the same dates as
|
|
// displayed to the user in datetimepicker for comparison.
|
|
startDate = startDate.getInTimezone(kDefaultTimezone);
|
|
endDate = endDate.getInTimezone(kDefaultTimezone);
|
|
startDate.isDate = true;
|
|
endDate.isDate = true;
|
|
}
|
|
} else {
|
|
startDate = getElementValue("todo-has-entrydate", "checked") ?
|
|
jsDateToDateTime(getElementValue("todo-entrydate")) : null;
|
|
endDate = getElementValue("todo-has-duedate", "checked") ?
|
|
jsDateToDateTime(getElementValue("todo-duedate")) : null;
|
|
|
|
var taskRepeatWarning = document.getElementById("task-repeat-warning");
|
|
if (!startDate && getElementValue("item-recurrence", "checked")) {
|
|
enableAccept = false;
|
|
taskRepeatWarning.removeAttribute("hidden");
|
|
} else {
|
|
taskRepeatWarning.setAttribute("hidden", "true");
|
|
}
|
|
}
|
|
|
|
var timeWarning = document.getElementById("end-time-warning");
|
|
if (endDate && startDate && endDate.compare(startDate) == -1) {
|
|
enableAccept = false;
|
|
timeWarning.removeAttribute("hidden");
|
|
} else {
|
|
timeWarning.setAttribute("hidden", "true");
|
|
}
|
|
|
|
// can't add/edit items in readOnly calendars
|
|
document.getElementById("read-only-item").setAttribute("hidden", !gReadOnlyMode);
|
|
var cal = document.getElementById("item-calendar").selectedItem.calendar;
|
|
document.getElementById("read-only-cal").setAttribute("hidden",
|
|
!cal.readOnly);
|
|
if (gReadOnlyMode || cal.readOnly) {
|
|
enableAccept = false;
|
|
}
|
|
|
|
if (!updateTaskAlarmWarnings()) {
|
|
enableAccept = false;
|
|
}
|
|
|
|
var acceptButton = document.getElementById("calendar-event-dialog").getButton("accept");
|
|
if (!enableAccept) {
|
|
acceptButton.setAttribute("disabled", "true");
|
|
} else if (acceptButton.getAttribute("disabled")) {
|
|
acceptButton.removeAttribute("disabled");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
function updateDueDate()
|
|
{
|
|
if (!isToDo(window.calendarItem))
|
|
return;
|
|
|
|
// force something to get set if there was nothing there before
|
|
setElementValue("todo-duedate", getElementValue("todo-duedate"));
|
|
|
|
setElementValue("todo-duedate", !getElementValue("todo-has-duedate", "checked"), "disabled");
|
|
if (getElementValue("todo-has-entrydate", "checked") && getElementValue("todo-has-duedate", "checked")) {
|
|
var start = jsDateToDateTime(getElementValue("todo-entrydate"));
|
|
var end = jsDateToDateTime(getElementValue("todo-duedate"));
|
|
gItemDuration = end.subtractDate(start);
|
|
} else {
|
|
gItemDuration = null;
|
|
}
|
|
|
|
updateAccept();
|
|
}
|
|
|
|
function updateEntryDate()
|
|
{
|
|
if (!isToDo(window.calendarItem))
|
|
return;
|
|
|
|
// force something to get set if there was nothing there before
|
|
setElementValue("todo-entrydate", getElementValue("todo-entrydate"));
|
|
|
|
setElementValue("todo-entrydate", !getElementValue("todo-has-entrydate", "checked"), "disabled");
|
|
|
|
if (getElementValue("todo-has-entrydate", "checked") && getElementValue("todo-has-duedate", "checked")) {
|
|
var start = jsDateToDateTime(getElementValue("todo-entrydate"));
|
|
var end = jsDateToDateTime(getElementValue("todo-duedate"));
|
|
gItemDuration = end.subtractDate(start);
|
|
} else {
|
|
gItemDuration = null;
|
|
}
|
|
|
|
updateAccept();
|
|
}
|
|
|
|
function updateTaskAlarmWarnings() {
|
|
document.getElementById("alarm-start-warning").setAttribute("hidden", true);
|
|
document.getElementById("alarm-end-warning").setAttribute("hidden", true);
|
|
|
|
var alarmType = getElementValue("item-alarm");
|
|
if (!gDetailsShown || !isToDo(window.calendarItem) || alarmType == "none") {
|
|
return true;
|
|
}
|
|
|
|
var hasEntryDate = getElementValue("todo-has-entrydate", "checked");
|
|
var hasDueDate = getElementValue("todo-has-duedate", "checked");
|
|
|
|
var alarmRelated = document.getElementById("alarm-trigger-relation").selectedItem.value;
|
|
|
|
if ((alarmType != "custom" || alarmRelated == "START") && !hasEntryDate) {
|
|
document.getElementById("alarm-start-warning").removeAttribute("hidden");
|
|
return false;
|
|
}
|
|
|
|
if (alarmRelated == "END" && !hasDueDate) {
|
|
document.getElementById("alarm-end-warning").removeAttribute("hidden");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function updateAllDay()
|
|
{
|
|
if (!isEvent(window.calendarItem))
|
|
return;
|
|
|
|
var allDay = getElementValue("event-all-day", "checked");
|
|
setElementValue("event-starttime", allDay, "timepickerdisabled");
|
|
setElementValue("event-endtime", allDay, "timepickerdisabled");
|
|
|
|
updateAccept();
|
|
}
|
|
|
|
|
|
function updateRecurrence()
|
|
{
|
|
var recur = getElementValue("item-recurrence", "checked");
|
|
if (recur) {
|
|
setElementValue("set-recurrence", false, "disabled");
|
|
} else {
|
|
setElementValue("set-recurrence", "true", "disabled");
|
|
}
|
|
|
|
updateAccept();
|
|
}
|
|
|
|
var prevAlarmItem = null;
|
|
function setAlarmFields(alarmItem)
|
|
{
|
|
var alarmLength = alarmItem.getAttribute("length");
|
|
if (alarmLength != "") {
|
|
var alarmUnits = alarmItem.getAttribute("unit");
|
|
var alarmRelation = alarmItem.getAttribute("relation");
|
|
setElementValue("alarm-length-field", alarmLength);
|
|
setElementValue("alarm-length-units", alarmUnits);
|
|
setElementValue("alarm-trigger-relation", alarmRelation);
|
|
}
|
|
}
|
|
function updateAlarm()
|
|
{
|
|
var alarmMenu = document.getElementById("item-alarm");
|
|
var alarmItem = alarmMenu.selectedItem;
|
|
|
|
var alarmItemValue = alarmItem.getAttribute("value");
|
|
switch (alarmItemValue) {
|
|
case "custom":
|
|
/* restore old values if they're around */
|
|
setAlarmFields(alarmItem);
|
|
|
|
document.getElementById("alarm-details").removeAttribute("hidden");
|
|
break;
|
|
default:
|
|
var customItem = document.getElementById("alarm-custom-menuitem");
|
|
if (prevAlarmItem == customItem) {
|
|
customItem.setAttribute("length", getElementValue("alarm-length-field"));
|
|
customItem.setAttribute("unit", getElementValue("alarm-length-units"));
|
|
customItem.setAttribute("relation", getElementValue("alarm-trigger-relation"));
|
|
}
|
|
setAlarmFields(alarmItem);
|
|
|
|
document.getElementById("alarm-details").setAttribute("hidden", true);
|
|
break;
|
|
}
|
|
|
|
prevAlarmItem = alarmItem;
|
|
updateAccept();
|
|
|
|
this.sizeToContent();
|
|
}
|
|
|
|
function editRecurrence()
|
|
{
|
|
var args = new Object();
|
|
args.calendarEvent = window.calendarItem;
|
|
args.recurrenceInfo = window.recurrenceInfo || args.calendarEvent.recurrenceInfo;
|
|
|
|
var kDefaultTimezone = calendarDefaultTimezone();
|
|
if (isEvent(window.calendarItem)) {
|
|
var startDate = jsDateToDateTime(getElementValue("event-starttime")).getInTimezone(kDefaultTimezone);
|
|
if (getElementValue("event-all-day", "checked")) {
|
|
startDate.isDate = true;
|
|
startDate.normalize();
|
|
}
|
|
args.startDate = startDate;
|
|
} else if (isToDo(window.calendarItem)) {
|
|
if (!getElementValue("todo-has-entrydate", "checked")) {
|
|
return;
|
|
}
|
|
args.startDate = jsDateToDateTime(getElementValue("todo-entrydate")).getInTimezone(kDefaultTimezone);
|
|
}
|
|
|
|
var savedWindow = window;
|
|
args.onOk = function(recurrenceInfo) {
|
|
savedWindow.recurrenceInfo = recurrenceInfo;
|
|
};
|
|
|
|
// wait cursor will revert to auto in eventDialog.js loadCalendarEventDialog
|
|
window.setCursor("wait");
|
|
|
|
// open the dialog modally
|
|
openDialog("chrome://calendar/content/calendar-recurrence-dialog.xul", "_blank", "chrome,titlebar,modal", args);
|
|
}
|
|
|
|
|
|
|
|
/* utility functions */
|
|
function setItemProperty(item, propertyName, value)
|
|
{
|
|
switch(propertyName) {
|
|
case "startDate":
|
|
if (value.isDate && !item.startDate.isDate ||
|
|
!value.isDate && item.startDate.isDate ||
|
|
value.timezone != item.startDate.timezone ||
|
|
value.compare(item.startDate) != 0)
|
|
item.startDate = value;
|
|
break;
|
|
case "endDate":
|
|
if (value.isDate && !item.endDate.isDate ||
|
|
!value.isDate && item.endDate.isDate ||
|
|
value.timezone != item.endDate.timezone ||
|
|
value.compare(item.endDate) != 0)
|
|
item.endDate = value;
|
|
break;
|
|
|
|
case "entryDate":
|
|
if (value == item.entryDate)
|
|
break;
|
|
if ((value && !item.entryDate) ||
|
|
(!value && item.entryDate) ||
|
|
(value.timezone != item.entryDate.timezone) ||
|
|
(value.compare(item.entryDate) != 0) ||
|
|
(value.isDate != item.entryDate.isDate))
|
|
item.entryDate = value;
|
|
break;
|
|
case "dueDate":
|
|
if (value == item.dueDate)
|
|
break;
|
|
if ((value && !item.dueDate) ||
|
|
(!value && item.dueDate) ||
|
|
(value.timezone != item.dueDate.timezone) ||
|
|
(value.compare(item.dueDate) != 0) ||
|
|
(value.isDate != item.dueDate.isDate))
|
|
item.dueDate = value;
|
|
break;
|
|
case "isCompleted":
|
|
if (value != item.isCompleted)
|
|
item.isCompleted = value;
|
|
break;
|
|
|
|
case "title":
|
|
if (value != item.title)
|
|
item.title = value;
|
|
break;
|
|
|
|
default:
|
|
if (!value || value == "")
|
|
item.deleteProperty(propertyName);
|
|
else if (item.getProperty(propertyName) != value)
|
|
item.setProperty(propertyName, value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
function toggleDetails() {
|
|
var detailsElements = document.getElementsByAttribute("details", "true");
|
|
var detailsButton = document.getElementById("calendar-event-dialog").getButton("disclosure");
|
|
|
|
if (!detailsElements[0].collapsed) {
|
|
// Hide details
|
|
for each (elem in detailsElements) {
|
|
elem.collapsed = true;
|
|
}
|
|
detailsButton.setAttribute("label", moreLabel);
|
|
this.sizeToContent();
|
|
return;
|
|
}
|
|
|
|
// Display details
|
|
for each (elem in detailsElements) {
|
|
elem.collapsed = false;
|
|
}
|
|
detailsButton.setAttribute("label", lessLabel);
|
|
this.sizeToContent();
|
|
|
|
if (gDetailsShown) {
|
|
// We've already loaded this stuff before, so we're done
|
|
return;
|
|
}
|
|
|
|
loadDetails();
|
|
}
|
|
|
|
function loadDetails() {
|
|
gDetailsShown = true;
|
|
var item = window.calendarItem;
|
|
|
|
/* attendence */
|
|
var attendeeString = "";
|
|
var attendeeListBox = document.getElementById("attendees-listbox");
|
|
|
|
var child;
|
|
while ((child = attendeeListBox.lastChild) && (child.tagName == "listitem")) {
|
|
attendeeListBox.removeChild(child);
|
|
}
|
|
|
|
for each (var attendee in item.getAttendees({})) {
|
|
var listItem = document.createElement("listitem");
|
|
var nameCell = document.createElement("listcell");
|
|
nameCell.setAttribute("label", attendee.id.split("MAILTO:")[1]);
|
|
listItem.appendChild(nameCell);
|
|
|
|
listItem.attendee = attendee;
|
|
attendeeListBox.appendChild(listItem);
|
|
}
|
|
|
|
|
|
/* Status */
|
|
setElementValue("item-url", item.getProperty("URL"));
|
|
setElementValue("item-description", item.getProperty("DESCRIPTION"));
|
|
if (isEvent(item)) {
|
|
setElementValue("event-status", item.getProperty("STATUS"));
|
|
} else {
|
|
setElementValue("todo-status", item.getProperty("STATUS"));
|
|
}
|
|
|
|
/* Task completed date */
|
|
if (item.completedDate) {
|
|
updateToDoStatus(item.status, item.completedDate.jsDate);
|
|
} else {
|
|
updateToDoStatus(item.status);
|
|
}
|
|
|
|
/* Task percent complete */
|
|
if (isToDo(item)) {
|
|
var percentCompleteInteger = 0;
|
|
var percentCompleteProperty = item.getProperty("PERCENT-COMPLETE");
|
|
if (percentCompleteProperty != null) {
|
|
percentCompleteInteger = parseInt(percentCompleteProperty);
|
|
}
|
|
if (percentCompleteInteger < 0) {
|
|
percentCompleteInteger = 0;
|
|
} else if (percentCompleteInteger > 100) {
|
|
percentCompleteInteger = 100;
|
|
}
|
|
setElementValue("percent-complete-textbox", percentCompleteInteger);
|
|
}
|
|
|
|
/* Priority */
|
|
var priorityInteger = parseInt(item.priority);
|
|
if (priorityInteger >= 1 && priorityInteger <= 4) {
|
|
document.getElementById("priority-levels").selectedIndex = 3; // high priority
|
|
} else if (priorityInteger == 5) {
|
|
document.getElementById("priority-levels").selectedIndex = 2; // medium priority
|
|
} else if (priorityInteger >= 6 && priorityInteger <= 9) {
|
|
document.getElementById("priority-levels").selectedIndex = 1; // low priority
|
|
} else {
|
|
document.getElementById("priority-levels").selectedIndex = 0; // not defined
|
|
}
|
|
|
|
/* Privacy */
|
|
switch (item.privacy) {
|
|
case "PUBLIC":
|
|
case "PRIVATE":
|
|
case "CONFIDENTIAL":
|
|
setElementValue("privacy-menulist", item.privacy);
|
|
break;
|
|
case "":
|
|
setElementValue("private-menulist", "PUBLIC");
|
|
break;
|
|
default: // bogus value
|
|
dump("ERROR! Event has invalid privacy string: " + item.privacy + "\n");
|
|
break;
|
|
}
|
|
|
|
/* alarms */
|
|
if (item.alarmOffset) {
|
|
var alarmRelatedStart = (item.alarmRelated == Components.interfaces.calIItemBase.ALARM_RELATED_START);
|
|
if (alarmRelatedStart) {
|
|
setElementValue("alarm-trigger-relation", "START");
|
|
} else {
|
|
setElementValue("alarm-trigger-relation", "END");
|
|
}
|
|
|
|
var offset = item.alarmOffset;
|
|
if (offset.minutes) {
|
|
var minutes = offset.minutes + offset.hours*60 + offset.days*24*60 + offset.weeks*60*24*7;
|
|
// Special cases for the common alarms
|
|
if ((minutes == 15) && alarmRelatedStart) {
|
|
document.getElementById("item-alarm").selectedIndex = 2;
|
|
} else if ((minutes == 30) && alarmRelatedStart) {
|
|
document.getElementById("item-alarm").selectedIndex = 3;
|
|
} else {
|
|
setElementValue("alarm-length-field", minutes);
|
|
setElementValue("alarm-length-units", "minutes");
|
|
setElementValue("item-alarm", "custom");
|
|
}
|
|
} else if (offset.hours) {
|
|
var hours = offset.hours + offset.days*24 + offset.weeks*24*7;
|
|
setElementValue("alarm-length-field", hours);
|
|
setElementValue("alarm-length-units", "hours");
|
|
setElementValue("item-alarm", "custom");
|
|
} else { // days
|
|
var days = offset.days + offset.weeks*7;
|
|
setElementValue("alarm-length-field", days);
|
|
setElementValue("alarm-length-units", "days");
|
|
setElementValue("item-alarm", "custom");
|
|
}
|
|
}
|
|
|
|
// update alarm checkbox/label/settings button
|
|
updateAlarm();
|
|
|
|
updateTaskAlarmWarnings();
|
|
|
|
updateURL(item.getProperty("URL"));
|
|
return;
|
|
}
|
|
|
|
function updateToDoStatus(status, passedInCompletedDate)
|
|
{
|
|
// RFC2445 doesn't support completedDates without the todo's status
|
|
// being "COMPLETED", however twiddling the status menulist shouldn't
|
|
// destroy that information at this point (in case you change status
|
|
// back to COMPLETED). When we go to store this VTODO as .ics the
|
|
// date will get lost.
|
|
|
|
var completedDate;
|
|
if (passedInCompletedDate) {
|
|
completedDate = passedInCompletedDate;
|
|
} else {
|
|
completedDate = null;
|
|
}
|
|
|
|
// remember the original values
|
|
var oldPercentComplete = getElementValue("percent-complete-textbox");
|
|
var oldCompletedDate = getElementValue("completed-date-picker");
|
|
|
|
switch (status) {
|
|
case null:
|
|
case "":
|
|
case "NONE":
|
|
document.getElementById("todo-status").selectedIndex = 0;
|
|
disableElement("percent-complete-textbox");
|
|
disableElement("percent-complete-label");
|
|
break;
|
|
case "CANCELLED":
|
|
document.getElementById("todo-status").selectedIndex = 4;
|
|
disableElement("percent-complete-textbox");
|
|
disableElement("percent-complete-label");
|
|
break;
|
|
case "COMPLETED":
|
|
document.getElementById("todo-status").selectedIndex = 3;
|
|
enableElement("percent-complete-textbox");
|
|
enableElement("percent-complete-label");
|
|
// if there isn't a completedDate, set it to now
|
|
if (!completedDate)
|
|
completedDate = new Date();
|
|
break;
|
|
case "IN-PROCESS":
|
|
document.getElementById("todo-status").selectedIndex = 2;
|
|
disableElement("completed-date-picker");
|
|
enableElement("percent-complete-textbox");
|
|
enableElement("percent-complete-label");
|
|
break;
|
|
case "NEEDS-ACTION":
|
|
document.getElementById("todo-status").selectedIndex = 1;
|
|
enableElement("percent-complete-textbox");
|
|
enableElement("percent-complete-label");
|
|
break;
|
|
}
|
|
|
|
if (status == "COMPLETED") {
|
|
setElementValue("percent-complete-textbox", "100");
|
|
setElementValue("completed-date-picker", completedDate);
|
|
enableElement("completed-date-picker");
|
|
} else {
|
|
if (oldPercentComplete != 100) {
|
|
setElementValue("percent-complete-textbox", oldPercentComplete);
|
|
} else {
|
|
setElementValue("percent-complete-textbox", "");
|
|
}
|
|
setElementValue("completed-date-picker", oldCompletedDate);
|
|
disableElement("completed-date-picker");
|
|
}
|
|
}
|
|
|
|
function updateURL(aValue)
|
|
{
|
|
var button = document.getElementById("load-url-button");
|
|
button.setAttribute("disabled", true)
|
|
|
|
if (!aValue) {
|
|
return;
|
|
}
|
|
|
|
// The user might have just put in 'www.foo.com', correct that here
|
|
if (aValue.indexOf( ":" ) == -1) {
|
|
aValue = "http://" + aValue;
|
|
}
|
|
try {
|
|
makeURL(aValue);
|
|
// If we made it this far, that means it's a valid url
|
|
button.removeAttribute("disabled");
|
|
} catch(ex) {}
|
|
|
|
return;
|
|
}
|
|
|
|
function loadURL()
|
|
{
|
|
var url = getElementValue("item-url");
|
|
|
|
// The user might have just put in 'www.foo.com', correct that here
|
|
if (url.indexOf( ":" ) == -1) {
|
|
url = "http://" + url;
|
|
}
|
|
|
|
launchBrowser(url);
|
|
return;
|
|
}
|
|
|
|
var gOldCatIndex = 0;
|
|
function categorySelect(aValue) {
|
|
if (aValue != "##NEW") {
|
|
gOldCatIndex = document.getElementById("item-categories").selectedIndex;
|
|
return;
|
|
}
|
|
|
|
// Make sure we don't leave 'New..' selected if they hit cancel
|
|
document.getElementById("item-categories").selectedIndex = gOldCatIndex;
|
|
|
|
window.openDialog("chrome://calendar/content/preferences/editCategory.xul",
|
|
"addCategory", "modal,centerscreen,chrome,resizable=no",
|
|
"", null, calGetString("calendar", "addCategory"));
|
|
}
|
|
|
|
// Trick the dialog into thinking we're the categories pane
|
|
var gCategoriesPane = {
|
|
saveCategory: function eventDialog_saveCategory(aName, aColor) {
|
|
//Check to make sure another category doesn't have the same name
|
|
var promptService =
|
|
Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
.getService(Components.interfaces.nsIPromptService);
|
|
var categoriesString = getLocalizedPref("calendar.categories.names");
|
|
var categoriesList = categoriesString.split( "," );
|
|
for each (cat in categoriesList) {
|
|
if (aName.toLowerCase() == cat.toLowerCase()) {
|
|
var repTitle = calGetString("calendar", "categoryReplaceTitle");
|
|
var rep = calGetString("calendar", "categoryReplace");
|
|
if (promptService.confirm(null, repTitle, rep)) {
|
|
var categoryNameFix = aName.toLowerCase().replace(' ','_');
|
|
setPref("calendar.category.color."+categoryNameFix,
|
|
"CHAR", aColor);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (aName.length == 0) {
|
|
promptService.alert(null, null, noBlankCategories);
|
|
return;
|
|
}
|
|
|
|
categoriesList.push(aName);
|
|
categoriesList.sort();
|
|
|
|
setLocalizedPref("calendar.categories.names", categoriesList.join(','));
|
|
|
|
if (aColor) {
|
|
var categoryNameFix = aName.toLowerCase().replace(' ','_');
|
|
setPref("calendar.category.color."+categoryNameFix, "CHAR", aColor);
|
|
}
|
|
var catList = document.getElementById("item-categories");
|
|
var index = categoriesList.indexOf(aName);
|
|
catList.insertItemAt(index, aName, aName);
|
|
catList.selectedIndex = index;
|
|
}
|
|
};
|