/**
 * @fileoverview
 * Functions to set up a time selection popup. 
 * Some code modifed or taken wholesale from the DHTML Calendar (highly recommended and available at <a href="http://www.dynarch.com/projects/calendar">http://www.dynarch.com/projects/calendar</a>).
 */

/**
 * Setup function to attach handler to trigger
 * @param (params) an associative array of parameters, hopefully containing a
 *	'trigger' and 'field' element
 */
Time.setup = function(params) {
	var trigger = document.getElementById(params['trigger']);
	var field = document.getElementById(params['field']);
	if (!trigger || !field) {
		alert('Required fields for the time picker were not found. Please check your Time.setup call.');
		return false;
	}
	trigger.onclick = function() {
		if (field.value.match(/\d{2}:\d{2} (p|a)m/)) {
			var oldTime = field.value.split(/[: ]/);
				var oldHour = oldTime[0];
				var oldMinute = oldTime[1];
				var oldAmPm = oldTime[2];
		}
		else {
			var oldHour, oldMinute, oldAmPm = null;
		}
		var time = new Time(this, field, oldHour, oldMinute, oldAmPm);
		time.create();
		time.show();
		return false;
	};  
}

/**
 * Time constructor
 * @constructor
 * @param trigger the element that opens the picker when clicked
 * @param field the field to update when selections are made
 * @param hour the currently set hour
 * @param minute the currently set minute
 * @param ampm the currently set am/pm
 */
function Time(trigger, field, hour, minute, ampm) {
	/**
	 * The element that shows the time picker.
	 */
	this.trigger = trigger;
	/**
	 * The field that displays the picked time.
	 */
	this.field = field;
	/**
	 * The table cell that shows the picked time.
	 */
	this.statusCell = null;
	/**
	 * The cell containing the selected hour.
	 */
	this.selectedHour = null;
	/**
	 * The cell containing the selected minute.
	 */
	this.selectedMinute = null;
	/**
	 * The cell containing the selected am/pm.
	 */
	this.selectedAmPm = null;
	/**
	 * The chosen hour.
	 */
	this.theHour = hour,
	/**
	 * The chosen minute.
	 */
	this.theMinute = minute;
	/**
	 * The chosen am/pm.
	 */
	this.theAmPm = ampm;
};

/**
 * Update the time in the picker's associated field
 */
Time.prototype.updateTime = function() {
	var hour = this.theHour ? this.theHour : '12';
	var minute = this.theMinute ? this.theMinute : '00';
	var ampm = this.theAmPm ? this.theAmPm : 'am';
	this.statusCell.innerHTML = hour + ':' + minute + ' ' + ampm;
	this.field.value = hour + ':' + minute + ' ' + ampm
};

/**
 * Temporarily set the status bar
 */
Time.prototype.tempStatus = function(str) {
	if (str == '') {
		str = this.oldStatus;
	} else {
		this.oldStatus = this.statusCell.innerHTML;
	}
	this.statusCell.innerHTML = str;
}

/**	
 * Creates the table to allow user picking
 */
Time.prototype.create = function() {
	window.time = this;
	var table = Time.createElement('table');
	table.cellSpacing = 0;
	table.cellPadding = 3;
	
	var parent = document.getElementsByTagName("body")[0];
	var div = Time.createElement('div', parent);
	div.onblur = function() { this.style.display = 'none'; };
	div.className = 'calendar';
	div.style.position = 'absolute';
	div.style.zIndex = 100; // @todo fix bug where IE renders the table under a disabled select input
	this.element = div;
	div.appendChild(table);
	div.onblur = function() { window.time.style.display = 'none'; };

    var thead = Time.createElement('thead', table);
    var row = Time.createElement('tr', thead);

    cell = document.createElement('td');
    cell.className = 'button';
    cell.onclick = function() { alert('Time selection:\n- Click to select hour, minutes, and am/pm\n'); }
    cell.onmouseover = function() { Time.addClass(this, 'hilite'); window.time.tempStatus('Help'); }
    cell.onmouseout = function() { Time.removeClass(this, 'hilite'); window.time.tempStatus(''); }
    cell.innerHTML = '<div unselectable="on">?</div>';
    row.appendChild(cell);

    cell = document.createElement('td');
    cell.className = 'title';
    cell.colSpan = 3;
    cell.innerHTML = 'Time';
    row.appendChild(cell);

    cell = document.createElement('td');
    cell.className = 'button';
    cell.onclick = function() { window.time.hide(); }
    cell.onmouseover = function() { Time.addClass(this, 'hilite'); window.time.tempStatus('Close'); }
    cell.onmouseout = function() { Time.removeClass(this, 'hilite'); window.time.tempStatus(''); }
    cell.innerHTML = "<div unselectable='on'>&#x00d7;</div>";
    row.appendChild(cell);

    row = Time.createElement('tr', thead);
    row.className = 'daynames';

    cell = document.createElement('td');
    cell.colSpan = 2;
    cell.className = 'day name';
    cell.style.borderTop = '1px solid #556';
    cell.style.borderRight = '1px solid #556';
    cell.innerHTML = 'Hour';
    row.appendChild(cell);

    cell = document.createElement('td');
    cell.colSpan = 2;
    cell.className = 'day name';
    cell.style.borderTop = '1px solid #556';
    cell.style.borderRight = '1px solid #556';
    cell.innerHTML = 'Minutes';
    row.appendChild(cell);

    cell = document.createElement('td');
    cell.className = 'day name';
    cell.style.borderTop = '1px solid #556';
    cell.innerHTML = '&nbsp;';
    row.appendChild(cell);

	var tbody = Time.createElement('tbody', table);

	row = Time.createRow(tbody);

	cell = this.createCell('01', 'hour', row, 'top left');
	cell = this.createCell('07', 'hour', row, 'top right');
	cell = this.createCell('00', 'minute', row, 'top left');
	cell = this.createCell('30', 'minute', row, 'top right');
	cell = this.createCell('am', 'ampm', row, 'top center'); 

	row = Time.createRow(tbody);

	cell = this.createCell('02', 'hour', row, 'left');
	cell = this.createCell('08', 'hour', row, 'right');
	cell = this.createCell('05', 'minute', row, 'left');
	cell = this.createCell('35', 'minute', row, 'right');
	cell = this.createCell('pm', 'ampm', row, 'center');

	row = Time.createRow(tbody);

	cell = this.createCell('03', 'hour', row, 'left');
	cell = this.createCell('09', 'hour', row, 'right');
	cell = this.createCell('10', 'minute', row, 'left');
	cell = this.createCell('40', 'minute', row, 'right');
	cell = this.createCell('', 'ampm', row, 'center');

	row = Time.createRow(tbody);

	cell = this.createCell('04', 'hour', row, 'left');
	cell = this.createCell('10', 'hour', row, 'right');
	cell = this.createCell('15', 'minute', row, 'left');
	cell = this.createCell('45', 'minute', row, 'right');
	cell = this.createCell('', 'ampm', row, 'center');

	row = Time.createRow(tbody);

	cell = this.createCell('05', 'hour', row, 'left');	
	cell = this.createCell('11', 'hour', row, 'right');
	cell = this.createCell('20', 'minute', row, 'left');
	cell = this.createCell('50', 'minute', row, 'right');
	cell = this.createCell('', 'ampm', row, 'center');

	row = Time.createRow(tbody);

	cell = this.createCell('06', 'hour', row, 'bottom left');
	cell = this.createCell('12', 'hour', row, 'bottom right');
	cell = this.createCell('25', 'minute', row, 'bottom left');
	cell = this.createCell('55', 'minute', row, 'bottom right');
	cell = this.createCell('', 'ampm', row, 'bottom center');

	tfoot = Time.createElement('tfoot', table);

	row = Time.createElement('tr', tfoot);
	row.className = "footrow";
	
	cell = Time.createElement('td', row);
	cell.className = 'ttip';
	cell.innerHTML = 'Select time';
	cell.colSpan = 5;
	this.statusCell = cell;
};

/**
 * Creates a table cell for picking.
 * @param {string} contents the contents of the table cell
 * @param {string} type the type of the content (hour, minute, ampm)
 * @param {Object} parent the parent element
 * @param {string} position where the cell is placed within the table, used for drawing
 *	the cell borders (e.g., 'top right', 'bottom left', 'left')
 * @returns the cell object
 * @type HTMLTableCellElement
 */
Time.prototype.createCell = function(contents, type, parent, position) {
	var tCell = Time.createElement('td', parent);
	tCell.position = position;
	tCell.innerHTML = contents;
	tCell.className = 'day name';

	if (contents != '') {
		tCell.onmouseover = function() { Time.addClass(this, 'hilite'); Time.drawBorders(this)};
		tCell.onmouseout = function() { Time.removeClass(this, 'hilite'); };
		switch (type) {
			case 'hour':
				if (this.theHour == contents) {
					this.selectedHour = tCell;
					Time.addClass(tCell, 'selected');
				}
				tCell.onclick = function() {
					if (window.time.selectedHour != this) {
						Time.addClass(this, 'selected');
						if (window.time.selectedHour) {
							Time.removeClass(window.time.selectedHour, 'selected');
							Time.drawBorders(window.time.selectedHour);
						}
						window.time.selectedHour = this;		
						window.time.theHour = this.innerHTML;
						window.time.updateTime();
						Time.drawBorders(this); 
					}
				};
				break;

			case 'minute':
				if (this.theMinute == contents) {
					this.selectedMinute = tCell;
					Time.addClass(tCell, 'selected');
				}
				tCell.onclick = function() {
					if (window.time.selectedMinute != this) {
						Time.addClass(this, 'selected');
						if (window.time.selectedMinute) {
							Time.removeClass(window.time.selectedMinute, 'selected');
							Time.drawBorders(window.time.selectedMinute);
						}
						window.time.selectedMinute = this;		
						window.time.theMinute = this.innerHTML;
						window.time.updateTime();
						Time.drawBorders(this);
					}
				};
				break;

			case 'ampm':
				if (this.theAmPm == contents) {
					this.selectedAmPm = tCell;
					Time.addClass(tCell, 'selected');
					tCell.style.borderLeft = 'none';
				}
				tCell.onclick = function() {
					if (window.time.selectedAmPm != this) {
						Time.addClass(this, 'selected');
						if (window.time.selectedAmPm) {
							Time.removeClass(window.time.selectedAmPm, 'selected');
							Time.drawBorders(window.time.selectedAmPm);
						}
						window.time.selectedAmPm = this;		
						window.time.theAmPm = this.innerHTML;
						window.time.updateTime();
						Time.drawBorders(this);
					}
				};
				break;
		}
		Time.drawBorders(tCell);
	}
	return tCell;
}

/**
 * Creates a table row.
 * @param {Object} parent the parent element
 * @return the row object
 * @type HTMLTableRowElement
 */
Time.createRow = function(parent) {
	var row = Time.createElement('tr', parent);
	row.className = 'daysrow';
	row.onmouseover = function() { this.className = 'rowhilite'; };
	row.onmouseout = function() { this.className = 'daysrow'; };
	return row;
}

/**
 * Creates an element as a child of the element parent.
 * @param {string} type the type of the element (e.g., 'td', 'div', etc.)
 * @param {Object} parent the parent element
 * @return The element
 * @type HTMLUnknownElement
 */
Time.createElement = function(type, parent) {
	var el = null;
	if (document.createElementNS) {
		// use the XHTML namespace; IE won't normally get here unless
		// _they_ "fix" the DOM2 implementation.
		el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
	} else {
		el = document.createElement(type);
	}
	if (typeof parent != "undefined") {
		parent.appendChild(el);
	}
	return el;
};

/**
 * Gets the absolute position of an element.
 * @param {Object} el the element
 * @return the position in format {x:xpos, y:ypos}
 * @type Object
 */
Time.getAbsolutePos = function(el) {
	var SL = 0, ST = 0;
	var is_div = /^div$/i.test(el.tagName);
	if (is_div && el.scrollLeft)
		SL = el.scrollLeft;
	if (is_div && el.scrollTop)
		ST = el.scrollTop;
	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
	if (el.offsetParent) {
		var tmp = this.getAbsolutePos(el.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}
	return r;
};

/** 
 * Displays the time picker.
 */
Time.prototype.show = function() {
	var field = this.field;
	var p = Time.getAbsolutePos(field);	

	function fixPosition(box) {
		if (box.x < 0)
			box.x = 0;
		if (box.y < 0)
			box.y = 0;
		var cp = document.createElement("div");
		var s = cp.style;
		s.position = "absolute";
		s.right = s.bottom = s.width = s.height = "0px";
		document.body.appendChild(cp);
		var br = Time.getAbsolutePos(cp);
		document.body.removeChild(cp);
		if (Time.is_ie) {
			br.y += document.body.scrollTop;
			br.x += document.body.scrollLeft;
		} else {
			br.y += window.scrollY;
			br.x += window.scrollX;
		}
		var tmp = box.x + box.width - br.x;
		if (tmp > 0) box.x -= tmp;
		tmp = box.y + box.height - br.y;
		if (tmp > 0) box.y -= tmp;
	};

	p.y += field.offsetHeight;
	p.x += field.offsetWidth;
	p.width = this.element.offsetWidth;;
	p.height = this.element.offsetHeight + 40;

	fixPosition(p);
	this.showAt(p.x , p.y);	
	Time.addEvent(document, "mousedown", Time._checkShown);
	this.hideShowCovered();
};

/**
 * Hides the time picker.
 */
Time.prototype.hide = function() {
	this.element.style.display = 'none';
	Time.removeEvent(document, "mousedown", Time._checkShown);
	this.hideShowCovered();
}

/**
 * Shows the time picker at position x, y.
 * @param {number} x the x coordinate
 * @param {number} y the y coordinate
 */
Time.prototype.showAt = function(x, y) {
	var s = this.element.style;
	s.left = x + "px";
	s.top = y + "px";
	s.position = 'absolute';
	s.display = 'block';
};

/**
 * Checks to see if the element clicked was not the time picker and
 * closes the time picker if something else was clicked.
 * @param {Object} ev the click event
 * @type boolean
 */
Time._checkShown = function(ev) {
	var time = window.time;
	var el = Time.is_ie ? Time.getElement(ev) : Time.getTargetElement(ev);
	for (; el != null && el != time.element; el = el.parentNode);
	if (el == null) {
		// calls closeHandler which should hide the calendar.
		window.time.hide();
		return Time.stopEvent(ev);
	}
};


/**
 * Stops an event.
 * @param {Object} ev the event
 * @type boolean
 */
Time.stopEvent = function(ev) {
	ev || (ev = window.event);
	if (Time.is_ie) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
	return false;
};

/**
 * Hides all the select boxes if the browser is IE and the div is popping over them
 */
Time.prototype.hideShowCovered = function () {
	if (!Calendar.is_ie && !Calendar.is_opera)
		return;
	function getVisib(obj){
		var value = obj.style.visibility;
		if (!value) {
			if (document.defaultView && typeof (document.defaultView.getComputedStyle) == "function") { // Gecko, W3C
				if (!Calendar.is_khtml)
					value = document.defaultView.
						getComputedStyle(obj, "").getPropertyValue("visibility");
				else
					value = '';
			} else if (obj.currentStyle) { // IE
				value = obj.currentStyle.visibility;
			} else
				value = '';
		}
		return value;
	};

	var tags = new Array("applet", "iframe", "select");
	var el = this.element;

	var p = Calendar.getAbsolutePos(el);
	var EX1 = p.x;
	var EX2 = el.offsetWidth + EX1;
	var EY1 = p.y;
	var EY2 = el.offsetHeight + EY1;

	for (var k = tags.length; k > 0; ) {
		var ar = document.getElementsByTagName(tags[--k]);
		var cc = null;

		for (var i = ar.length; i > 0;) {
			cc = ar[--i];

			p = Calendar.getAbsolutePos(cc);
			var CX1 = p.x;
			var CX2 = cc.offsetWidth + CX1;
			var CY1 = p.y;
			var CY2 = cc.offsetHeight + CY1;

			if (this.hidden || (CX1 > EX2) || (CX2 < EX1) || (CY1 > EY2) || (CY2 < EY1)) {
				if (!cc.__msh_save_visibility) {
					cc.__msh_save_visibility = getVisib(cc);
				}
				cc.style.visibility = cc.__msh_save_visibility;
			} else {
				if (!cc.__msh_save_visibility) {
					cc.__msh_save_visibility = getVisib(cc);
				}
				cc.style.visibility = "hidden";
			}
		}
	}
};

/**
 * Adds an event to an element.
 * @param {Object} el The element
 * @param {string} evname The event name (e.g., 'click')
 * @param {function} func The function to attach
 */
Time.addEvent = function(el, evname, func) {
	if (el.attachEvent) { // IE
		el.attachEvent("on" + evname, func);
	} else if (el.addEventListener) { // Gecko / W3C
		el.addEventListener(evname, func, true);
	} else {
		el["on" + evname] = func;
	}
};

/**
 * Removes an event from an element.
 * @param {Object} el The element
 * @param {string} evname The event name (e.g., 'click')
 * @param {function} func The function to remove
 */
Time.removeEvent = function(el, evname, func) {
	if (el.detachEvent) { // IE
		el.detachEvent("on" + evname, func);
	} else if (el.removeEventListener) { // Gecko / W3C
		el.removeEventListener(evname, func, true);
	} else {
		el["on" + evname] = null;
	}
};

/**
 * Gets the parent of the clicked element.
 * @param {Object} ev the click event
 * @return The parent element
 * @type Object
 */
Time.getTargetElement = function(ev) {
	var f = Time.is_ie ? window.event.srcElement : ev.target;
	while (f.nodeType != 1)
		f = f.parentNode;
	return f;
};

/**
 * Returns the parent element of the clicked element.
 * @param {Object} ev the click event
 * @return The parent element
 * @type Object
 */
Time.getElement = function(ev) {
	var f = Time.is_ie ? window.event.srcElement : ev.currentTarget;
	while (f.nodeType != 1 || /^div$/i.test(f.tagName))
		f = f.parentNode;
	return f;
};

/**
 * Removes a class from an element.
 * @param {Object} el the element
 * @param {string} className the class to remove
 */
Time.removeClass = function(el, className) {
	if (!(el && el.className)) {
		return;
	}
	var cls = el.className.split(" ");
	var ar = new Array();
	for (var i = cls.length; i > 0;) {
		if (cls[--i] != className) {
			ar[ar.length] = cls[i];
		}
	}
	el.className = ar.join(" ");
};

/**
 * Adds a class to an element.
 * @param {Object} el the element
 * @param {string} className the class to add
 */
Time.addClass = function(el, className) {
	Time.removeClass(el, className);
	el.className += " " + className;
};

/**
 * Draws the borders for a cell based on its position.
 * @param {Object} el the element
 */
Time.drawBorders = function(el) {
	var position = el.position;

	if (!position) return false;

	if (el.className.match(/selected/)) {
		el.style.borderWidth = '0';
		if (position.match(/right/)) {
			el.style.borderRight= '1px solid #000';
			el.style.borderLeftWidth = '1px';
		}
		else {
			if (!position.match(/center/))
				el.style.borderRightWidth = '1px';
		}

		if (position.match(/top/)) {
			el.style.borderBottomWidth = '1px';
		}
		else {
			if (position.match(/bottom/)) {
				el.style.borderTopWidth = '1px';
			}
			else {
				el.style.borderBottomWidth = '1px';
				el.style.borderTopWidth = '1px';
			}
		}
	}
	else {
		if (!el.className.match(/hilite/)) {
			el.style.borderWidth = '0';
			if (position.match(/right/)) {
				el.style.borderRight = '1px solid #000';
			}
		}
		else {
			el.style.borderWidth = '1px';
		}
	}
}

/**
 * Detects a special case of quote unquote web browser.
 */
Time.is_ie = ( /msie/i.test(navigator.userAgent) &&
		   !/opera/i.test(navigator.userAgent) );
