mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 12:37:37 +00:00
1844 lines
61 KiB
JavaScript
1844 lines
61 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 OEone Calendar Code, released October 31st, 2001.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* OEone Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2001
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s): Garth Smedley <garths@oeone.com>
|
|
* Mike Potter <mikep@oeone.com>
|
|
* Colin Phillips <colinp@oeone.com>
|
|
* Chris Charabaruk <coldacid@meldstar.com>
|
|
* ArentJan Banck <ajbanck@planet.nl>
|
|
* Mostafa Hosseini <mostafah@oeone.com>
|
|
* Eric Belhaire <belhaire@ief.u-psud.fr>
|
|
*
|
|
* 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 ***** */
|
|
|
|
|
|
|
|
/***** calendar/eventDialog.js
|
|
* AUTHOR
|
|
* Garth Smedley
|
|
* REQUIRED INCLUDES
|
|
* <script type="application/x-javascript" src="chrome://calendar/content/dateUtils.js"/>
|
|
* <script type="application/x-javascript" src="chrome://calendar/content/applicationUtil.js"/>
|
|
* NOTES
|
|
* Code for the calendar's new/edit event dialog.
|
|
*
|
|
* Invoke this dialog to create a new event as follows:
|
|
|
|
var args = new Object();
|
|
args.mode = "new"; // "new" or "edit"
|
|
args.onOk = <function>; // funtion to call when OK is clicked
|
|
args.calendarEvent = calendarEvent; // newly creatd calendar event to be editted
|
|
|
|
openDialog("chrome://calendar/content/eventDialog.xul", "caEditEvent", "chrome,titlebar,modal", args );
|
|
*
|
|
* Invoke this dialog to edit an existing event as follows:
|
|
*
|
|
var args = new Object();
|
|
args.mode = "edit"; // "new" or "edit"
|
|
args.onOk = <function>; // funtion to call when OK is clicked
|
|
args.calendarEvent = calendarEvent; // javascript object containin the event to be editted
|
|
|
|
* When the user clicks OK the onOk function will be called with a calendar event object.
|
|
*
|
|
*
|
|
* IMPLEMENTATION NOTES
|
|
**********
|
|
*/
|
|
|
|
|
|
/*-----------------------------------------------------------------
|
|
* W I N D O W V A R I A B L E S
|
|
*/
|
|
|
|
var gDebugEnabled=true;
|
|
|
|
var gEvent; // event being edited
|
|
var gOnOkFunction; // function to be called when user clicks OK
|
|
|
|
var gDuration = -1; // used to preserve duration when changing event start.
|
|
|
|
const DEFAULT_ALARM_LENGTH = 15; //default number of time units, an alarm goes off before an event
|
|
|
|
var gStartDate = new Date( );
|
|
// Note: gEndDate is *exclusive* end date, so duration = end - start.
|
|
// For all-day(s) events (no times), displayed end date is one day earlier.
|
|
var gEndDate = new Date( ); // for events
|
|
var gDueDate = new Date( ); // for todos
|
|
|
|
/*-----------------------------------------------------------------
|
|
* W I N D O W F U N C T I O N S
|
|
*/
|
|
|
|
/**
|
|
* Called when the dialog is loaded.
|
|
*/
|
|
|
|
function loadCalendarEventDialog()
|
|
{
|
|
// Get arguments, see description at top of file
|
|
var args = window.arguments[0];
|
|
|
|
gOnOkFunction = args.onOk;
|
|
|
|
// XXX I want to get rid of the use of gEvent
|
|
gEvent = args.calendarEvent;
|
|
|
|
var event = args.calendarEvent;
|
|
|
|
// Set up dialog as event or todo
|
|
var componentType;
|
|
if (isEvent(event)) {
|
|
processComponentType("event");
|
|
componentType = "event";
|
|
} else if (isToDo(event)) {
|
|
processComponentType("todo");
|
|
componentType = "todo";
|
|
} else {
|
|
dump("loadCalendarEventDialog: ERROR! Got a bogus event! Not event or todo!\n");
|
|
// close the dialog before we screw anything else up
|
|
return false;
|
|
}
|
|
|
|
debug("-----");
|
|
debug("event: "+event);
|
|
debug("-----");
|
|
debug("event.startDate: "+event.startDate);
|
|
debug("-----");
|
|
|
|
// fill in fields from the event
|
|
switch(componentType) {
|
|
case "event":
|
|
gStartDate = event.startDate.jsDate;
|
|
document.getElementById("start-datetime").value = gStartDate;
|
|
|
|
// only events have end dates. todos have due dates
|
|
gEndDate = event.endDate.jsDate;
|
|
var displayEndDate = new Date(gEndDate);
|
|
/*
|
|
if (event.isAllDay) {
|
|
//displayEndDate == icalEndDate - 1, in the case of allday events
|
|
displayEndDate.setDate(displayEndDate.getDate() - 1);
|
|
}
|
|
*/
|
|
document.getElementById("end-datetime").value = displayEndDate;
|
|
|
|
gDuration = gEndDate.getTime() - gStartDate.getTime(); //in ms
|
|
|
|
// event status fields
|
|
switch(event.status) {
|
|
case "TENTATIVE":
|
|
setFieldValue("event-status-field", "TENTATIVE");
|
|
break;
|
|
case "CONFIRMED":
|
|
setFieldValue("event-status-field", "CONFIRMED");
|
|
break;
|
|
case "CANCELLED":
|
|
setFieldValue("event-status-field", "CANCELLED");
|
|
break;
|
|
}
|
|
|
|
setFieldValue("all-day-event-checkbox", event.isAllDay, "checked");
|
|
break;
|
|
case "todo":
|
|
var hasStart = event.start && event.start.isSet;
|
|
if (hasStart)
|
|
gStartDate = event.startDate.jsDate;
|
|
else
|
|
gStartDate = null;
|
|
|
|
var startPicker = document.getElementById( "start-datetime" );
|
|
startPicker.value = gStartDate;
|
|
startPicker.disabled = !hasStart;
|
|
document.getElementById("start-checkbox").checked = hasStart;
|
|
|
|
var hasDue = event.due && event.due.isSet;
|
|
if (hasDue)
|
|
gDueDate = event.dueDate.jsDate;
|
|
else
|
|
gDueDate = null;
|
|
|
|
var duePicker = document.getElementById( "due-datetime" );
|
|
duePicker.value = gDueDate;
|
|
duePicker.disabled = !hasDue;
|
|
document.getElementById("due-checkbox").checked = hasDue;
|
|
|
|
if (hasStart && hasDue) {
|
|
gDuration = gDueDate.getTime() - gStartDate.getTime(); //in ms
|
|
} else {
|
|
gDuration = 0;
|
|
}
|
|
|
|
// todo status fields
|
|
processToDoStatus(event.status);
|
|
|
|
var completedDatePicker = document.getElementById( "completed-date-picker" );
|
|
if (event.completedDate) {
|
|
var completedDate = event.completedDate.jsDate;
|
|
completedDatePicker.value = completedDate;
|
|
} else {
|
|
completedDatePicker.value = null;
|
|
}
|
|
|
|
if (event.percentComplete && event.status == "IN-PROCESS" ) {
|
|
setFieldValue( "percent-complete-menulist", event.percentComplete );
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
// GENERAL -----------------------------------------------------------
|
|
setFieldValue("title-field", event.title );
|
|
setFieldValue("description-field", event.getProperty("description"));
|
|
setFieldValue("location-field", event.getProperty("location"));
|
|
setFieldValue("uri-field", event.getProperty("url"));
|
|
// only enable "Go" button if there's a url
|
|
processTextboxWithButton( "uri-field", "load-url-button" );
|
|
|
|
|
|
// PRIVACY -----------------------------------------------------------
|
|
switch(event.privacy) {
|
|
case "PUBLIC":
|
|
case "PRIVATE":
|
|
case "CONFIDENTIAL":
|
|
setFieldValue("privacy-menulist", event.privacy);
|
|
break;
|
|
case "":
|
|
setFieldValue("private-menulist", "PUBLIC");
|
|
break;
|
|
default: // bogus value
|
|
dump("loadCalendarEventDialog: ERROR! Event has invalid privacy string: " + event.privacy + "\n");
|
|
break;
|
|
}
|
|
|
|
|
|
// PRIORITY ----------------------------------------------------------
|
|
var priorityInteger = parseInt( event.priority );
|
|
if( priorityInteger == 0 ) {
|
|
menuListSelectItem("priority-levels", "0"); // not defined
|
|
} else if( priorityInteger >= 1 && priorityInteger <= 4 ) {
|
|
menuListSelectItem("priority-levels", "1"); // high priority
|
|
} else if( priorityInteger == 5 ) {
|
|
menuListSelectItem("priority-levels", "5"); // medium priority
|
|
} else if( priorityInteger >= 6 && priorityInteger <= 9 ) {
|
|
menuListSelectItem("priority-levels", "9"); // low priority
|
|
} else {
|
|
dump("loadCalendarEventDialog: ERROR! Event has invalid priority: " + event.priority +"\n");
|
|
}
|
|
|
|
|
|
// ALARMS ------------------------------------------------------------
|
|
if (!event.hasAlarm) {
|
|
menuListSelectItem("alarm-type", "none");
|
|
} else {
|
|
setFieldValue("alarm-length-field", event.getProperty("alarmLength"));
|
|
setFieldValue("alarm-length-units", event.getProperty("alarmUnits"));
|
|
if (componentType == "event" ||
|
|
(componentType == "todo" && !(startPicker.disabled && duePicker.disabled) ) ) {
|
|
// If the event has an alarm email address, assume email alarm type
|
|
var alarmEmailAddress = event.getProperty("alarmEmailAddress");
|
|
if (alarmEmailAddress && alarmEmailAddress != "") {
|
|
menuListSelectItem("alarm-type", "email");
|
|
setFieldValue("alarm-email-field", alarmEmailAddress);
|
|
} else {
|
|
menuListSelectItem("alarm-type", "popup");
|
|
/* XXX lilmatt: finish this by selection between popup and
|
|
popupAndSound by checking pref "calendar.alarms.playsound" */
|
|
}
|
|
var alarmRelated = event.getProperty("alarmRelated");
|
|
if (alarmRelated && alarmRelated != "") {
|
|
// if only one picker is enabled, check that the appropriate related
|
|
// parameter is chosen
|
|
if ( (componentType == "event") ||
|
|
(componentType == "todo" && !startPicker.disabled &&
|
|
duePicker.disabled && alarmRelated == "START") ||
|
|
(componentType == "todo" && startPicker.disabled &&
|
|
!duePicker.disabled && alarmRelated == "END") ||
|
|
(componentType == "todo" && !startPicker.disabled && !duePicker.disabled) )
|
|
{
|
|
setFieldValue("alarm-trigger-relation", alarmRelated);
|
|
} else {
|
|
dump("loadCalendarEventDialog: ERROR! alarmRelated: " +
|
|
alarmRelated + " is invalid! (trying to set " +
|
|
"START/END when the necessary datepicker isn't " +
|
|
"enabled?)\n");
|
|
}
|
|
}
|
|
}
|
|
// hide/show fields and widgets for alarm type
|
|
processAlarmType();
|
|
}
|
|
|
|
|
|
// RECURRENCE --------------------------------------------------------
|
|
// Set up widgets so they're not empty. Event values will override.
|
|
document.getElementById("repeat-end-date-picker").value = new Date();
|
|
document.getElementById("exceptions-date-picker").value = new Date();
|
|
|
|
if (event.recurrenceInfo) {
|
|
// we can only display at most one rule and one set of exceptions;
|
|
// nothing else.
|
|
var theRule = null;
|
|
var theExceptions = Array();
|
|
|
|
var ritems = event.recurrenceInfo.getRecurrenceItems({});
|
|
for (i in ritems) {
|
|
if (ritems[i] instanceof calIRecurrenceRule) {
|
|
if (theRule) {
|
|
dump ("eventDialog.js: Already found a calIRecurrenceRule, we can't handle multiple ones!\n");
|
|
} else {
|
|
theRule = ritems[i].QueryInterface(calIRecurrenceRule);
|
|
if (theRule.isNegative) {
|
|
dump ("eventDialog.js: Found an EXRULE, we can't handle this!\n");
|
|
theRule = null;
|
|
}
|
|
}
|
|
} else if (ritems[i] instanceof calIRecurrenceDate) {
|
|
var exc = ritems[i].QueryInterface(calIRecurrenceDate);
|
|
if (exc.isNegative) {
|
|
theExceptions.push(exc);
|
|
} else {
|
|
dump ("eventDialog.js: found a calIRecurrenceDate that wasn't an exception!\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (theRule) {
|
|
setFieldValue("repeat-checkbox", true, "checked");
|
|
setFieldValue("repeat-length-field", theRule.interval);
|
|
menuListSelectItem("repeat-length-units", theRule.type);
|
|
|
|
switch(theRule.type) {
|
|
case "WEEKLY":
|
|
var recurWeekdays = theRule.getComponent("BYDAY", {});
|
|
for (i = 0; i < 7; i++ ) {
|
|
setFieldValue( "advanced-repeat-week-"+i, false, "checked" );
|
|
setFieldValue( "advanced-repeat-week-"+i, false, "today" );
|
|
for (var j = 0; j < recurWeekdays.length; j++) {
|
|
//libical day #s are one more than ours
|
|
if ((i + 1) == recurWeekdays[j]) {
|
|
setFieldValue( "advanced-repeat-week-"+i, true, "checked" );
|
|
}
|
|
}
|
|
}
|
|
|
|
//get day number for event's start date, and check and disable it
|
|
var dayNumber = document.getElementById( "start-datetime" ).value.getDay();
|
|
setFieldValue( "advanced-repeat-week-"+dayNumber, true, "checked" );
|
|
setFieldValue( "advanced-repeat-week-"+dayNumber, true, "disabled" );
|
|
setFieldValue( "advanced-repeat-week-"+dayNumber, true, "today" );
|
|
break;
|
|
}
|
|
|
|
if (theRule.count == -1) {
|
|
setFieldValue("repeat-forever-radio", true, "selected");
|
|
} else {
|
|
if (theRule.isByCount) {
|
|
setFieldValue("repeat-numberoftimes-radio", true, "selected");
|
|
setFieldValue("repeat-numberoftimes-textbox", theRule.count);
|
|
} else {
|
|
setFieldValue("repeat-until-radio", true, "selected" );
|
|
document.getElementById("repeat-end-date-picker").value = theRule.endDate.jsDate;
|
|
}
|
|
}
|
|
} else {
|
|
menuListSelectItem("repeat-length-units", "DAILY");
|
|
}
|
|
updateRepeatUnitExtensions()
|
|
|
|
if (theExceptions.length > 0) {
|
|
for (i in theExceptions) {
|
|
var date = theExceptions[i].date.jsDate;
|
|
addException(date);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Old crap:
|
|
setFieldValue("advanced-repeat-dayofmonth", (gEvent.recurWeekNumber == 0 || gEvent.recurWeekNumber == undefined), "selected");
|
|
setFieldValue("advanced-repeat-dayofweek", (gEvent.recurWeekNumber > 0 && gEvent.recurWeekNumber != 5), "selected");
|
|
setFieldValue("advanced-repeat-dayofweek-last", (gEvent.recurWeekNumber == 5), "selected");
|
|
*/
|
|
|
|
|
|
// INVITEES ----------------------------------------------------------
|
|
var inviteEmailAddress = event.getProperty("inviteEmailAddress");
|
|
if (inviteEmailAddress != undefined && inviteEmailAddress != "") {
|
|
setFieldValue("invite-checkbox", true, "checked");
|
|
setFieldValue("invite-email-field", inviteEmailAddress);
|
|
} else {
|
|
setFieldValue("invite-checkbox", false, "checked");
|
|
}
|
|
processInviteCheckbox();
|
|
|
|
// handle attendees
|
|
var attendeeList = event.getAttendees({});
|
|
for (var i = 0; i < attendeeList.length; i++) {
|
|
var attendee = attendeeList[i];
|
|
addAttendee(attendee.id);
|
|
}
|
|
|
|
|
|
// ATTACHMENTS -------------------------------------------------------
|
|
/* XXX this could work when attachments are supported by calItemBase
|
|
var count = event.attachments.length;
|
|
for(var i = 0; i < count; i++) {
|
|
var thisAttachment = event.attachments.queryElementAt(i, Components.interfaces.calIAttachment);
|
|
addAttachment(thisAttachment);
|
|
}
|
|
*/
|
|
|
|
|
|
// CATEGORIES --------------------------------------------------------
|
|
// Load categories from preferences
|
|
var categoriesString = opener.GetUnicharPref(opener.gCalendarWindow.calendarPreferences.calendarPref, "categories.names", getDefaultCategories());
|
|
var categoriesList = categoriesString.split( "," );
|
|
|
|
// insert the category already in the task so it doesn't get lost
|
|
var categories = event.getProperty("categories");
|
|
if (categories) {
|
|
if (categoriesString.indexOf(categories) == -1)
|
|
categoriesList[categoriesList.length] = categories;
|
|
}
|
|
categoriesList.sort();
|
|
|
|
var oldMenulist = document.getElementById( "categories-menulist-menupopup" );
|
|
while( oldMenulist.hasChildNodes() )
|
|
oldMenulist.removeChild( oldMenulist.lastChild );
|
|
for (i = 0; i < categoriesList.length ; i++) {
|
|
document.getElementById( "categories-field" ).appendItem(categoriesList[i], categoriesList[i]);
|
|
}
|
|
if (categories) {
|
|
menuListSelectItem("categories-field", categories );
|
|
} else {
|
|
document.getElementById( "categories-field" ).selectedIndex = -1;
|
|
}
|
|
|
|
/* XXX
|
|
// Server stuff
|
|
document.getElementById( "server-menulist-menupopup" ).database.AddDataSource( opener.gCalendarWindow.calendarManager.rdf.getDatasource() );
|
|
document.getElementById( "server-menulist-menupopup" ).builder.rebuild();
|
|
|
|
if (args.mode == "new") {
|
|
if ("server" in args)
|
|
setFieldValue( "server-field", args.server );
|
|
else
|
|
document.getElementById( "server-field" ).selectedIndex = 1;
|
|
} else {
|
|
if (gEvent.parent)
|
|
setFieldValue( "server-field", gEvent.parent.server );
|
|
else
|
|
document.getElementById( "server-field" ).selectedIndex = 1;
|
|
|
|
//for now you can't edit which file the event is in.
|
|
setFieldValue( "server-field", "true", "disabled" );
|
|
setFieldValue( "server-field-label", "true", "disabled" );
|
|
}
|
|
*/
|
|
|
|
|
|
// update enabling and disabling
|
|
updateRepeatItemEnabled();
|
|
updateStartEndItemEnabled();
|
|
|
|
updateAddExceptionButton();
|
|
|
|
//set the advanced weekly repeating stuff
|
|
|
|
// enable/disable subordinate buttons of textboxes
|
|
/* these are listboxes, not textboxes
|
|
processTextboxWithButton( "exception-dates-listbox", "delete-exception-button" );
|
|
processTextboxWithButton( "invite-email-field", "invite-email-button" ); */
|
|
|
|
// start focus on title
|
|
var firstFocus = document.getElementById("title-field");
|
|
firstFocus.focus();
|
|
|
|
// revert cursor from "wait" set in calendar.js editEvent, editNewEvent
|
|
opener.setCursor( "auto" );
|
|
|
|
self.focus();
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when the OK button is clicked.
|
|
*/
|
|
|
|
function onOKCommand()
|
|
{
|
|
var event = gEvent;
|
|
|
|
// if this event isn't mutable, we need to clone it like a sheep
|
|
var originalEvent = event;
|
|
// get values from the form and put them into the event
|
|
|
|
var componentType;
|
|
|
|
// calIEvent properties
|
|
if (isEvent(event)) {
|
|
componentType = "event";
|
|
if (!event.isMutable) // I will cut vlad for making me do this QI
|
|
event = originalEvent.clone().QueryInterface(Components.interfaces.calIEvent);
|
|
|
|
event.startDate.jsDate = gStartDate;
|
|
event.endDate.jsDate = gEndDate;
|
|
event.isAllDay = getFieldValue("all-day-event-checkbox", "checked");
|
|
event.status = getFieldValue("event-status-field");
|
|
} else if (isToDo(event)) {
|
|
componentType = "todo";
|
|
if (!event.isMutable) // I will cut vlad for making me do this QI
|
|
event = originalEvent.clone().QueryInterface(Components.interfaces.calITodo);
|
|
|
|
dump ("this todo is: " + event + "\n");
|
|
if (gStartDate) {
|
|
event.entryDate = gStartDate;
|
|
} else {
|
|
event.entryDate.reset();
|
|
}
|
|
|
|
if (gDueDate) {
|
|
event.dueDate = gDueDate;
|
|
} else {
|
|
event.dueDate.reset();
|
|
}
|
|
event.status = getFieldValue("todo-status-field");
|
|
event.percentComplete = getFieldValue("percent-complete-menulist");
|
|
}
|
|
|
|
// XXX should do an idiot check here to see if duration is negative
|
|
|
|
// calIItemBase properties
|
|
event.title = getFieldValue("title-field");
|
|
event.priority = getFieldValue("priority-levels");
|
|
|
|
// other properties
|
|
event.setProperty("categories", getFieldValue("categories-field"));
|
|
event.setProperty("description", getFieldValue("description-field"));
|
|
event.setProperty("location", getFieldValue("location-field"));
|
|
event.setProperty("url", getFieldValue("uri-field"));
|
|
|
|
|
|
// PRIVACY -----------------------------------------------------------
|
|
var privacyValue = getFieldValue( "privacy-menulist" );
|
|
switch(privacyValue) {
|
|
case "PUBLIC":
|
|
case "PRIVATE":
|
|
case "CONFIDENTIAL":
|
|
event.privacy = privacyValue;
|
|
break;
|
|
default: // bogus value
|
|
dump( "onOKCommand: ERROR! Event has invalid privacy-menulist value: " +
|
|
privacyValue + "\n" );
|
|
break;
|
|
}
|
|
|
|
|
|
// ALARMS ------------------------------------------------------------
|
|
var alarmType = getFieldValue( "alarm-type" );
|
|
if (alarmType != "" && alarmType != "none") {
|
|
event.hasAlarm = true;
|
|
event.setProperty("alarmLength", getFieldValue("alarm-length-field"));
|
|
event.setProperty("alarmUnits", getFieldValue("alarm-length-units"));
|
|
event.setProperty("alarmRelated", getFieldValue("alarm-trigger-relation"));
|
|
//event.alarmTime = ...
|
|
}
|
|
if (alarmType == "email" )
|
|
event.setProperty("alarmEmailAddress", getFieldValue("alarm-email-field"));
|
|
else
|
|
event.deleteProperty("alarmEmailAddress");
|
|
|
|
|
|
// RECURRENCE --------------------------------------------------------
|
|
event.recurrenceInfo = null;
|
|
|
|
if (getFieldValue("repeat-checkbox", "checked")) {
|
|
var recurrenceInfo = createRecurrenceInfo();
|
|
debug("** recurrenceInfo: " + recurrenceInfo );
|
|
recurrenceInfo.initialize(event);
|
|
|
|
var recRule = new calRecurrenceRule();
|
|
|
|
if (getFieldValue("repeat-forever-radio", "selected")) {
|
|
recRule.count = -1;
|
|
} else if (getFieldValue("repeat-numberoftimes-radio", "selected")) {
|
|
recRule.count = Math.max(1, getFieldValue("repeat-numberoftimes-textbox"));
|
|
} else if (getFieldValue("repeat-until-radio", "selected")) {
|
|
var recurEndDate = document.getElementById("repeat-end-date-picker").value;
|
|
recRule.endDate = jsDateToDateTime(recurEndDate);
|
|
}
|
|
|
|
// don't allow for a null interval here; js
|
|
// silently converts that to 0, which confuses
|
|
// libical.
|
|
var recurInterval = getFieldValue("repeat-length-field");
|
|
if (recurInterval == null)
|
|
recurInterval = 1;
|
|
recRule.interval = recurInterval;
|
|
recRule.type = getFieldValue("repeat-length-units");
|
|
|
|
// XXX need to do extra work for weeks here and for months incase
|
|
// extra things are checked
|
|
|
|
|
|
switch(recRule.type) {
|
|
case "WEEKLY":
|
|
var checkedState;
|
|
var byDay = new Array();
|
|
for (var i = 0; i < 7; i++) {
|
|
checkedState = getFieldValue( "advanced-repeat-week-"+i, "checked" );
|
|
if (checkedState) {
|
|
byDay.push(i+1);
|
|
}
|
|
}
|
|
recRule.setComponent("BYDAY", byDay.length, byDay);
|
|
break;
|
|
}
|
|
|
|
recurrenceInfo.appendRecurrenceItem(recRule);
|
|
|
|
// Exceptions
|
|
var listbox = document.getElementById("exception-dates-listbox");
|
|
|
|
var exceptionArray = new Array();
|
|
for (i = 0; i < listbox.childNodes.length; i++) {
|
|
dump ("valuestr '" + listbox.childNodes[i].value + "'\n");
|
|
var dateObj = new Date(parseInt(listbox.childNodes[i].value));
|
|
var dt = jsDateToDateTime(dateObj);
|
|
dt.isDate = true;
|
|
|
|
var dateitem = new calRecurrenceDate();
|
|
dateitem.isNegative = true;
|
|
dateitem.date = dt;
|
|
recurrenceInfo.appendRecurrenceItem(dateitem);
|
|
}
|
|
|
|
// Finally, set the recurrenceInfo
|
|
event.recurrenceInfo = recurrenceInfo;
|
|
}
|
|
debug("RECURRENCE INFO ON EVENT: " + event.recurrenceInfo );
|
|
|
|
|
|
// INVITEES ----------------------------------------------------------
|
|
event.removeAllAttendees();
|
|
var attendeeList = document.getElementById("bucketBody").getElementsByTagName("treecell");
|
|
for (i = 0; i < attendeeList.length; i++) {
|
|
label = attendeeList[i].getAttribute("label");
|
|
attendee = createAttendee();
|
|
attendee.id = label;
|
|
event.addAttendee(attendee);
|
|
}
|
|
|
|
if (getFieldValue("invite-checkbox", "checked"))
|
|
event.setProperty("inviteEmailAddress", getFieldValue("invite-email-field"));
|
|
else
|
|
event.deleteProperty("inviteEmailAddress");
|
|
|
|
|
|
// ATTACHMENTS -------------------------------------------------------
|
|
/* XXX this could will work when attachments are supported by calItemBase
|
|
var attachmentListbox = documentgetElementById("attachmentBucket");
|
|
|
|
var attachments = event.attachments.QueryInterface(Components.interfaces.nsIMutableArray);
|
|
|
|
attachments.clear();
|
|
for (i = 0; i < attachmentListbox.childNodes.length; i++) {
|
|
attachment = Components.classes["@mozilla.org/calendar/attachment;1"].createInstance(Components.interfaces.calIAttachment);
|
|
attachment.url = makeURL(attachmentListbox.childNodes[i].getAttribute("label"));
|
|
attachments.appendElement(attachment);
|
|
}
|
|
*/
|
|
|
|
|
|
var Server = getFieldValue( "server-field" );
|
|
|
|
// :TODO: REALLY only do this if the alarm or start settings change.?
|
|
//if the end time is later than the start time... alert the user using text from the dtd.
|
|
// call caller's on OK function
|
|
|
|
gOnOkFunction(event, Server, originalEvent);
|
|
|
|
// tell standard dialog stuff to close the dialog
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compare dateA with dateB ignoring time of day of each date object.
|
|
* Comparison based on year, month, and day, ignoring rest.
|
|
* Returns
|
|
* -1 if dateA < dateB (ignoring time of day)
|
|
* 0 if dateA == dateB (ignoring time of day)
|
|
* +1 if dateA > dateB (ignoring time of day)
|
|
*/
|
|
|
|
function compareIgnoringTimeOfDay(dateA, dateB)
|
|
{
|
|
if (dateA.getFullYear() == dateB.getFullYear() &&
|
|
dateA.getMonth() == dateB.getMonth() &&
|
|
dateA.getDate() == dateB.getDate() ) {
|
|
return 0;
|
|
} else if (dateA < dateB) {
|
|
return -1;
|
|
} else if (dateA > dateB) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Check that end date is not before start date, and update warning message
|
|
* Return true if problem found.
|
|
*/
|
|
|
|
function checkSetTimeDate()
|
|
{
|
|
var dateComparison = compareIgnoringTimeOfDay(gEndDate, gStartDate);
|
|
|
|
if (dateComparison < 0 ||
|
|
(dateComparison == 0 && getFieldValue( "all-day-event-checkbox",
|
|
"checked" )))
|
|
{
|
|
// end before start, or all day event and end date is not exclusive.
|
|
setDateError(true);
|
|
//showElement("end-date-warning");
|
|
setTimeError(false);
|
|
//hideElement("end-time-warning");
|
|
return false;
|
|
} else if (dateComparison == 0) {
|
|
setDateError(false);
|
|
//hideElement("end-date-warning");
|
|
// start & end date same, so compare entire time (ms since 1970)
|
|
var isBadEndTime = gEndDate.getTime() < gStartDate.getTime();
|
|
setTimeError(isBadEndTime);
|
|
return !isBadEndTime;
|
|
} else {
|
|
setDateError(false);
|
|
//hideElement("end-date-warning");
|
|
setTimeError(false);
|
|
//hideElement("end-time-warning");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Check that the recurrence end date is after the end date of the event.
|
|
* Unlike the time/date versions this one sets the error message too as it
|
|
* doesn't depend on the outcome of any of the other tests
|
|
*/
|
|
|
|
function checkSetRecur()
|
|
{
|
|
var recur = getFieldValue( "repeat-checkbox", "checked" );
|
|
var recurUntil = getFieldValue( "repeat-until-radio", "selected" );
|
|
var recurUntilDate = document.getElementById("repeat-end-date-picker").value;
|
|
|
|
var untilDateIsBeforeEndDate =
|
|
( recur && recurUntil &&
|
|
// until date is less than end date if total milliseconds time is less
|
|
recurUntilDate.getTime() < gEndDate.getTime() &&
|
|
// if on same day, ignore time of day for now
|
|
// (may change in future for repeats within a day, such as hourly)
|
|
!( recurUntilDate.getFullYear() == gEndDate.getFullYear() &&
|
|
recurUntilDate.getMonth() == gEndDate.getMonth() &&
|
|
recurUntilDate.getDate() == gEndDate.getDate() ));
|
|
setRecurError(untilDateIsBeforeEndDate);
|
|
return(!untilDateIsBeforeEndDate);
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when the start or due datetime checkbox is clicked.
|
|
* Enables/disables corresponding datetime picker and alarm relation.
|
|
*/
|
|
|
|
function onDateTimeCheckbox(checkbox, pickerId)
|
|
{
|
|
var picker = document.getElementById( pickerId );
|
|
picker.disabled = !checkbox.checked;
|
|
|
|
processAlarmType();
|
|
updateOKButton();
|
|
}
|
|
|
|
|
|
function setRecurError(state)
|
|
{
|
|
document.getElementById("repeat-time-warning" ).setAttribute( "hidden", !state);
|
|
}
|
|
|
|
|
|
function setDateError(state)
|
|
{
|
|
document.getElementById( "end-date-warning" ).setAttribute( "hidden", !state );
|
|
}
|
|
|
|
|
|
function setTimeError(state)
|
|
{
|
|
document.getElementById( "end-time-warning" ).setAttribute( "hidden", !state );
|
|
}
|
|
|
|
|
|
/*
|
|
* Check that the due date is after the start date, if they are the same day
|
|
* then the checkDueTime function should catch the problem (if there is one).
|
|
*/
|
|
|
|
function checkDueDate()
|
|
{
|
|
var startDate = document.getElementById( "start-datetime" ).value;
|
|
var dueDate = document.getElementById( "due-datetime" ).value;
|
|
compareIgnoringTimeOfDay(dueDate, startDate);
|
|
}
|
|
|
|
|
|
/*
|
|
* Check that start datetime <= due datetime if both exist.
|
|
* Provide separate error message for startDate > dueDate or
|
|
* startDate == dueDate && startTime > dueTime.
|
|
*/
|
|
|
|
function checkDueSetTimeDate()
|
|
{
|
|
var startCheckBox = document.getElementById( "start-checkbox" );
|
|
var dueCheckBox = document.getElementById( "due-checkbox" );
|
|
if (startCheckBox.checked && dueCheckBox.checked) {
|
|
var CheckDueDate = checkDueDate();
|
|
|
|
if ( CheckDueDate < 0 ) {
|
|
// due before start
|
|
setDueDateError(true);
|
|
setDueTimeError(false);
|
|
return false;
|
|
} else if ( CheckDueDate == 0 ) {
|
|
setDueDateError(false);
|
|
// start & due same
|
|
var CheckDueTime = checkDueTime();
|
|
setDueTimeError(CheckDueTime);
|
|
return !CheckDueTime;
|
|
}
|
|
}
|
|
setDueDateError(false);
|
|
setDueTimeError(false);
|
|
return true;
|
|
}
|
|
|
|
|
|
function setDueDateError(state)
|
|
{
|
|
document.getElementById( "due-date-warning" ).setAttribute( "hidden", !state );
|
|
}
|
|
|
|
|
|
function setDueTimeError(state)
|
|
{
|
|
document.getElementById( "due-time-warning" ).setAttribute( "hidden", !state );
|
|
}
|
|
|
|
|
|
function setOkButton(state)
|
|
{
|
|
if (state == false)
|
|
document.getElementById( "calendar-new-component-window" ).getButton( "accept" ).setAttribute( "disabled", true );
|
|
else
|
|
document.getElementById( "calendar-new-component-window" ).getButton( "accept" ).removeAttribute( "disabled" );
|
|
}
|
|
|
|
|
|
function updateOKButton()
|
|
{
|
|
var checkRecur = checkSetRecur();
|
|
var checkTimeDate = checkSetTimeDate();
|
|
setOkButton(checkRecur && checkTimeDate);
|
|
//this.sizeToContent();
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when a datepicker is finished, and a date was picked.
|
|
*/
|
|
|
|
function onDateTimePick( dateTimePicker )
|
|
{
|
|
var pickedDateTime = new Date( dateTimePicker.value );
|
|
|
|
if( dateTimePicker.id == "end-datetime" ) {
|
|
if( getFieldValue( "all-day-event-checkbox", "checked" ) ) {
|
|
//display enddate == ical enddate - 1 (for allday events)
|
|
pickedDateTime.setDate( pickedDateTime.getDate() + 1 );
|
|
}
|
|
gEndDate = pickedDateTime;
|
|
// save the duration
|
|
gDuration = gEndDate.getTime() - gStartDate.getTime();
|
|
|
|
updateOKButton();
|
|
return;
|
|
}
|
|
|
|
if( dateTimePicker.id == "start-datetime" ) {
|
|
gStartDate = pickedDateTime;
|
|
// preserve the previous duration by changing end
|
|
gEndDate.setTime( gStartDate.getTime() + gDuration );
|
|
|
|
var displayEndDate = new Date(gEndDate)
|
|
if( getFieldValue( "all-day-event-checkbox", "checked" ) ) {
|
|
//display enddate == ical enddate - 1 (for allday events)
|
|
displayEndDate.setDate( displayEndDate.getDate() - 1 );
|
|
}
|
|
document.getElementById( "end-datetime" ).value = displayEndDate;
|
|
}
|
|
|
|
var now = new Date();
|
|
|
|
// change the end date of recurring events to today,
|
|
// if the new date is after today and repeat is not checked.
|
|
if ( pickedDateTime.getTime() > now.getTime() &&
|
|
!getFieldValue( "repeat-checkbox", "checked" ) )
|
|
{
|
|
document.getElementById( "repeat-end-date-picker" ).value = pickedDateTime;
|
|
}
|
|
updateAdvancedWeekRepeat();
|
|
updateAdvancedRepeatDayOfMonth();
|
|
updateAddExceptionButton();
|
|
updateOKButton();
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when the repeat checkbox is clicked.
|
|
*/
|
|
|
|
function commandRepeat()
|
|
{
|
|
updateRepeatItemEnabled();
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when the until radio is clicked.
|
|
*/
|
|
|
|
function commandUntil()
|
|
{
|
|
updateUntilItemEnabled();
|
|
|
|
updateOKButton();
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when the all day checkbox is clicked.
|
|
*/
|
|
|
|
function commandAllDay()
|
|
{
|
|
//user enddate == ical enddate - 1 (for allday events)
|
|
if( getFieldValue( "all-day-event-checkbox", "checked" ) ) {
|
|
gEndDate.setDate( gEndDate.getDate() + 1 );
|
|
} else {
|
|
gEndDate.setDate( gEndDate.getDate() - 1 );
|
|
}
|
|
// update the duration
|
|
gDuration = gEndDate.getTime() - gStartDate.getTime();
|
|
|
|
updateStartEndItemEnabled();
|
|
updateOKButton();
|
|
}
|
|
|
|
|
|
/*
|
|
* Called when the invite checkbox is clicked.
|
|
*/
|
|
|
|
function processInviteCheckbox()
|
|
{
|
|
processEnableCheckbox( "invite-checkbox" , "invite-email-field" );
|
|
}
|
|
|
|
|
|
/*
|
|
* Enable/Disable Repeat items
|
|
*/
|
|
|
|
function updateRepeatItemEnabled()
|
|
{
|
|
var repeatCheckBox = document.getElementById( "repeat-checkbox" );
|
|
var repeatDisableList = document.getElementsByAttribute( "disable-controller", "repeat" );
|
|
|
|
if( repeatCheckBox.checked ) {
|
|
for( var i = 0; i < repeatDisableList.length; ++i ) {
|
|
if( repeatDisableList[i].getAttribute( "today" ) != "true" )
|
|
repeatDisableList[i].removeAttribute( "disabled" );
|
|
}
|
|
} else {
|
|
for( var j = 0; j < repeatDisableList.length; ++j )
|
|
repeatDisableList[j].setAttribute( "disabled", "true" );
|
|
}
|
|
|
|
// udpate plural/singular
|
|
updateRepeatPlural();
|
|
|
|
updateAlarmPlural();
|
|
|
|
// update until items whenever repeat changes
|
|
updateUntilItemEnabled();
|
|
|
|
// extra interface depending on units
|
|
updateRepeatUnitExtensions();
|
|
}
|
|
|
|
|
|
/*
|
|
* Update plural singular menu items
|
|
*/
|
|
|
|
function updateRepeatPlural()
|
|
{
|
|
updateMenuLabels( "repeat-length-field", "repeat-length-units" );
|
|
}
|
|
|
|
|
|
function updateAlarmPlural()
|
|
{
|
|
updateMenuLabels( "alarm-length-field", "alarm-length-units" );
|
|
}
|
|
|
|
|
|
/*
|
|
* Enable/Disable Until items
|
|
*/
|
|
|
|
function updateUntilItemEnabled()
|
|
{
|
|
var repeatCheckBox = document.getElementById("repeat-checkbox");
|
|
var numberRadio = document.getElementById("repeat-numberoftimes-radio");
|
|
var untilRadio = document.getElementById("repeat-until-radio");
|
|
|
|
if( repeatCheckBox.checked && numberRadio.selected )
|
|
enableElement("repeat-numberoftimes-textbox");
|
|
else
|
|
disableElement("repeat-numberoftimes-textbox");
|
|
|
|
if( repeatCheckBox.checked && untilRadio.selected )
|
|
enableElement("repeat-end-date-picker");
|
|
else
|
|
disableElement("repeat-end-date-picker");
|
|
}
|
|
|
|
|
|
function updateRepeatUnitExtensions()
|
|
{
|
|
var repeatMenu = document.getElementById( "repeat-length-units" );
|
|
if( repeatMenu.selectedItem ) {
|
|
switch( repeatMenu.selectedItem.value ) {
|
|
case "DAILY":
|
|
hideElement("repeat-extensions-week");
|
|
hideElement("repeat-extensions-month");
|
|
break;
|
|
case "WEEKLY":
|
|
showElement("repeat-extensions-week");
|
|
hideElement("repeat-extensions-month");
|
|
updateAdvancedWeekRepeat();
|
|
break;
|
|
case "MONTHLY":
|
|
hideElement("repeat-extensions-week");
|
|
showElement("repeat-extensions-month");
|
|
//the following line causes resize problems after turning on repeating events
|
|
updateAdvancedRepeatDayOfMonth();
|
|
break;
|
|
case "YEARLY":
|
|
hideElement("repeat-extensions-week");
|
|
hideElement("repeat-extensions-month");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Enable/Disable Start/End items
|
|
*/
|
|
|
|
function updateStartEndItemEnabled()
|
|
{
|
|
var allDayCheckBox = document.getElementById( "all-day-event-checkbox" );
|
|
var editTimeDisabled = allDayCheckBox.checked;
|
|
|
|
var startTimePicker = document.getElementById( "start-datetime" );
|
|
var endTimePicker = document.getElementById( "end-datetime" );
|
|
|
|
startTimePicker.timepickerdisabled = editTimeDisabled;
|
|
endTimePicker.timepickerdisabled = editTimeDisabled;
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle key down in repeat field
|
|
*/
|
|
|
|
function repeatLengthKeyDown( repeatField )
|
|
{
|
|
updateRepeatPlural();
|
|
}
|
|
|
|
|
|
/*
|
|
* Handle key down in alarm field
|
|
*/
|
|
|
|
function alarmLengthKeyDown( repeatField )
|
|
{
|
|
updateAlarmPlural();
|
|
}
|
|
|
|
|
|
function repeatUnitCommand( repeatMenu )
|
|
{
|
|
updateRepeatUnitExtensions();
|
|
}
|
|
|
|
|
|
/*
|
|
* function to set the menu items text
|
|
*/
|
|
|
|
function updateAdvancedWeekRepeat()
|
|
{
|
|
//get the day number for today.
|
|
var dayNumber = document.getElementById( "start-datetime" ).value.getDay();
|
|
|
|
//uncheck them all if the repeat checkbox is checked
|
|
var repeatCheckBox = document.getElementById( "repeat-checkbox" );
|
|
|
|
if( repeatCheckBox.checked ) {
|
|
//uncheck them all
|
|
for( var i = 0; i < 7; i++ ) {
|
|
setFieldValue( "advanced-repeat-week-"+i, false, "disabled" );
|
|
setFieldValue( "advanced-repeat-week-"+i, false, "today" );
|
|
}
|
|
}
|
|
|
|
if( !repeatCheckBox.checked ) {
|
|
for( i = 0; i < 7; i++ ) {
|
|
setFieldValue( "advanced-repeat-week-"+i, false, "checked" );
|
|
}
|
|
}
|
|
|
|
setFieldValue( "advanced-repeat-week-"+dayNumber, "true", "checked" );
|
|
setFieldValue( "advanced-repeat-week-"+dayNumber, "true", "disabled" );
|
|
setFieldValue( "advanced-repeat-week-"+dayNumber, "true", "today" );
|
|
}
|
|
|
|
|
|
/*
|
|
* function to set the menu items text
|
|
*/
|
|
|
|
function updateAdvancedRepeatDayOfMonth()
|
|
{
|
|
//get the day number for today.
|
|
var dayNumber = document.getElementById( "start-datetime" ).value.getDate();
|
|
var dayExtension = getDayExtension( dayNumber );
|
|
var weekNumber = getWeekNumberOfMonth();
|
|
var calStrings = document.getElementById("bundle_calendar");
|
|
var weekNumberText = getWeekNumberText( weekNumber );
|
|
var dayOfWeekText = getDayOfWeek( dayNumber );
|
|
var ofTheMonthText = document.getElementById( "ofthemonth-text" ).getAttribute( "value" );
|
|
var lastText = document.getElementById( "last-text" ).getAttribute( "value" );
|
|
var onTheText = document.getElementById( "onthe-text" ).getAttribute( "value" );
|
|
var dayNumberWithOrdinal = dayNumber + dayExtension;
|
|
var repeatSentence = calStrings.getFormattedString( "weekDayMonthLabel", [onTheText, dayNumberWithOrdinal, ofTheMonthText] );
|
|
|
|
document.getElementById( "advanced-repeat-dayofmonth" ).setAttribute( "label", repeatSentence );
|
|
|
|
if( weekNumber == 4 && isLastDayOfWeekOfMonth() ) {
|
|
document.getElementById( "advanced-repeat-dayofweek" ).setAttribute( "label", calStrings.getFormattedString( "weekDayMonthLabel", [weekNumberText, dayOfWeekText, ofTheMonthText] ) );
|
|
showElement("advanced-repeat-dayofweek-last");
|
|
document.getElementById( "advanced-repeat-dayofweek-last" ).setAttribute( "label", calStrings.getFormattedString( "weekDayMonthLabel", [lastText, dayOfWeekText, ofTheMonthText] ) );
|
|
} else if( weekNumber == 4 && !isLastDayOfWeekOfMonth() ) {
|
|
document.getElementById( "advanced-repeat-dayofweek" ).setAttribute( "label", calStrings.getFormattedString( "weekDayMonthLabel", [weekNumberText, dayOfWeekText, ofTheMonthText] ) );
|
|
hideElement("advanced-repeat-dayofweek-last");
|
|
} else {
|
|
document.getElementById( "advanced-repeat-dayofweek" ).setAttribute( "label", calStrings.getFormattedString( "weekDayMonthLabel", [weekNumberText, dayOfWeekText, ofTheMonthText] ) );
|
|
hideElement("advanced-repeat-dayofweek-last");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* function to enable or disable the add exception button
|
|
*/
|
|
|
|
function updateAddExceptionButton()
|
|
{
|
|
//get the date from the picker
|
|
var datePickerValue = document.getElementById( "exceptions-date-picker" ).value;
|
|
|
|
if( isAlreadyException( datePickerValue ) ||
|
|
document.getElementById( "repeat-checkbox" ).getAttribute( "checked" ) != "true" )
|
|
{
|
|
disableElement("exception-add-button");
|
|
} else {
|
|
enableElement("exception-add-button");
|
|
}
|
|
}
|
|
|
|
|
|
function removeSelectedExceptionDate()
|
|
{
|
|
var exceptionsListbox = document.getElementById( "exception-dates-listbox" );
|
|
var SelectedItem = exceptionsListbox.selectedItem;
|
|
|
|
if( SelectedItem )
|
|
exceptionsListbox.removeChild( SelectedItem );
|
|
}
|
|
|
|
|
|
function addException( dateToAdd )
|
|
{
|
|
if( !dateToAdd ) {
|
|
//get the date from the date and time box.
|
|
//returns a date object
|
|
dateToAdd = document.getElementById( "exceptions-date-picker" ).value;
|
|
}
|
|
|
|
if( isAlreadyException( dateToAdd ) )
|
|
return;
|
|
|
|
var DateLabel = formatDate( dateToAdd );
|
|
|
|
//add a row to the listbox.
|
|
var listbox = document.getElementById( "exception-dates-listbox" );
|
|
//ensure user can see that add occurred (also, avoid bug 231765, bug 250123)
|
|
listbox.ensureElementIsVisible( listbox.appendItem( DateLabel, dateToAdd.getTime() ));
|
|
|
|
//sizeToContent();
|
|
}
|
|
|
|
|
|
function isAlreadyException( dateObj )
|
|
{
|
|
//check to make sure that the date is not already added.
|
|
var listbox = document.getElementById( "exception-dates-listbox" );
|
|
|
|
for( var i = 0; i < listbox.childNodes.length; i++ ) {
|
|
var dateToMatch = new Date( );
|
|
|
|
dateToMatch.setTime( listbox.childNodes[i].value );
|
|
if( dateToMatch.getMonth() == dateObj.getMonth() && dateToMatch.getFullYear() == dateObj.getFullYear() && dateToMatch.getDate() == dateObj.getDate() )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
function getDayExtension( dayNumber )
|
|
{
|
|
var dateStringBundle = srGetStrBundle("chrome://calendar/locale/dateFormat.properties");
|
|
|
|
if ( dayNumber >= 1 && dayNumber <= 31 )
|
|
return( dateStringBundle.GetStringFromName( "ordinal.suffix."+dayNumber ) );
|
|
else {
|
|
dump("ERROR: Invalid day number: " + dayNumber);
|
|
return ( false );
|
|
}
|
|
}
|
|
|
|
|
|
function getDayOfWeek( )
|
|
{
|
|
//get the day number for today.
|
|
var dayNumber = document.getElementById( "start-datetime" ).value.getDay();
|
|
|
|
var dateStringBundle = srGetStrBundle("chrome://calendar/locale/dateFormat.properties");
|
|
|
|
//add one to the dayNumber because in the above prop. file, it starts at day1, but JS starts at 0
|
|
var oneBasedDayNumber = parseInt( dayNumber ) + 1;
|
|
|
|
return( dateStringBundle.GetStringFromName( "day."+oneBasedDayNumber+".name" ) );
|
|
}
|
|
|
|
|
|
function getWeekNumberOfMonth()
|
|
{
|
|
//get the day number for today.
|
|
var startTime = document.getElementById( "start-datetime" ).value;
|
|
|
|
var oldStartTime = startTime;
|
|
|
|
var thisMonth = startTime.getMonth();
|
|
|
|
var monthToCompare = thisMonth;
|
|
|
|
var weekNumber = 0;
|
|
|
|
while( monthToCompare == thisMonth ) {
|
|
startTime = new Date( startTime.getTime() - ( 1000 * 60 * 60 * 24 * 7 ) );
|
|
|
|
monthToCompare = startTime.getMonth();
|
|
|
|
weekNumber++;
|
|
}
|
|
|
|
return( weekNumber );
|
|
}
|
|
|
|
|
|
function isLastDayOfWeekOfMonth()
|
|
{
|
|
//get the day number for today.
|
|
var startTime = document.getElementById( "start-datetime" ).value;
|
|
|
|
var oldStartTime = startTime;
|
|
|
|
var thisMonth = startTime.getMonth();
|
|
|
|
var monthToCompare = thisMonth;
|
|
|
|
var weekNumber = 0;
|
|
|
|
while( monthToCompare == thisMonth ) {
|
|
startTime = new Date( startTime.getTime() - ( 1000 * 60 * 60 * 24 * 7 ) );
|
|
|
|
monthToCompare = startTime.getMonth();
|
|
|
|
weekNumber++;
|
|
}
|
|
|
|
if( weekNumber > 3 ) {
|
|
var nextWeek = new Date( oldStartTime.getTime() + ( 1000 * 60 * 60 * 24 * 7 ) );
|
|
|
|
if( nextWeek.getMonth() != thisMonth ) {
|
|
//its the last week of the month
|
|
return( true );
|
|
}
|
|
}
|
|
return( false );
|
|
}
|
|
|
|
|
|
/* FILE ATTACHMENTS */
|
|
|
|
function removeSelectedAttachment()
|
|
{
|
|
var attachmentListbox = document.getElementById("attachmentBucket");
|
|
var SelectedItem = attachmentListbox.selectedItem;
|
|
|
|
if(SelectedItem)
|
|
attachmentListbox.removeChild(SelectedItem);
|
|
}
|
|
|
|
|
|
function addAttachment(attachmentToAdd)
|
|
{
|
|
if(!attachmentToAdd)
|
|
return;
|
|
|
|
//add a row to the listbox
|
|
document.getElementById("attachmentBucket").appendItem(attachmentToAdd.url, attachmentToAdd.url);
|
|
|
|
//sizeToContent();
|
|
}
|
|
|
|
|
|
function getWeekNumberText( weekNumber )
|
|
{
|
|
var dateStringBundle = srGetStrBundle("chrome://calendar/locale/dateFormat.properties");
|
|
if ( weekNumber >= 1 && weekNumber <= 4 )
|
|
return( dateStringBundle.GetStringFromName( "ordinal.name."+weekNumber) );
|
|
else if( weekNumber == 5 )
|
|
return( dateStringBundle.GetStringFromName( "ordinal.name.last" ) );
|
|
else
|
|
return( false );
|
|
}
|
|
|
|
|
|
/* URL */
|
|
|
|
var launch = true;
|
|
|
|
function loadURL()
|
|
{
|
|
if( launch == false ) //stops them from clicking on it twice
|
|
return;
|
|
|
|
launch = false;
|
|
//get the URL from the text box
|
|
var UrlToGoTo = document.getElementById( "uri-field" ).value;
|
|
|
|
if( UrlToGoTo.length < 4 ) //it has to be > 4, since it needs at least 1 letter, a . and a two letter domain name.
|
|
return;
|
|
|
|
//check if it has a : in it
|
|
if( UrlToGoTo.indexOf( ":" ) == -1 )
|
|
UrlToGoTo = "http://"+UrlToGoTo;
|
|
|
|
//launch the browser to that URL
|
|
launchBrowser( UrlToGoTo );
|
|
|
|
launch = true;
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function for filling the form, set the value of a property of a XUL element
|
|
*
|
|
* PARAMETERS
|
|
* elementId - ID of XUL element to set
|
|
* newValue - value to set property to ( if undefined no change is made )
|
|
* propertyName - OPTIONAL name of property to set, default is "value", use "checked" for
|
|
* radios & checkboxes, "data" for drop-downs
|
|
*/
|
|
|
|
function setFieldValue( elementId, newValue, propertyName )
|
|
{
|
|
var undefined;
|
|
|
|
if( newValue !== undefined ) {
|
|
var field = document.getElementById( elementId );
|
|
|
|
if( newValue === false ) {
|
|
try {
|
|
field.removeAttribute( propertyName );
|
|
} catch (e) {
|
|
dump("setFieldValue: field.removeAttribute couldn't remove "+propertyName+
|
|
" from "+elementId+" e: "+e+"\n");
|
|
}
|
|
} else if( propertyName ) {
|
|
try {
|
|
field.setAttribute( propertyName, newValue );
|
|
} catch (e) {
|
|
dump("setFieldValue: field.setAttribute couldn't set "+propertyName+
|
|
" from "+elementId+" to "+newValue+" e: "+e+"\n");
|
|
}
|
|
} else {
|
|
field.value = newValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function for getting data from the form,
|
|
* Get the value of a property of a XUL element
|
|
*
|
|
* PARAMETERS
|
|
* elementId - ID of XUL element to get from
|
|
* propertyName - OPTIONAL name of property to set, default is "value", use "checked" for
|
|
* radios & checkboxes, "data" for drop-downs
|
|
* RETURN
|
|
* newValue - value of property
|
|
*/
|
|
|
|
function getFieldValue( elementId, propertyName )
|
|
{
|
|
var field = document.getElementById( elementId );
|
|
|
|
if( propertyName )
|
|
return field[ propertyName ];
|
|
else
|
|
return field.value;
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function for getting a date/time from the form.
|
|
* The element must have been set up with setDateFieldValue or setTimeFieldValue.
|
|
*
|
|
* PARAMETERS
|
|
* elementId - ID of XUL element to get from
|
|
* RETURN
|
|
* newValue - Date value of element
|
|
*/
|
|
|
|
function getDateTimeFieldValue( elementId )
|
|
{
|
|
var field = document.getElementById( elementId );
|
|
return field.editDate;
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function for filling the form, set the value of a date field
|
|
*
|
|
* PARAMETERS
|
|
* elementId - ID of time textbox to set
|
|
* newDate - Date Object to use
|
|
*/
|
|
|
|
function setDateFieldValue( elementId, newDate )
|
|
{
|
|
// set the value to a formatted date string
|
|
|
|
var field = document.getElementById( elementId );
|
|
field.value = formatDate( newDate );
|
|
|
|
// add an editDate property to the item to hold the Date object
|
|
// used in onDatePick to update the date from the date picker.
|
|
// used in getDateTimeFieldValue to get the Date back out.
|
|
|
|
// we clone the date object so changes made in place do not propagte
|
|
|
|
field.editDate = new Date( newDate );
|
|
}
|
|
|
|
|
|
/**
|
|
* Helper function for filling the form, set the value of a time field
|
|
*
|
|
* PARAMETERS
|
|
* elementId - ID of time textbox to set
|
|
* newDate - Date Object to use
|
|
*/
|
|
|
|
function setTimeFieldValue( elementId, newDate )
|
|
{
|
|
// set the value to a formatted time string
|
|
var field = document.getElementById( elementId );
|
|
field.value = formatTime( newDate );
|
|
|
|
// add an editDate property to the item to hold the Date object
|
|
// used in onTimePick to update the date from the time picker.
|
|
// used in getDateTimeFieldValue to get the Date back out.
|
|
|
|
// we clone the date object so changes made in place do not propagte
|
|
|
|
field.editDate = new Date( newDate );
|
|
}
|
|
|
|
|
|
/*
|
|
* Take a Date object and return a displayable date string i.e.: May 5, 1959
|
|
*/
|
|
|
|
function formatDate( date )
|
|
{
|
|
return( opener.gCalendarWindow.dateFormater.getFormatedDate( date ) );
|
|
}
|
|
|
|
|
|
/*
|
|
* Take a Date object and return a displayable time string i.e.: 12:30 PM
|
|
*/
|
|
|
|
function formatTime( time )
|
|
{
|
|
var timeString = opener.gCalendarWindow.dateFormater.getFormatedTime( time );
|
|
return timeString;
|
|
}
|
|
|
|
|
|
// XXX lilmatt's new crap for the new XUL
|
|
|
|
|
|
/*
|
|
* A textbox changed - see if its button needs to be enabled or disabled
|
|
*/
|
|
|
|
function processTextboxWithButton( textboxId, buttonId )
|
|
{
|
|
var theTextbox = document.getElementById( textboxId );
|
|
var theButton = document.getElementById( buttonId );
|
|
|
|
// Only change things if we have to
|
|
if ( theTextbox.textLength == 0 && !theButton.disabled ) {
|
|
disableElement( buttonId );
|
|
} else if ( theTextbox.textLength > 0 && theButton.disabled ) {
|
|
enableElement( buttonId );
|
|
}
|
|
}
|
|
|
|
|
|
function processAlarmType()
|
|
{
|
|
var alarmMenu = document.getElementById("alarm-type");
|
|
var componentType = getFieldValue("component-type");
|
|
if( alarmMenu.selectedItem ) {
|
|
if( componentType == "event" ) {
|
|
debug("processAlarmType: EVENT " + alarmMenu.selectedItem.value );
|
|
switch( alarmMenu.selectedItem.value ) {
|
|
case "none":
|
|
disableElement("alarm-length-field");
|
|
disableElement("alarm-length-units");
|
|
disableElement("alarm-trigger-relation");
|
|
disableElement("before-this-event-label");
|
|
disableElement("alarm-email-field-label");
|
|
disableElement("alarm-email-field");
|
|
break;
|
|
//case "popupAndSound":
|
|
case "popup":
|
|
enableElement("alarm-length-field");
|
|
enableElement("alarm-length-units");
|
|
enableElement("alarm-trigger-relation");
|
|
enableElement("before-this-event-label");
|
|
disableElement("alarm-email-field-label");
|
|
disableElement("alarm-email-field");
|
|
break;
|
|
case "email":
|
|
enableElement("alarm-length-field");
|
|
enableElement("alarm-length-units");
|
|
enableElement("alarm-trigger-relation");
|
|
enableElement("before-this-event-label");
|
|
enableElement("alarm-email-field-label");
|
|
enableElement("alarm-email-field");
|
|
break;
|
|
}
|
|
} else if( componentType == "todo" ) {
|
|
debug("processAlarmType: TODO " + alarmMenu.selectedItem.value );
|
|
var startChecked = getFieldValue("start-checkbox", "checked");
|
|
var dueChecked = getFieldValue("due-checkbox", "checked");
|
|
|
|
// disable alarms if and only if neither checked
|
|
if ( (!startChecked && !dueChecked) ) {
|
|
menuListSelectItem("alarm-type", "none");
|
|
disableElement("alarm-length-field");
|
|
disableElement("alarm-length-units");
|
|
disableElement("alarm-trigger-relation");
|
|
disableElement("before-this-todo-label");
|
|
disableElement("alarm-email-field-label");
|
|
disableElement("alarm-email-field");
|
|
disableElement("alarm-type");
|
|
} else {
|
|
enableElement("alarm-type")
|
|
switch( alarmMenu.selectedItem.value ) {
|
|
case "none":
|
|
disableElement("alarm-length-field");
|
|
disableElement("alarm-length-units");
|
|
disableElement("alarm-trigger-relation");
|
|
disableElement("before-this-todo-label");
|
|
disableElement("alarm-email-field-label");
|
|
disableElement("alarm-email-field");
|
|
break;
|
|
//case "popupAndSound":
|
|
case "popup":
|
|
enableElement("alarm-length-field");
|
|
enableElement("alarm-length-units");
|
|
enableElement("before-this-todo-label");
|
|
disableElement("alarm-email-field-label");
|
|
disableElement("alarm-email-field");
|
|
if (startChecked && !dueChecked) {
|
|
menuListSelectItem("alarm-trigger-relation", "START");
|
|
disableElement("alarm-trigger-relation");
|
|
} else if (!startChecked && dueChecked) {
|
|
menuListSelectItem("alarm-trigger-relation", "END");
|
|
disableElement("alarm-trigger-relation");
|
|
} else {
|
|
enableElement("alarm-trigger-relation");
|
|
}
|
|
break;
|
|
case "email":
|
|
enableElement("alarm-length-field");
|
|
enableElement("alarm-length-units");
|
|
enableElement("before-this-todo-label");
|
|
enableElement("alarm-email-field-label");
|
|
enableElement("alarm-email-field");
|
|
if (startChecked && !dueChecked) {
|
|
menuListSelectItem("alarm-trigger-relation", "START");
|
|
disableElement("alarm-trigger-relation");
|
|
} else if (!startChecked && dueChecked) {
|
|
menuListSelectItem("alarm-trigger-relation", "END");
|
|
disableElement("alarm-trigger-relation");
|
|
} else {
|
|
enableElement("alarm-trigger-relation");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
dump("processAlarmType: ERROR! Invalid componentType passed: \n");
|
|
} else
|
|
dump("processAlarmType: ERROR! No alarmMenu.selectedItem!\n");
|
|
}
|
|
|
|
|
|
function processComponentType(componentType)
|
|
{
|
|
debug("processComponentType: " + componentType );
|
|
switch( componentType ) {
|
|
case "event":
|
|
// Hide and show the appropriate fields and widgets
|
|
changeMenuState("todo", "event");
|
|
// Set the menu properly if it isn't already
|
|
menuListSelectItem("component-type", "event");
|
|
// calling just enableElement _should_ work here, but it doesn't
|
|
document.getElementById("start-datetime").setAttribute( "disabled", "false" );
|
|
enableElement("start-datetime");
|
|
enableElement("end-datetime");
|
|
// Set alarm trigger menulist text correctly (using singular/plural code)
|
|
setFieldValue("alarm-trigger-text-kludge", "2"); // event text is "plural"
|
|
updateMenuLabels("alarm-trigger-text-kludge", "alarm-trigger-relation");
|
|
enableElement("alarm-type")
|
|
// Set menubar title correctly
|
|
changeTitleBar("event");
|
|
break;
|
|
case "todo":
|
|
// Hide and show the appropriate fields and widgets
|
|
changeMenuState("event", "todo");
|
|
// Set the menu properly if it isn't already
|
|
menuListSelectItem("component-type", "todo");
|
|
// Enable/disable date/time pickers as need be
|
|
onDateTimeCheckbox("start-checkbox", "start-datetime");
|
|
onDateTimeCheckbox("due-checkbox", "due-datetime");
|
|
// Set alarm trigger menulist text correctly (using singular/plural code)
|
|
setFieldValue("alarm-trigger-text-kludge", "1"); // todo text is "singular"
|
|
updateMenuLabels("alarm-trigger-text-kludge", "alarm-trigger-relation");
|
|
// Set menubar title correctly
|
|
changeTitleBar("todo");
|
|
break;
|
|
//case "journal":
|
|
default:
|
|
// We were passed an invalid value:
|
|
dump("processComponentType: ERROR! Tried to select invalid component type: "+componentType+"\n");
|
|
break;
|
|
}
|
|
processAlarmType();
|
|
}
|
|
|
|
|
|
/*
|
|
* Hides/shows/enables/disables fields and widgets using "hidden-controller"
|
|
* and "disable-controller" in the .xul
|
|
*/
|
|
|
|
function changeMenuState(hiddenController, showController, disableController, enableController)
|
|
{
|
|
var hiddenList = document.getElementsByAttribute( "hidden-controller", hiddenController );
|
|
var showList = document.getElementsByAttribute( "hidden-controller", showController );
|
|
var disableList = document.getElementsByAttribute( "disable-controller", disableController );
|
|
var enableList = document.getElementsByAttribute( "disable-controller", enableController );
|
|
|
|
for( var i = 0; i < hiddenList.length; ++i ) {
|
|
hideElement( hiddenList[i].id );
|
|
}
|
|
|
|
for( i = 0; i < showList.length; ++i ) {
|
|
showElement( showList[i].id );
|
|
}
|
|
|
|
for( i = 0; i < disableList.length; ++i ) {
|
|
disableElement( disableList[i].id );
|
|
}
|
|
|
|
for( i = 0; i < enableList.length; ++i ) {
|
|
enableElement( enableList[i].id );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Changes window title bar title ("New Event", "Edit Task", etc.)
|
|
*/
|
|
|
|
function changeTitleBar(componentType)
|
|
{
|
|
var title;
|
|
// Sanity check input
|
|
if ( componentType == "event" || componentType == "todo" ) {
|
|
var args = window.arguments[0];
|
|
|
|
// Is this a NEW event/todo, or are we EDITing an existing event/todo?
|
|
if( "new" == args.mode )
|
|
title = document.getElementById( "data-"+componentType+"-title-new" ).getAttribute("value");
|
|
else
|
|
title = document.getElementById( "data-"+componentType+"-title-edit" ).getAttribute("value");
|
|
debug("changeTitleBar: "+title);
|
|
document.title = title;
|
|
} else {
|
|
dump("changeTitleBar: ERROR! Tried to change titlebar to invalid value: "+componentType+"\n");
|
|
}
|
|
}
|
|
|
|
|
|
function processToDoStatus(status)
|
|
{
|
|
var completedDatePicker = document.getElementById( "completed-date-picker" );
|
|
switch(status) {
|
|
case "":
|
|
case "None":
|
|
menuListSelectItem("todo-status-field", "None");
|
|
disableElement( "completed-date-picker" );
|
|
disableElement( "percent-complete-menulist" );
|
|
disableElement( "percent-complete-label" );
|
|
break;
|
|
case "CANCELLED":
|
|
menuListSelectItem("todo-status-field", "CANCELLED");
|
|
disableElement( "completed-date-picker" );
|
|
disableElement( "percent-complete-menulist" );
|
|
disableElement( "percent-complete-label" );
|
|
break;
|
|
case "COMPLETED":
|
|
menuListSelectItem("todo-status-field", "COMPLETED");
|
|
enableElement( "completed-date-picker" );
|
|
enableElement( "percent-complete-menulist" );
|
|
enableElement( "percent-complete-label" );
|
|
completedDatePicker.value = new Date();
|
|
setFieldValue( "percent-complete-menulist", "100" );
|
|
break;
|
|
case "IN-PROCESS":
|
|
menuListSelectItem("todo-status-field", "IN-PROCESS");
|
|
enableElement( "completed-date-picker" );
|
|
enableElement( "percent-complete-menulist" );
|
|
enableElement( "percent-complete-label" );
|
|
break;
|
|
case "NEEDS-ACTION":
|
|
menuListSelectItem("todo-status-field", "NEEDS-ACTION");
|
|
enableElement( "percent-complete-menulist" );
|
|
enableElement( "percent-complete-label" );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
function onInviteeAdd()
|
|
{
|
|
textBox = document.getElementById("invite-email-field");
|
|
var email = "mailto:" + textBox.value;
|
|
addAttendee(email);
|
|
}
|
|
|
|
|
|
function addAttendee(email)
|
|
{
|
|
var treeItem = document.createElement("treeitem");
|
|
var treeRow = document.createElement("treerow");
|
|
var treeCell = document.createElement("treecell");
|
|
treeCell.setAttribute("label", email);
|
|
treeItem.appendChild(treeRow);
|
|
treeRow.appendChild(treeCell);
|
|
document.getElementById("bucketBody").appendChild(treeItem);
|
|
}
|
|
|
|
|