// Time-Tracker Controller
// Copyright (c) 2005 Cédric Savarese <pro@4213miles.com>
// v2.01.1 Jan. 26th 2006

// v2.01.1 -  Jan. 26th - Added auto-focus when adding a task.


var Task              = new Model('models/task.xml');
var User              = new Model('srvc-registrar.php');
var TaskList          = null;  // loaded once the user is authenticated.

var user_login        = new View('views/user_login.xsl',        { placeholder: 'Menu',    onRenderComplete: addBehaviors_UserLogin });
var user_registration = new View('views/user_registration.xsl', { placeholder: 'Menu',    onRenderComplete: addBehaviors_UserRegistration });
var user_account      = new View('views/user_account.xsl',      { placeholder: 'Menu',    onRenderComplete: addBehaviors_UserAccount });
var task_list         = new View('views/task_list.xsl',         { placeholder: 'Content', onRenderComplete: addBehaviors_TaskList });
var task_report       = new View('views/task_report.xsl',       { placeholder: 'Report',  onRenderComplete: addBehaviors_TaskReport });
var timeslice_comment = new View('views/timeslice_comment.xsl', { placeholder: 'Comment', onRenderComplete: addBehaviors_Comment });

if(location.search.indexOf('debugLevel') != -1)
	Controller.debugLevel = parseInt(location.search.split('debugLevel=')[1]);
else
	Controller.debugLevel = 0;

Controller.onLoadComplete = dispatcher;
Controller.onLoadError    = errorHandler;
Controller.loadAssets();

// Task list display parameters
var taskListFilter = {};

// wDrag Initialization (nested lists drag&drop)
wHelpers.prototype.debug = function(msg) { /*Controller.debug(msg,2);*/ }; // hijack wDrag debug output
var wh = new wHelpers();
var wd = new wDrag();

// Contextual Menu
wh.addEvent(document,'mousedown', showContextualMenu);
var ctxMenu       = null;
var ctxMenuTarget = 0;

// Mouse Position
var mouseX = 0;
var mouseY = 0;

// User Preferences
User.getPreference = function(pref) {
	return User.get('document').getElementsByTagName('preferences')[0].getAttribute(pref);
}

// Timer Refresh Interval
var updateInterval = null;

// Save Status
var saveStatus  = "saveComplete";

// Javascript / Flash Integration
var uid         = "1"; 
var flashProxy  = new FlashProxy(uid, 'flash/JavaScriptFlashGateway.swf');		// Javascript / Flash Integration
var clockMarkup = new FlashTag('flash/clock.swf', 110, 130, '7,0,14,0'); 
clockMarkup.setFlashvars('lcId='+ uid);


// -------------------------------------------------------------------------------------------
// DISPATCHER
// -------------------------------------------------------------------------------------------

function dispatcher(action) {
	switch(action) {
		default:
			document.getElementById('ShowHelpLink').onclick = function() { 
				var help = document.getElementById('HelpInfo');
				help.style.display = (help.style.display=='block')?'none':'block';
			}
			Controller.render(User, user_account, { onRenderComplete: function() { addBehaviors_UserAccount({keepOpen: false}); } } );
			// User is authenticated. Load task list.
			TaskList = new Model('srvc-tracker.php?taskId=0');
			Controller.reloadAsset(TaskList, function(){ dispatcher('showTasks'); } );
			break;
		case 'showLogin':
			Controller.render(User, user_login);
			break;
		case 'showRegister':
			Controller.render(User, user_registration);
			break;
		case 'showAccount':
			Controller.render(User, user_account);
			break;
		case 'updateTimers':
			updateTimers(taskListFilter);	
			break;
		case 'showTasks':
			var	param = new Array();
			param.push(new Array('saveStatus',saveStatus));
			if(taskListFilter.type) 
				param.push(new Array('filter', taskListFilter.type));
			if(User.getPreference('showcompleted')=='true') 
				param.push(new Array('showCompleted', 'true'));
				
			Controller.render(TaskList, task_list, { xslParams: param });
			break;
		case 'showComment':
			var	param = new Array();
			param[0]  = new Array('taskId',ctxMenuTarget);
			param[1]  = new Array('timesliceId',arguments[1]);	
			Controller.render(TaskList, timeslice_comment, { xslParams: param });
			break;
		case 'viewReport':
			var	param = new Array();
			param.push(new Array('taskId',ctxMenuTarget));
			Controller.render(TaskList, task_report, { xslParams: param });
			break;
	}
}

function errorHandler(url, errorCode, message) {
	Controller.debug('Error Handler for :'+url +' ('+errorCode+' - ' + message + ')');
	if(errorCode == 401 || errorCode == 403) {
		dispatcher('showLogin');
	} else {
		alert('Error '+errorCode+' - url:'+url+' ' + message);
	}
}

// -------------------------------------------------------------------------------------------
// VIEW BEHAVIORS
// -------------------------------------------------------------------------------------------

function addBehaviors_UserLogin() {
	document.getElementById('RegisterLink').onclick = function(e) { dispatcher('showRegister'); return Controller.preventEvent(e);  } ;
	Controller.handleSubmit('LoginForm', { onSuccess: function(){ Controller.reloadAsset(TaskList) }, onError: showError, updateModel : User } ); 
} 

function addBehaviors_UserReLogin() { // Used after the session expired
	document.getElementById('RegisterLink').style.display = "none";
	Controller.handleSubmit('LoginForm', { onSuccess: function() { saveTaskList(); dispatcher('showAccount'); }, onError: showError, updateModel : User } ); 
} 

function addBehaviors_UserRegistration() {
	document.getElementById('LoginLink').onclick = function(e) { dispatcher('showLogin'); return Controller.preventEvent(e);  } ;
	Controller.handleSubmit('RegistrationForm', { onSuccess: function(){ Controller.reloadAsset(TaskList) }, onError: showError, updateModel : User } ); 
} 

function showError(errorCode, msg) {
	var n = document.getElementById('ErrorPlaceholder');
	if(n) n.innerHTML = "<p class='errorMsg'>Error: "+msg+"</p>";
}

function addBehaviors_UserAccount() {
	User.panel = { id     : 'EditAccountForm', 
				   form   : document.getElementById('EditAccountForm'),
				   effect : new fx.Height('EditAccountForm',{ duration:400, onComplete: function() { 
																								 if(this.el.offsetHeight > 0)
																								 	this.el.style.height='auto'; 
																									} })
			 	 };
		 
	User.panel.form.onsubmit = function(e) {
		if(wf.formValidation(e)) {
			if(User.panel.form["wf_ConfirmPassword"].value != User.panel.form["user:password"].value) {
				wf.showError(User.panel.form['wf_ConfirmPassword'],"The passwords don't match");
				return wh.preventEvent(e);
			} else {
				Controller.update(User, user_account);
				Controller.save(User);
				initContextualMenu();
				User.panel.effect.toggle();		
				dispatcher('showTasks');
			}
		}
		return Controller.preventEvent(e);
	}
	
	wf.addBehaviors('EditAccountForm');
	
	document.getElementById('ShowUserAccountLink').onclick       = function() { User.panel.effect.toggle(); }
	document.getElementById('EditUserAccountCancelLink').onclick = function() { Controller.render(User, user_account, { onRenderComplete: function() { addBehaviors_UserAccount({keepOpen: false}); } } ); }

	var keepOpen = arguments[0] ? arguments[0].keepOpen : true;
	if(!keepOpen) {
		User.panel.effect.toggle();
	}
}

function addBehaviors_TaskList() {
	
	// Add  task
	var addTaskLink = document.getElementById('AddTaskLink');
	if(addTaskLink)   addTaskLink.onclick    = addTask;

	// Udate timers every minute
	if(!updateInterval) {
		updateInterval = setInterval(function() { dispatcher('updateTimers'); },60000);	
		dispatcher('updateTimers');
	}
	
	// Drag&Drop
	wd.create("TaskList", { onUpdate:sortTaskList, handleClass:'dragHandle' } );
	
	// Timer filter
	 document.getElementById('FilterNone').onclick  = changeTaskListFilter;
	 document.getElementById('FilterMonth').onclick = changeTaskListFilter;
	 document.getElementById('FilterWeek').onclick  = changeTaskListFilter;
	 document.getElementById('FilterDay').onclick   = changeTaskListFilter;
	 
	 // Edit Mode 
	if(TaskList.get('document').documentElement.getAttribute('edit')=='true') {
		var saveTaskLink       = document.getElementById('SaveTaskLink');	
		var cancelTaskLink     = document.getElementById('CancelTaskLink');	
		var editTimeSlicesLink = document.getElementById('EditTimeSlicesLink');	
		saveTaskLink.onclick   = saveTask;
		cancelTaskLink.onclick = cancelEditTask;
		
		if(editTimeSlicesLink) {
			editTimeSlicesLink.onclick = function() { 
				//document.getElementById('TimeSlices').style.display = 'block'; 
				document.getElementById('TimeSlices').className = "visible";
			};
		}
		showClock();
		
		// comment links
		var n  = document.getElementById('TimeSlicesTable');
		var nl = n.getElementsByTagName('a');
		for(var i = nl.length-1; i>=0; i--) {
			if(nl[i].className=='addCommentLink' || nl[i].className=='editCommentLink') {
				nl[i].onclick = function(e) { var n=wh.getSourceElement(e); dispatcher('showComment',n.id.substr(3)) };
			}
		}
		
		// Auto-focus on input field
		var editField = document.getElementById('field_editTask_'+ ctxMenuTarget);	
		if(editField) {
			editField.focus();
		}
	}
}

function addBehaviors_TaskReport() {
	document.getElementById('CloseReportLink').onclick = hideReport;
}

function addBehaviors_Comment() {
	var comment = document.getElementById('Comment');
	var left    = mouseX - 350; 
	var top     = mouseY - 100; 
	if (left<20) left = 20;	
	if (top<40)  top  = 40;	
	comment.style.left    = left+'px';
	comment.style.top     = top+'px';
	comment.style.display = 'block';
	document.getElementById('SaveCommentLink').onclick   = saveComment;
	document.getElementById('CancelCommentLink').onclick = hideComment;
}

function changeTaskListFilter(e) { 
	var e = wh.getSourceElement(e).parentNode;
	var d = new Date();
	
	switch(e.id) {
		case 'FilterNone':
			taskListFilter.type  = '';
			taskListFilter.value = new Array(); 
			document.getElementById('TaskListFilter').className = 'filterNone';
			break;
		case 'FilterMonth':
			taskListFilter.type  = 'month';		
			taskListFilter.value = new Array(d.getFullYear(), d.getMonth()+1); 
			document.getElementById('TaskListFilter').className = 'filterMonth';			
			break;
		case 'FilterWeek':
			taskListFilter.type  = 'week';		
			taskListFilter.value = new Array(d.getFullYear(), d.getWeekNumber()); 
			document.getElementById('TaskListFilter').className = 'filterWeek';
			break;
		case 'FilterDay':
			taskListFilter.type  = 'day';	
			taskListFilter.value = new Array(d.getFullYear(), d.getMonth()+1, d.getDate()); 
			document.getElementById('TaskListFilter').className = 'filterDay';
			break;
	}
	Controller.debug('set filter to : ' + taskListFilter.type + ' ' + taskListFilter.value.join(','));	
	TaskList.get('document').documentElement.setAttribute('filter',taskListFilter.type);
	dispatcher('updateTimers');
	return Controller.preventEvent(e);
}

// -------------------------------------------------------------------------------------------
// TASK LIST EDIT FUNCTIONS
// -------------------------------------------------------------------------------------------

function addTask(e) {
	Controller.history.add(TaskList);
	
	var tasklistDoc = TaskList.get('document');
	var newtaskDoc  = Task.get('document');
	var insertPoint = tasklistDoc.documentElement;
	if(typeof tasklistDoc.importNode != 'undefined') 
		var task    = insertPoint.appendChild(tasklistDoc.importNode(newtaskDoc.documentElement,true));
	else 
		var task    = insertPoint.appendChild((newtaskDoc.documentElement).cloneNode(true));
	ctxMenuTarget   = "id-" + (new Date()).getTime();
	task.setAttribute('id', ctxMenuTarget);
	Controller.debug('add task #' + ctxMenuTarget);
	tasklistDoc.documentElement.setAttribute('edit','true');
	dispatcher('showTasks');
}

function editTask(e) {
	Controller.history.add(TaskList);
	Controller.debug('edit task #' + ctxMenuTarget);
	var tasklistDoc = TaskList.get('document');
	tasklistDoc.documentElement.setAttribute('edit','true');
	// allow only one task edit at a time
	var allTasks = tasklistDoc.getElementsByTagName('task');
	for (var i=0; i < allTasks.length; i++) {
		if(allTasks[i].getAttribute('id') == ctxMenuTarget) 
			allTasks[i].setAttribute('edit', 'true');
		else
			allTasks[i].setAttribute('edit', 'false');
	}
	hideContextualMenu(e);
	dispatcher('showTasks');
}

function saveTask(e) {
	Controller.history.removeLast();
	var tasklistDoc = TaskList.get('document');
	var task        = TaskList._getElementById(ctxMenuTarget);
	task.setAttribute('edit','false');
	Controller.debug('save task #' + ctxMenuTarget + ' ' + task.getAttribute('edit'));	
	tasklistDoc.documentElement.setAttribute('edit','false');
	hideContextualMenu(e);
	Controller.update(TaskList, task_list, task); 
	saveTaskList();
	dispatcher('showTasks');
}
function cancelEditTask(e) {
	Controller.debug('cancel edit task #' + ctxMenuTarget);
	Controller.history.undo();
	hideContextualMenu(e);
	dispatcher('showTasks');
}

function completeTask(e) {
	Controller.debug('complete task #' + ctxMenuTarget);
	var task = TaskList._getElementById(ctxMenuTarget);
	task.setAttribute('status', 'complete');
	hideContextualMenu(e);
	saveTaskList();
	dispatcher('showTasks');
}

function undoTask(e) {
	Controller.debug('undo task #' + ctxMenuTarget);
	var task = TaskList._getElementById(ctxMenuTarget);
	task.setAttribute('status', 'hold');
	hideContextualMenu(e);
	saveTaskList();
	dispatcher('showTasks');
}

function deleteTask(e) {
	Controller.debug('delete task #' + ctxMenuTarget);
	hideContextualMenu(e);
	if(confirm('Are you sure you want to delete this task ?')) {
		var task = TaskList._getElementById(ctxMenuTarget);
		task.parentNode.removeChild(task);
		saveTaskList();
		dispatcher('showTasks');
	}
}
// -------------------------------------------------------------------------------------------
// TIME-SLICE COMMENTS EDIT FUNCTIONS
// -------------------------------------------------------------------------------------------
function saveComment() {
	var task        = TaskList._getElementById(ctxMenuTarget);
	var sliceindex  = parseInt(document.getElementById('TimesliceIndex').value)-1;
	var timeslices  = task.getElementsByTagName('timetrack')[0].childNodes;
	var comments    = timeslices[sliceindex].getElementsByTagName('comment');
	if(comments.length==0) {
		// create an empty comment element. Controller.update will not work otherwise.	
		var doc = TaskList.get('document');
		var com = doc.createElement('comment');
		timeslices[sliceindex].appendChild(com);
	}
	Controller.update(TaskList, timeslice_comment, timeslices[sliceindex]);
	hideComment();
	//saveTaskList();
}
function hideComment() {
	document.getElementById('Comment').style.display='none';
}
// -------------------------------------------------------------------------------------------
// REPORT DISPLAY FUNCTIONS
// -------------------------------------------------------------------------------------------
function showReport(e) {
	hideContextualMenu(e);
	dispatcher('viewReport');
	document.getElementById('Report').style.display='block';
}
function hideReport(e) {
	document.getElementById('Report').style.display='none';
}
// -------------------------------------------------------------------------------------------
// SAVE MODELS FUNCTIONS
// -------------------------------------------------------------------------------------------
function saveTaskList() {
	Controller.debug('save in progress',1);
	saveStatus = 'saveInProgress';
	document.getElementById('SaveIndicator').className = saveStatus;		
	Controller.save(TaskList, { onSaveComplete: saveComplete, onError: saveError } );
}
function saveComplete() {
	Controller.debug('save complete',1);	
	saveStatus = 'saveComplete';
	document.getElementById('SaveIndicator').className = saveStatus;	
}
function saveError(errorCode, errorMessage) {
	Controller.debug('saving error',1);
	saveStatus = 'saveError';
	document.getElementById('ErrorMessagePlaceholder').innerHTML = errorMessage;
	document.getElementById('SaveIndicator').className = saveStatus;
	if(errorCode == 401 || errorCode == 403) {
		// session expired. log back in with cookie.
		Controller.reloadAsset(User, saveTaskList);
		//Controller.render(User, user_login, { onRenderComplete: addBehaviors_UserReLogin });
	}
}
// -------------------------------------------------------------------------------------------
// DRAG & DROP FUNCTIONS
// -------------------------------------------------------------------------------------------

function sortTaskList(list) {
	iterateList(list,TaskList.get('document').documentElement);	
	dispatcher('updateTimers');
}

function iterateList(list,xmlNode) {
	var n  = list.firstChild;
	var lg = list.childNodes.length;
	for(var i=0;i<lg;i++) {		
		if(n.nodeType==1 && (n.tagName.toLowerCase()=='li' || n.tagName.toLowerCase()=='ul')) {						
			if(n.tagName.toLowerCase()=='li') {
				var fn = TaskList._getElementById(n.id.substr(5));
				if(fn && fn.tagName!='form') {
					try {
						fn = xmlNode.appendChild(fn);				
					} catch(x) { alert('error: '+x); }
				}
				iterateList(n,fn);				
			} else {
				iterateList(n,xmlNode);
			}
		}
		n = n.nextSibling;
	}	
}

// -------------------------------------------------------------------------------------------
// CONTEXTUAL MENU
// -------------------------------------------------------------------------------------------
function initContextualMenu() {
	
	if(document.getElementById('CtxMenu')) {
		var eventType    = User.getPreference('selectonrelease')=='true' ? 'onmouseup' : 'onclick';
		var nonEventType = eventType == 'onmouseup' ? 'onclick' : 'onmouseup';
		Controller.debug('init contextual menu / event type: ' + eventType);
		document.getElementById('CtxMenuStart')[eventType]  = pressTimer;
		document.getElementById('CtxMenuStop')[eventType]   = pressTimer;
		document.getElementById('CtxMenuEdit')[eventType]   = editTask;		
		document.getElementById('CtxMenuReport')[eventType] = showReport;	
		document.getElementById('CtxMenuDone')[eventType]   = completeTask;			
		document.getElementById('CtxMenuUndone')[eventType] = undoTask;			
		document.getElementById('CtxMenuDelete')[eventType] = deleteTask;	

		document.getElementById('CtxMenuStart')[nonEventType]  = null;
		document.getElementById('CtxMenuStop')[nonEventType]   = null;
		document.getElementById('CtxMenuEdit')[nonEventType]   = null;			
		document.getElementById('CtxMenuDone')[nonEventType]   = null;	
		document.getElementById('CtxMenuReport')[nonEventType] = null;	
		document.getElementById('CtxMenuUndone')[nonEventType] = null;			
		document.getElementById('CtxMenuDelete')[nonEventType] = null;	

		ctxMenu = document.getElementById('CtxMenu');
	}				
}

function showContextualMenu(e) {

	// If we don't have a tasklist yet (not logged in), don't open it.
	if(!TaskList.get('document')) {
		return false;
	}
	
	// click coordinates
	mouseX = e.pageX ? e.pageX : (document.documentElement.scrollLeft? (e.clientX + document.documentElement.scrollLeft) : (e.clientX + document.body.scrollLeft));
	mouseY = e.pageY ? e.pageY : (document.documentElement.scrollTop ? (e.clientY + document.documentElement.scrollTop ) : (e.clientY + document.body.scrollTop ));

	if(!ctxMenu) initContextualMenu();

	var tg = wh.getSourceElement(e);
	
	// If the menu is already displayed, and the click is not on the menu, close it.
	if(ctxMenu.style.display == 'block') {
		var tmp = tg;
		while(tmp.id != 'CtxMenu' && tmp.nodeName.toLowerCase() !='html') {
			tmp = tmp.parentNode;
		}
		if(tmp.id != 'CtxMenu') {		
			hideContextualMenu(e);
			return;
		}
	}
	
	// If the task list is in edit mode, do not open it
	if(TaskList.get('document').documentElement.getAttribute('edit')=='true') {	
		return;
	}
	// If it's not the left button, do not open it.
	if(!wd.isLeftClick(e))
		return;

	// if it's on a draggable element, do not open it
	if(tg.className && tg.className.indexOf(wd.classNameHandle) != -1) 
		return;
	
	// If it's on an input tag do not open it 	
	if(tg.tagName.toUpperCase() == 'INPUT') return;

	// If it's not on the task list, do not open it
	while((!tg.id || tg.id.indexOf('task_') == -1) && tg.nodeName.toLowerCase() !='html') {
		tg = tg.parentNode;
	}
	if(!tg.className ||  tg.id.indexOf('task_') == -1) {		
		return;
	}

	// Get Related Task Id (stored in the LI tag id)		
	var taskId     = tg.id.substr(tg.id.lastIndexOf('_')+1);
	ctxMenuTarget  = taskId; // Set TimeTracker object property. 

	// Show/Hide appropriate menu options
	var task       = TaskList._getElementById(taskId);	
	var taskEdit   = task.getAttribute('edit');
	var taskStatus = task.getAttribute('status');
	switch(taskStatus) {
			case 'running':
				document.getElementById('CtxMenuStart').style.display  = 'none';								
				document.getElementById('CtxMenuStop').style.display   = 'block';
				document.getElementById('CtxMenuDone').style.display   = 'block';				
				document.getElementById('CtxMenuUndone').style.display = 'none';					
				document.getElementById('CtxMenuEdit').style.display   = 'block';					
				break;
			case 'hold':
				document.getElementById('CtxMenuStart').style.display  = 'block';				
				document.getElementById('CtxMenuStop').style.display   = 'none';
				document.getElementById('CtxMenuDone').style.display   = 'block';				
				document.getElementById('CtxMenuUndone').style.display = 'none';					
				document.getElementById('CtxMenuEdit').style.display   = 'block';	
				break;
			case 'complete':
				document.getElementById('CtxMenuStart').style.display  = 'none';				
				document.getElementById('CtxMenuStop').style.display   = 'none';					
				document.getElementById('CtxMenuDone').style.display   = 'none';				
				document.getElementById('CtxMenuUndone').style.display = 'block';		
				document.getElementById('CtxMenuEdit').style.display   = 'none';	
				break;					
	}
	
	// Position Menu and display it
	ctxMenu.style.left    = (mouseX-15)+'px';
	ctxMenu.style.top     = (mouseY-15)+'px';
	ctxMenu.style.display = 'block';

	// Prevents the click event from bubbling up
	wh.stopPropagation(e);
	// Prevents text from being selected when holding the mouse button down 
	window.document.onselectstart = function(){return false;} // IE
	return wh.preventEvent(e);
}

function hideContextualMenu(e) {
	Controller.debug('hide contextual menu');
	var ctxMenu           = document.getElementById('CtxMenu');
	ctxMenu.style.display ='none'; 
	ctxMenu.onmouseout    = null;
	window.document.onselectstart = null // IE
	wh.stopPropagation(e);
	return wh.preventEvent(e);
}

// -------------------------------------------------------------------------------------------
// TIMER FUNCTIONS
// -------------------------------------------------------------------------------------------

function pressTimer(e) {
	Controller.debug('press Timer tasklist #'+ ctxMenuTarget);
	var task = TaskList._getElementById(ctxMenuTarget);
	if(task.getAttribute('status') == 'hold') {
		startTimer(task);
	} else {
		stopTimer(task);
	}
	hideContextualMenu(e);
	saveTaskList();
	dispatcher('showTasks');
}

function startTimer(task) {		
	var tasklistDoc = TaskList.get('document');
	
	if(User.getPreference('onerunningtask')=='true') { 
		// stop all other running tasks
		var allTasks = tasklistDoc.getElementsByTagName('task');
		for (var i=0; i < allTasks.length; i++) {
			if(allTasks[i].getAttribute('status')=='running')
				stopTimer(allTasks[i]);
		}
	}
	var timeSlice = task.getElementsByTagName('timetrack')[0].appendChild(tasklistDoc.createElement('timeslice'));				
	setStartDate(timeSlice);			
	task.setAttribute('status','running');
	
	if(User.getPreference('addcommentonstart')=='true') { 
		var timesliceIndex = task.getElementsByTagName('timetrack')[0].childNodes.length;
		dispatcher('showComment', timesliceIndex);
	}

}

function stopTimer(task) {
	var timeSlice = task.getElementsByTagName('timetrack')[0].lastChild;
	setEndDate(timeSlice);			
	task.setAttribute('status','hold');
	
	if(User.getPreference('addcommentonstop')=='true') { 
		var timesliceIndex = task.getElementsByTagName('timetrack')[0].childNodes.length;		
		dispatcher('showComment', timesliceIndex);
	}
}

function setStartDate(timeSlice) {
	if(arguments[1]) 
		var startDate = new Date(arguments[1]);
	else
		var startDate = new Date();
	
	timeSlice.setAttribute('startshortdate', getShortDate(startDate));
	timeSlice.setAttribute('startdate'     , startDate.toLocaleDateString() + ', ' + startDate.toLocaleTimeString());
	timeSlice.setAttribute('starttimestamp', startDate.getTime());
	timeSlice.setAttribute('week'          , startDate.getWeekNumber()); 
	timeSlice.setAttribute('day'           , startDate.getDate());
	timeSlice.setAttribute('month'         , startDate.getMonth()+1);	
	timeSlice.setAttribute('year'          , startDate.getFullYear());	
}	

function setEndDate(timeSlice) {
	if(arguments[1]) 
		var endDate = new Date(arguments[1]);
	else
		var endDate = new Date();
		
	timeSlice.setAttribute('endshortdate', getShortDate(endDate));
	timeSlice.setAttribute('enddate'     , endDate.toLocaleDateString() + ', ' + endDate.toLocaleTimeString());
	timeSlice.setAttribute('endtimestamp', endDate.getTime());
	var timeDif = parseInt(timeSlice.getAttribute('endtimestamp')) - parseInt(timeSlice.getAttribute('starttimestamp'));
	timeSlice.setAttribute('duration', toHours(timeDif));
}

function updateTimers(filter) {
	Controller.debug('Update timers');
	var root = TaskList.get('document').documentElement;
	for (var i=0; i < root.childNodes.length; i++) {
		var task = root.childNodes[i];
		if(task.nodeName == 'task') {
			compoundTaskDuration(task, filter);
		}
	}
}

function compoundTaskDuration(task) {
	var filter   = arguments[1] || {};
	
	var nl       = task.childNodes;
	var duration = 0;
	// Add duration of nested tasks
	for(var i=0; i < nl.length; i++) {
		if(nl[i].nodeName == 'task') {
			duration += compoundTaskDuration(nl[i], filter);
		}
	}
	// Add duration of current task
	duration += getTaskDuration(task, filter); 
	var txtDuration = toHours(duration);
	task.setAttribute('totaltime',txtDuration);
	
	// Update display
	var n = document.getElementById('taskTimer_'+task.getAttribute('id'))
	if(n) n.innerHTML = txtDuration;
	
	return duration;
}

function getTaskDuration(task, filter) {
	var n        = task.getElementsByTagName('timetrack')[0]; 
	var slices   = n.getElementsByTagName('timeslice');
	var duration = 0;
	
	for(var i=0; i < slices.length; i++) {
		var timeSlice      = slices[i];
		var startTimestamp = timeSlice.getAttribute('starttimestamp');
		var endTimestamp   = timeSlice.getAttribute('endtimestamp');

		if(!endTimestamp || endTimestamp=="") {
			endTimestamp   = new Date().getTime();
		}
		
		var addTime = parseInt(endTimestamp) - parseInt(startTimestamp);
		
		if(isNaN(addTime)) 
			Controller.debug("Invalid timestamp - slice #" + i + ", start was: " + startTimestamp + " end was: " + endTimestamp);
		else {
			var startDate = new Date(parseInt(startTimestamp));
			// START BACKWARD COMPATIBILITY CODE - CS. 15/12/2005
			if(!(typeof(timeSlice.getAttribute('week')) == typeof(''))) { // cross-browser 'hasAttribute'
				timeSlice.removeAttribute('startdate_std');
				timeSlice.removeAttribute('starthour_std');
				timeSlice.removeAttribute('endhourSTD');
				timeSlice.removeAttribute('enddate_std');
				timeSlice.removeAttribute('endhour_std');
				
				timeSlice.setAttribute('startshortdate', getShortDate(startDate)); 
				timeSlice.setAttribute('startdate', startDate.toLocaleDateString() + ', ' + startDate.toLocaleTimeString()); 
				timeSlice.setAttribute('week',  startDate.getWeekNumber()); 
				timeSlice.setAttribute('day',   startDate.getDate());
				timeSlice.setAttribute('month', startDate.getMonth()+1);	
				timeSlice.setAttribute('year',  startDate.getFullYear());	

				if(timeSlice.getAttribute('endtimestamp') != '') {
					var endDate = new Date(parseInt(endTimestamp));
					timeSlice.setAttribute('endshortdate', getShortDate(endDate)); 
					timeSlice.setAttribute('enddate', endDate.toLocaleDateString() + ', ' + endDate.toLocaleTimeString()); 
				} 
			}
			// END BACKWARD COMPATIBILITY CODE
			
			// Check if the timeslice is filtered out
			if(filter.type == 'week'  && ( timeSlice.getAttribute('year')  != filter.value[0] || 
									       timeSlice.getAttribute('week')  != filter.value[1] )) 
				continue;
			if(filter.type == 'day'   && ( timeSlice.getAttribute('year')  != filter.value[0] ||
									       timeSlice.getAttribute('month') != filter.value[1] ||
									       timeSlice.getAttribute('day')   != filter.value[2] ))
				continue;			
			if(filter.type == 'month' && ( timeSlice.getAttribute('year')  != filter.value[0] ||
  									       timeSlice.getAttribute('month') != filter.value[1] ))
				continue;			

			// All good
			duration     += addTime;	

		}
	}
	return duration;
}

function toHours(milliseconds) {
	
	var sign = "";
	if(milliseconds<0) {
		milliseconds = - milliseconds;
		sign = "-";
	}
	var seconds = milliseconds/1000;
	var hh=Math.floor(seconds/3600);
	var mm=Math.floor((seconds - (hh*3600))/60);
	if(mm<10) mm = "0"+mm;
	return sign + hh +"h "+mm+"mn"; 
}

// returns a date formatted according to the user's preferences.
function getShortDate(date) {
	switch(User.getPreference('dateformat')) {
		default: /* US mm/dd */
			var shortDate = (date.getMonth()+1) + '/' + date.getDate()   ;/*   + '/' + date.getFullYear();*/
			break;
		case 'dd/mm':
			var shortDate = date.getDate()      + '/' + (date.getMonth()+1)  ;/* + '/' + date.getFullYear(); */
			break;
		case 'yyyy/mm/dd':
			var shortDate = date.getFullYear()  + '/' + (date.getMonth()+1) + '/' + date.getDate();
			break;
	}

	switch(User.getPreference('timeformat')) {
		default: /* US am/pm */
			var hh = date.getHours();
			if(hh>12) 
				var shortTime = (hh-12) + ':' + (date.getMinutes()<10 ? '0':'') + date.getMinutes() + ' pm' ;
			else if(hh==12) 
				var shortTime = hh + ':' + (date.getMinutes()<10 ? '0':'') + date.getMinutes() + ' pm' ;
			else if(hh==0) 
				var shortTime = (hh+12) + ':' + (date.getMinutes()<10 ? '0':'') + date.getMinutes() + ' am' ;
			else 
				var shortTime = hh + ':' + (date.getMinutes()<10 ? '0':'') + date.getMinutes() + ' am' ;
			break;
		case '24h':
			var shortTime = (date.getHours()<10 ? '0':'') + date.getHours() + ':'+ (date.getMinutes()<10 ? '0':'') + date.getMinutes();
			break;
	}
	return shortDate + ' ' + shortTime;
}

/** Returns the number of the week in year, as defined in ISO 8601. */
Date.prototype.getWeekNumber = function() {
	var d = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
	var DoW = d.getDay();
	d.setDate(d.getDate() - (DoW + 6) % 7 + 3); // Nearest Thu
	var ms = d.valueOf(); // GMT
	d.setMonth(0);
	d.setDate(4); // Thu in Week 1
	return Math.round((ms - d.valueOf()) / (7 * 864e5)) + 1;
};

// -------------------------------------------------------------------------------------------
// CLOCK FUNCTIONS
// -------------------------------------------------------------------------------------------

function showClock() {
	// Show Clock (Flash)
	document.getElementById('ClockPlaceHolder').innerHTML = clockMarkup.toString();
	var radioGroup = document.forms['TaskListForm'].timestamp;
	for(i=0; radioGroup && i<radioGroup.length;i++) 
		radioGroup[i].onclick = resetClock;		
}

function resetClock(e) {
	var task       = TaskList._getElementById(ctxMenuTarget);
	var timeSlices = task.getElementsByTagName('timeslice');
	
	var tg = wh.getSourceElement(e);
	if(tg) { 
		// function called by onclick event on the timeslices radio group
		var timeSliceIdx  = parseInt(tg.id.substring(tg.id.indexOf('-')+1))-1;
		var timeSliceType = tg.id.substring(0,tg.id.indexOf('-'));			
		var timeSlice     = timeSlices[timeSliceIdx];

		if(timeSliceType=='start') 
			var dateTime = parseInt(timeSlice.getAttribute('starttimestamp'));
		else
			var dateTime = parseInt(timeSlice.getAttribute('endtimestamp'));
		Controller.debug('reset clock id='+tg.id+' idx='+ timeSliceIdx + ' ' + timeSlice+ ' ts:' + dateTime,3);

		flashProxy.call('resetTime',dateTime); 
	}
}

function updateTimeSlice(timeStamp) {
	Controller.debug('update timestamp:'+timeStamp,3);
	var task       = TaskList._getElementById(ctxMenuTarget);
	var timeSlices = task.getElementsByTagName('timeslice');
	var radioGroup = document.forms['TaskListForm'].timestamp;
	var labelId    = null;
	for(i=0; i<radioGroup.length; i++) {
		if(radioGroup[i].checked) {
			var tg            = radioGroup[i];
			var timeSliceIdx  = parseInt(tg.id.substring(tg.id.indexOf('-')+1))-1;
			var timeSliceType = tg.id.substring(0,tg.id.indexOf('-'));
			labelId           = tg.id + '-L';
			var durationId    = 'duration-'+ (timeSliceIdx+1);
			break;
		}
	}
	if(labelId) {
		// update model
		if(timeSliceType=='start') 
			setStartDate(timeSlices[timeSliceIdx], timeStamp);
		else
			setEndDate(timeSlices[timeSliceIdx], timeStamp);
			
		// update display
		var localString = new Date(timeStamp).toDateString() + ', ' + new Date(timeStamp).toLocaleTimeString();			
		var duration    = toHours(parseInt(timeSlices[timeSliceIdx].getAttribute('endtimestamp'))-parseInt(timeSlices[timeSliceIdx].getAttribute('starttimestamp')));

		document.getElementById(labelId).innerHTML    = localString;
		document.getElementById(durationId).innerHTML = duration;		
	}
}