existing timed tasks and milestones in that they have a duration that increases based on the amount of time tracked for them, and no set completion time. Nearby minor fixes.
471 lines
16 KiB
JavaScript
471 lines
16 KiB
JavaScript
var dayMS = 86400000;
|
|
var popTitle = "Add/Edit Task";
|
|
|
|
// To be set by template vars
|
|
var dunits, hoursPerDay, taskLength;
|
|
var extrasPath, errorMsgs;
|
|
var taskArray;
|
|
|
|
function parseFloatOrNA(str) {
|
|
if (str == 'N/A') return 0.0;
|
|
else return parseFloat(str);
|
|
}
|
|
|
|
function doCalendar(fieldId) {
|
|
Calendar.setup({
|
|
inputField : fieldId,
|
|
ifFormat : "%Y-%m-%d",
|
|
showsTime : false,
|
|
step : 1,
|
|
timeFormat : "12",
|
|
firstDay : false
|
|
});
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function buildMenuUrl (urltype,taskId) {
|
|
if(urltype == "edit") {
|
|
alert("edit task: "+taskId);
|
|
} else if(urltype == "insertAbove") {
|
|
alert("insert task above: "+taskId);
|
|
} else if(urltype == "insertBelow") {
|
|
alert("insert task below: "+taskId);
|
|
} else if(urltype == "delete") {
|
|
alert("delete task: "+taskId);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function closeImage() {
|
|
return extrasPath + '/close.gif';
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function configureForTaskType(form) {
|
|
switch (getTaskType(form)) {
|
|
case 'timed':
|
|
form.end.disabled = false;
|
|
form.duration.disabled = false;
|
|
form.lagTime.disabled = false;
|
|
form.dependants.disabled = false;
|
|
form.percentComplete.disabled = false;
|
|
|
|
if (!form.duration.value || parseFloatOrNA(form.duration.value) == 0)
|
|
form.duration.value = (dunits == "hrs")? hoursPerDay : 1;
|
|
if (!form.percentComplete.value || form.percentComplete.value == 'N/A')
|
|
form.percentComplete.value = 0;
|
|
if (!form.lagTime.value || form.lagTime.value == 'N/A')
|
|
form.lagTime.value = 0;
|
|
|
|
break;
|
|
|
|
case 'progressive':
|
|
form.end.value = form.start.value;
|
|
form.end.disabled = true;
|
|
form.duration.disabled = false;
|
|
form.dependants.disabled = false;
|
|
form.lagTime.value = 'N/A';
|
|
form.lagTime.disabled = true;
|
|
form.percentComplete.value = 'N/A';
|
|
form.percentComplete.disabled = true;
|
|
break;
|
|
|
|
case 'milestone':
|
|
form.end.value = form.start.value;
|
|
form.duration.value = 0;
|
|
form.duration.disabled = true;
|
|
form.lagTime.value = 'N/A';
|
|
form.lagTime.disabled = true;
|
|
form.end.disabled = true;
|
|
form.dependants.disabled = true;
|
|
form.percentComplete.value = 'N/A';
|
|
form.percentComplete.disabled = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function getCheckedOfNodeList(list) {
|
|
for (var i = 0; i < list.length; i++) {
|
|
if (list[i].checked) { return list[i].value; }
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function setCheckedOfNodeList(list, value) {
|
|
for (var i = 0; i < list.length; i++) {
|
|
list[i].checked = (list[i].value == value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function getTaskType(form) { return getCheckedOfNodeList(form.taskType); }
|
|
function setTaskType(form, value) { setCheckedOfNodeList(form.taskType, value); }
|
|
function isTimed(form) { return getTaskType(form) == 'timed'; }
|
|
function isUntimed(form) { return !isTimed(form); }
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function checkEditTaskForm (form) {
|
|
if (form.name.value == "") {
|
|
alert(errorMsgs.name);
|
|
return;
|
|
} else if (form.start.value == "") {
|
|
alert(errorMsgs.start);
|
|
return;
|
|
} else if (isTimed(form) && form.end.value == "") {
|
|
alert(errorMsgs.end);
|
|
return;
|
|
}
|
|
form.submit();
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function intlDate(dateObj) {
|
|
return dateObj.getFullYear()+"-"+pad((dateObj.getMonth()+1))+"-"+pad(dateObj.getDate());
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function toDateObj(date) {
|
|
var to = date.split("-");
|
|
var dateObj = new Date(to[0],(to[1]-1),to[2],0,0,1,0);
|
|
return dateObj;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function adjustTaskTimeFromDuration(start, end, duration, lagTime, isTaskForm, predecessor, origStart, origEnd, seqNum) {
|
|
//set the form element
|
|
var form = duration.form;
|
|
|
|
var today = new Date();
|
|
var todayIntl = intlDate(today);
|
|
|
|
// Set values if not already set.
|
|
if (start.value == "") start.value = todayIntl;
|
|
if (end.value == "") end.value = todayIntl;
|
|
if (duration.value == "") duration.value = hoursPerDay;
|
|
|
|
// Convert hours to days.
|
|
var taskDuration = parseFloat(duration.value);
|
|
var taskTotalDuration = taskDuration + parseFloatOrNA(lagTime.value);
|
|
if(dunits == "hrs") taskTotalDuration = taskTotalDuration / hoursPerDay;
|
|
var totalDurationFloor = Math.floor(taskTotalDuration);
|
|
|
|
// We can't have timed tasks with duration zero. Those are called "milestones".
|
|
if (taskDuration <= 0 && getTaskType(form) == 'timed') {
|
|
if (isTaskForm) {
|
|
// Convert to milestone if desired.
|
|
if (confirm("Zero duration tasks are considered milestones. Do you wish to change this task to a milestone?")) {
|
|
setTaskType(form, 'milestone');
|
|
configureForTaskType(form);
|
|
} else {
|
|
duration.value = form.orig_duration.value;
|
|
if (parseFloat(duration.value) <= 0)
|
|
duration.value = hoursPerDay;
|
|
}
|
|
} else {
|
|
// Do not let users zero out tasks from the quick view.
|
|
alert("Zero duration tasks are considered Milestones. Please edit the task by clicking the link if you wish to change this task to a milestone");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//create the start date
|
|
var aTo = start.value.split("-");
|
|
var toDate = new Date(aTo[0],(aTo[1]-1),aTo[2],0,0,1,0);
|
|
|
|
//add new duration days to the start date
|
|
toDate.setDate(toDate.getDate() + totalDurationFloor);
|
|
|
|
//set end date to this date
|
|
end.value = intlDate(toDate);
|
|
|
|
//Set new duration in taskArray
|
|
taskArray[seqNum]["duration"] = taskDuration;
|
|
//Adjust time based on new end date
|
|
adjustTaskTimeFromDate(start, end, duration, lagTime, end, isTaskForm, predecessor, origStart, origEnd, seqNum);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function adjustTaskTimeFromDate(start, end, duration, lagTime, element, isTaskForm, predecessor, origStart, origEnd, seqNum) {
|
|
//set the form element
|
|
var form = element.form;
|
|
//set original duration from task form to determine whether or not to continue to set duration
|
|
var orig_duration;
|
|
|
|
if (isTaskForm) {
|
|
if (isUntimed(form)) return;
|
|
orig_duration = form.orig_duration.value;
|
|
}
|
|
|
|
//Handle case where both start and end are empty
|
|
if (start.value == "" && end.value == "") {
|
|
//get today's date
|
|
var today = new Date();
|
|
var todayIntl = intlDate(today);
|
|
//set start and end date if not already set
|
|
start.value = todayIntl;
|
|
end.value = todayIntl;
|
|
}
|
|
|
|
//Handle case where one is set and the other isn't
|
|
if (end.value == "") end.value = start.value;
|
|
if (start.value == "") start.value = end.value;
|
|
|
|
if (isTaskForm && orig_duration == "") {
|
|
//Set duration if this is a new record
|
|
//Check to make sure start date comes before end date
|
|
var startcomp = start.value.replace(/-/g,"");
|
|
var endcomp = end.value.replace(/-/g,"");
|
|
if(startcomp > endcomp) {
|
|
alert(errorMsgs.greaterthan);
|
|
if(element.name == "start") {
|
|
end.value = element.value;
|
|
} else {
|
|
start.value = element.value;
|
|
}
|
|
duration.value = (dunits == "hrs")?hoursPerDay:1;
|
|
lagTime.value = 0;
|
|
return;
|
|
}
|
|
|
|
var d = getDaysInterval(start.value,end.value);
|
|
if(d == 0) d = 1;
|
|
if(dunits == "hrs") {
|
|
d = d * hoursPerDay;
|
|
}
|
|
duration.value = d - lagTime.value;
|
|
} else {
|
|
//Set start/end if duration has been saved
|
|
var d = parseFloat(duration.value) + parseFloatOrNA(lagTime.value);
|
|
if(dunits == "hrs") {
|
|
//Convert to days
|
|
d = d / hoursPerDay;
|
|
}
|
|
//Round off duration or set it to zero if less than 1;
|
|
//alert("d = " + d + " floor = " + Math.floor(d));
|
|
if(d < 1) d = 0;
|
|
else d = Math.floor(d);
|
|
|
|
if(element.name.indexOf("start") > -1) {
|
|
//create the date
|
|
var aTo = start.value.split("-");
|
|
var toDate = new Date(aTo[0],(aTo[1]-1),aTo[2],0,0,1,0);
|
|
//add duration days to the start date
|
|
toDate.setDate(toDate.getDate() + d);
|
|
//set end date to this date
|
|
end.value = intlDate(toDate);
|
|
} else if(element.name.indexOf("end") > -1) {
|
|
//create the date
|
|
var aFrom = end.value.split("-");
|
|
var fromDate = new Date(aFrom[0],(aFrom[1]-1),aFrom[2],0,0,1,0);
|
|
//subtract duration days from the end date
|
|
fromDate.setDate(fromDate.getDate() - d);
|
|
//set start date to this date
|
|
start.value = intlDate(fromDate);
|
|
}
|
|
}
|
|
|
|
//Check Predecessors before moving stuff
|
|
var pred = predecessor.value;
|
|
if(pred != "") {
|
|
//Check to make sure that the dependency requirement for this task is still valid
|
|
//Get the predecessor end date
|
|
var taskStart = toDateObj(start.value);
|
|
var predTaskEnd;
|
|
if(isTaskForm) {
|
|
predTaskEnd = toDateObj(taskArray[pred]["end"]);
|
|
} else {
|
|
var predTaskEndId = "end_"+taskArray[pred]["id"]+"_formId"
|
|
predTaskEnd = toDateObj(document.getElementById(predTaskEndId).value);
|
|
}
|
|
|
|
if(taskStart.getTime() < predTaskEnd.getTime()) {
|
|
alert(errorMsgs.invalidMove);
|
|
start.value = origStart.value;
|
|
end.value = origEnd.value;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Check all tasks past this one and move them forward if necessary (this only needs to happen on the main form)
|
|
if(!isTaskForm) {
|
|
arrangePredecessors(element,seqNum);
|
|
}
|
|
//reset orig start and end values
|
|
origStart.value = start.value;
|
|
origEnd.value = end.value;
|
|
|
|
if(!isTaskForm) {
|
|
//Adjust task form for
|
|
paintGanttChart();
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function trim(str) {
|
|
return str.replace(/^\s+|\s+$/, '');
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function arrangePredecessors (element,seqNum) {
|
|
for (var i = 1; i <= taskLength; i++) {
|
|
var seq = i;
|
|
var task = taskArray[seq];
|
|
var taskId = task["id"];
|
|
//Calculate duration and duraiton floor
|
|
var totalDurationInDays = parseFloat(task["duration"]) + parseFloat(task["lagTime"]);
|
|
if(dunits == "hrs") totalDurationInDays = totalDurationInDays / hoursPerDay;
|
|
var totalDurationFloor = Math.floor(totalDurationInDays);
|
|
//alert("Duration Floor is: "+durationFloor);
|
|
//Get the current elements
|
|
var currElementStart = document.getElementById("start_"+taskId+"_formId");
|
|
var currElementEnd = document.getElementById("end_"+taskId+"_formId");
|
|
//alert("Current Start Date: "+currElementStart.value+" Current End Date: "+currElementEnd.value);
|
|
//Skip the first record as it is the record that was changed
|
|
if(seq > 1) {
|
|
var predecessor = task["predecessor"];
|
|
//alert("predecessor for "+i+" is "+predecessor);
|
|
if(predecessor != "") {
|
|
var pred = taskArray[predecessor];
|
|
var predEndDate = toDateObj(pred["end"]);
|
|
var startDate = toDateObj(task["start"]);
|
|
//alert ("Pred End Date: "+intlDate(predEndDate));
|
|
//Make sure start date of this task is greater than the end date of the predecessor
|
|
if(startDate.getTime() <= predEndDate.getTime()) {
|
|
//Change the start and end dates of the task
|
|
//Get the day part of the predecessor
|
|
var predDayPart = parseFloat(pred["dayPart"]);
|
|
//alert("predDayPart: "+predDayPart);
|
|
if(predDayPart > 0) {
|
|
//The previous task took up part of a day. Add the additional day part to the duration
|
|
totalDurationInDays += predDayPart;
|
|
totalDurationFloor = Math.floor(totalDurationInDays);
|
|
}
|
|
//alert("Duration in Days: "+durationInDays+" Duration Floor: "+durationFloor);
|
|
//Set the start date of this task to the end date of the predecessor
|
|
currElementStart.value = pred["end"];
|
|
//Adjust end date for change in start date and update the object - start date is actually predEndDate now, so use the existing date object
|
|
predEndDate.setDate(predEndDate.getDate() + totalDurationFloor);
|
|
currElementEnd.value = intlDate(predEndDate);
|
|
//alert("Set seq "+i+" to start: "+pred["end"]+" end: "+intlDate(predEndDate));
|
|
}
|
|
}
|
|
}
|
|
|
|
//Update task array with new start/end values
|
|
taskArray[seq]["start"] = currElementStart.value;
|
|
taskArray[seq]["end"] = currElementEnd.value;
|
|
taskArray[seq]["dayPart"] = (totalDurationInDays - Math.floor(totalDurationInDays));
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function getDaysInterval(from,to) {
|
|
var aFrom = from.split("-");
|
|
var aTo = to.split("-");
|
|
var fromDate = new Date(aFrom[0],(aFrom[1]-1),aFrom[2],0,0,1,0);
|
|
var toDate = new Date(aTo[0],(aTo[1]-1),aTo[2],0,0,1,0);
|
|
var fromEpoch = fromDate.getTime();
|
|
var toEpoch = toDate.getTime();
|
|
|
|
var seconds = toEpoch - fromEpoch;
|
|
if(seconds == 0) return 0;
|
|
return (seconds/dayMS);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function pad(date) {
|
|
var str = ""+date;
|
|
if(str.length == 1) {
|
|
str = "0"+str;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function paintGanttChart () {
|
|
var status = AjaxRequest.submit(document.forms['editAll'],{
|
|
'onSuccess':function(req){ document.getElementById('gantt').innerHTML = req.responseText; }
|
|
});
|
|
|
|
var mwidth = document.getElementById("projectTableWidth").name + "px";
|
|
var swidth = document.getElementById("projectScrollPercentWidth").name + "%";
|
|
document.getElementById("mastertable").style.width=mwidth;
|
|
document.getElementById("scrolltd").style.width=swidth;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------
|
|
function validateDependant(field,origField,seqNum,start,end,duration,lagTime,isTaskForm,origStart,origEnd) {
|
|
var pred = field.value;
|
|
var newTask = false;
|
|
if(pred != "") {
|
|
if (seqNum == "") seqNum = taskLength+1;
|
|
|
|
if (pred < 1) {
|
|
alert(errorMsgs.noPredecessor);
|
|
field.value = origField.value;
|
|
return;
|
|
}
|
|
|
|
if (pred == seqNum) {
|
|
alert(errorMsgs.samePredecessor);
|
|
field.value = origField.value;
|
|
return;
|
|
}
|
|
|
|
if (pred > seqNum) {
|
|
alert(errorMsgs.previousPredecessor);
|
|
field.value = origField.value;
|
|
return;
|
|
}
|
|
|
|
if (taskArray[pred]["type"] != 'timed') {
|
|
alert(errorMsgs.untimedPredecessor);
|
|
field.value = origField.value;
|
|
return;
|
|
}
|
|
|
|
//Set defaults if it's a new record and one of the other options hasn't been checked.
|
|
if(start.value == "" || end.value == "") {
|
|
//get today's date
|
|
newTask = true;
|
|
duration.value = (dunits == "hrs")?hoursPerDay:1;
|
|
}
|
|
//Get the predecessor end date and decide where the new start date belongs
|
|
var taskStart = start.value;
|
|
var taskStartObj = toDateObj(taskStart);
|
|
var predTaskEnd = taskArray[pred]["end"];
|
|
var predTaskEndObj = toDateObj(predTaskEnd);
|
|
|
|
//Change start date if it comes before predecessor end date
|
|
if(newTask || (taskStartObj.getTime() < predTaskEndObj.getTime())) {
|
|
|
|
//Convert predecessor hours to days
|
|
var taskTotalDuration = parseFloat(duration.value) + parseFloatOrNA(lagTime.value);
|
|
if(dunits == "hrs") taskTotalDuration = taskTotalDuration / hoursPerDay;
|
|
var totalDurationFloor = Math.floor(taskTotalDuration);
|
|
|
|
//Get the predecessor dayPart
|
|
var predDayPart = parseFloat(pred["dayPart"]);
|
|
if(predDayPart > 0) {
|
|
//The previous task took up part of a day. Add the additional day part to the duration
|
|
taskTotalDuration += predDayPart;
|
|
totalDurationFloor = Math.floor(totalDurationInDays);
|
|
}
|
|
|
|
//Set the start date of this task to the end date of the predecessor
|
|
start.value = predTaskEnd;
|
|
//Adjust end date for change in start date
|
|
adjustTaskTimeFromDate(start,end,duration,lagTime,start,isTaskForm,field,origStart,origEnd,seqNum);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Repaint
|
|
if(!isTaskForm) {
|
|
//Set new predecessor in taskArray
|
|
taskArray[seqNum]["predecessor"] = pred;
|
|
paintGanttChart();
|
|
}
|
|
}
|