/* technihowlib.js
*
* 11/06/07 ND - add calendar functionality
*/

/* Utility Functions */
/* Refer to Sitepoint Book Modern Web Design using Javascript & DOM */

function addEventByID(id, evType, fn, useCapture) {
	addEvent(document.getElementById(id), evType, fn, useCapture);
}

function addEvent(elm, evType, fn, useCapture) {
	// cross-browser event handling for IE5+, NS6 and Mozilla
	// By Scott Andrew
	if (elm.addEventListener) {
		elm.addEventListener(evType, fn, useCapture);
		return true;
	} else if (elm.attachEvent) {
		var r = elm.attachEvent("on" + evType, fn);
		EventCache.add(elm, evType, fn);
		return r;
	} else {
		elm["on" + evType] = fn;
	}
}

function detachEvent(elm, evType, fn, useCapture) {
    if (elm.removeEventListener) {
        elm.removeEventListener(evType, fn, useCapture);
    } else if (elm.detachEvent) {
        elm.detachEvent("on" + evType, fn);
    } else {
        target["on" + evType] = null;
    }
    return true;
};

function detachEventByID(id, evType, fn, useCapture) {
	detachEvent(document.getElementById(id), evType, fn, useCapture);
}

/* From Sitepoint Book The Javascript Anthology*/
function getEventTarget(event){
	var targetElement = null;
	if (event.target)  {
		targetElement = event.target;
	} else {
		targetElement = event.srcElement;
	}
	while (targetElement.nodeType == 3 && targetElement.parentNode != null) {
		targetElement = targetElement.parentNode;
	}
	return targetElement;
};

function stopDefaultAction(event){
	event.returnValue = false;
	if (event.preventDefault) {
		event.preventDefault();
	}
	return true;
};

// climb up the tree to the supplied tag.
function ascendDOM(e, target) {
  while (e.nodeName.toLowerCase() != target && 
      e.nodeName.toLowerCase() != 'html')
    e = e.parentNode;
  
  return (e.nodeName.toLowerCase() == 'html') ? null : e;
}

/* Refer to Sitepoint Book The Javascript Anthology */
function getEventTarget(event) {
	var targetElement = null;
	if (event.target) {
		targetElement = event.target;
	} else {
		targetElement = event.srcElement;
	}
	while (targetElement.nodeType == 3 && targetElement.parentNode != null) {
		targetElement = targetElement.parentNode;
	}
	return targetElement;
}

function getViewportSize() {
	var size = [0,0];

	if (window.innerWidth) {
		size = [window.innerWidth, window.innerHeight];
	} else if (document.documentElement
			&& document.documentElement.clientWidth
			&& document.documentElement.clientWidth != 0) {
		size = [document.documentElement.clientWidth,
				document.documentElement.clientHeight];
	} else {
		body = document.getElementsByTagName('body');
		size = [body[0].clientWidth,
		body[0].clientHeight];
	}
	return size;
}

function getScrollingPosition() {
	var position = [0, 0];

	if(window.pageYOffset) {
		position = [window.pageXOffset,window.pageYOffset];
	}

	if(document.documentElement.scrollTop && document.documentElement.scrollTop > 0) {
		position = [document.documentElement.scrollLeft,document.documentElement.scrollTop];
	} else if(document.body.scrollTop) {
		position = [document.body.scrollLeft,document.body.scrollTop];
	}
	return position;
}

function getPosition(elm){
	var X = 0;
	var Y = 0;
	while (elm != null) {
		X += elm.offsetLeft;
		Y += elm.offsetTop;
		elm = elm.offsetParent;
	}
	return [X, Y];
};

function setRelativePosition(elm, relElm, x, y) {
	var elmPos = getPosition(relElm);
	
	if (!x) var x = 0;
	if (!y) var y = 0;
	
	x += elmPos[0];
	y += elmPos[1];
	
	elm.style.left = x + 'px';
	elm.style.top = y + 'px';
}
	
function setLyr(obj,lyr)
{
	var coors = findPos(obj);
	if (lyr == 'testP') coors[1] -= 50;
	var x = document.getElementById(lyr);
	x.style.top = coors[1] + 'px';
	x.style.left = coors[0] + 'px';
}

function identifyBrowser(){
	var agent = navigator.userAgent.toLowerCase();
	if (navigator.vendor && navigator.vendor == "KDE" && window.sidebar) {
		return "kde";
	} else if (window.opera) {
		var version = parseFloat(agent.replace(/.*opera[\/ ]([^ $]+).*/, "$1"));
		if (version >= 7) {
			return "opera7";
		} else if (version >= 5) {
			return "opera5";
		}
		return false;
	} else if (document.all) {
		if (document.getElementById) {
			var browser = agent.replace(/.*ms(ie[\/ ][^ $]+).*/, "$1").replace(/ /, "");
			if (document.uniqueID) {
				if (browser.indexOf("5.5") != -1) {
					return browser.replace(/(.*5\.5).*/, "$1");
				} else {
					return browser.replace(/(.*)\..*/, "$1");
				}
			} else {
				return "ie5mac";	
			}
		}
		return false;
	} else if (document.getElementById) {
		if (navigator.vendor.indexOf("Apple Computer, Inc.") != -1) {
			if (window.XMLHttpRequest){
				return "safari1.2";
			}
			return "safari1";
		} else if (agent.indexOf("gecko") != -1) {
			return "mozilla";
		}
	}
	return false;
};

function identifyOS(){
	var agent = navigator.userAgent.toLowerCase();
	if (agent.indexOf("win") != -1)	{
		return "win";
	} else if (agent.indexOf("mac")) {
		return "mac";
	} else {
		return "unix";
	}
	return false;
};

// matchedArray used in suite of functions below
var matchedArray = null;

function getDOMElementsByAttribute(parent, attribute, attributeValue) {
	matchedArray = new Array();
	
	topElem = document.getElementById(parent);
	if (topElem) {
		_addDOMChildElementsByAttribute(topElem, attribute, attributeValue);
	}
	
	var returnArray = matchedArray;
	// Set global array back to null for garbage collection
  	matchedArray = null;
  	return returnArray;
}
	
function _addDOMChildElementsByAttribute(elem, attribute, attributeValue) {
	var i = 0;
	var n = elem.childNodes.length;
	for (i = 0; i < n; i++) {
		if (typeof(elem.childNodes[i] == 'object')) {
			_testElementByAttribute(elem.childNodes[i], attribute, attributeValue);
			_addDOMChildElementsByAttribute(elem.childNodes[i], attribute, attributeValue);
		}
	}
	return true;
}
	
function getElementsByAttribute(attribute, attributeValue) {
	var elementArray = new Array();
	matchedArray = new Array();

	if (document.all) {
		elementArray = document.all;
	} else {
		elementArray = document.getElementsByTagName("*");
	}
	var n = elementArray.length;
	for (var i = 0; i < n; i++) {
		_testElementByAttribute(elementArray[i], attribute, attributeValue);
	}

	var returnArray = matchedArray;
	// Set global array back to null for garbage collection
	matchedArray = null;

	return returnArray;
}

function _testElementByAttribute(elem, attribute, attributeValue) {
	if (attribute == "class") {
		if (typeof elem.className == "string") {
			var pattern = new RegExp("(^| )" + attributeValue + "( |$)");

			if (elem.className.match(pattern)) {
				matchedArray[matchedArray.length] = elem;
			}
		}
	} else if (attribute == "for") {
		if (elem.getAttribute("htmlFor") || elem.getAttribute("for")) {
			if (elem.htmlFor == attributeValue) {
				matchedArray[matchedArray.length] = elem;
			}
		}
	} else if (elem.getAttribute(attribute) == attributeValue) {
		matchedArray[matchedArray.length] = elem;
	}
	return true;
}	

/* Functions to support dynamic select form elements */
function SelectKeyPress(e) {
    var obj = getEventTarget(e);
    var key = FindKey(e);

	if(obj.options.length != 0) {
    	if (obj.options.selectedIndex == 0) {
        	var cEdit = obj.options[0].text;

	        if ((key>47 && key<59)||(key>62 && key<127) ||(key==32))  {
		        // make Editable option Null if it is being edited for the first time
        		if (cEdit == "Type Here...") {
	        	    cEdit = "";
				}
    	    	cEdit+=String.fromCharCode(key);
        	}
	        if(key==8 || key==127) {	// backspace
    	      	cEdit = cEdit.substring(0,cEdit.length-1);
        	}

	        // Set the new edited string into the Editable option
    	    obj.options[0].text = cEdit;
        	obj.options[0].value = 0;
		
			//if (cEdit.length >= 2) {
				xajax__ChangeCompany(cEdit, obj.id);
			//}
	        return false;
		}
	}
    return true; 
}

function SelectKeyDown(e) {
	// To provide proper behaviour for Delete and Backspace keys
	var key = FindKey(e);

    // Delete key pressed
    if(key == 46) {
		SelectDelete(e);
    }

    // backspace key pressed
    if(key == 8 || key==127)  {
		if(e.which) {	// Netscape
    		e.which = ""; //this property has only a getter.
	    } else {	// IE
	    	//To prevent backspace from activating the -Back- button of the browser
	    	e.key = "";
    		if(window.event.keyCode) {
		    	window.event.keyCode = "";
			}
    	}
	    return true;
    }
}
  
function SelectDelete(e)  {
    var obj = getEventTarget(e);
  
    if(obj.options.length != 0) {
		if (obj.options.selectedIndex == 0) {
	    	obj.options[0].text = "";
    		obj.options[0].value = 0;
		}
    }
}
  
function FindKey(e) {
    if(e.which) {
	    key=e.which;  //Netscape
    } else {
		key=e.keyCode; //IE
    }
	return key;
}

/* Functions to support AJAX Box */
function AJAXBoxOpen(func, record_id, div_id) {
	div = document.getElementById(div_id);
	openImg = document.getElementById(div_id + "_o");
	refreshImg = document.getElementById(div_id + "_r");

	imgRoot = openImg.src.substring(0,openImg.src.search(/images/) + 7);
	
	if (openImg.src.search(/boxplus.gif/) > 0) {
		// Means we are opening up the box
		div.style.display = "block";
		if (div.innerHTML.length == 0) {
			// Only load up again if no content
			
			window["xajax_" + func](record_id);
		}
		// Change this button to minus
		openImg.src = imgRoot + "boxminus.gif";
		openImg.alt = "Close";
		// Show refresh icon
		refreshImg.style.display = "inline";
	} else {
		// Means we are hiding the box
		div.style.display = "none";
		openImg.src = imgRoot + "boxplus.gif";
		openImg.alt = "Open";
	}
}
	
function AJAXBoxRefresh(func, record_id, div_id) {
	div = document.getElementById(div_id);
	openImg = document.getElementById(div_id + "_o");
	imgRoot = openImg.src.substring(0,openImg.src.search(/images/) + 7);

	// Renew content
	window["xajax_" + func](record_id);
	div.style.display = "block";
	openImg.src = imgRoot + "boxminus.gif";
	openImg.alt = "Close";
}
	
/*	EventCache Version 1.0
	Copyright 2005 Mark Wubben

	Provides a way for automatically removing events from nodes and thus preventing memory leakage.
	See <http://novemberborn.net/javascript/event-cache> for more information.
	
	This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
*/

/*	Implement array.push for browsers which don't support it natively.
	Please remove this if it's already in other code */
if(Array.prototype.push == null){
	Array.prototype.push = function(){
		for(var i = 0; i < arguments.length; i++){
			this[this.length] = arguments[i];
		};
		return this.length;
	};
};

/*	Event Cache uses an anonymous function to create a hidden scope chain.
	This is to prevent scoping issues. */
var EventCache = function(){
	var listEvents = [];
	
	return {
		listEvents : listEvents,
	
		add : function(node, sEventName, fHandler, bCapture){
			listEvents.push(arguments);
		},
	
		flush : function(){
			var i, item;
			for(i = listEvents.length - 1; i >= 0; i = i - 1){
				item = listEvents[i];
				
				if(item[0].removeEventListener){
					item[0].removeEventListener(item[1], item[2], item[3]);
				};
				
				/* From this point on we need the event names to be prefixed with 'on" */
				if(item[1].substring(0, 2) != "on"){
					item[1] = "on" + item[1];
				};
				
				if(item[0].detachEvent){
					item[0].detachEvent(item[1], item[2]);
				};
				
				item[0][item[1]] = null;
			};
		}
	};
}();

addEvent(window, 'unload', EventCache.flush, false);

function DisplaySingleItem(displayID,allIDs){
	var n = allIDs.length;
	for(i = 0; i < n; i = i + 1) {
		thisID = allIDs[i];
		thisItem = document.getElementById(thisID);
		if(thisID == displayID) {
			thisItem.style.display = "block";
		} else {
			thisItem.style.display = "none";
		}
	}
}

function ForceChangeEvent(elem) {
	// http://www.howtocreate.co.uk/tutorials/javascript/domevents#
	if( document.createEvent ) {
		// DOM-compliant browsers
		var evObj = document.createEvent('HTMLEvents');
		evObj.initEvent( 'change', true, false );
		elem.dispatchEvent(evObj);
	} else if( document.createEventObject ) {
		// IE
		elem.fireEvent('onchange');
	}
}

function ShadowWindow(cHTML) {
	return '<div class="winWrapper" id="winWrapper">' +
			 '<div class="winShadow" id ="winShadow">&nbsp;</div>' + 
			 '<div class="winContent" id ="winContent">' + cHTML +
			 '</div></div>';
}

function ShadowFix() {
	// This forces the correct height of the shadow - for <= IE6
	_winContent = document.getElementById("winContent");
	_winShadow = document.getElementById("winShadow");
	
	if (window.XMLHttpRequest) {
	} else{
		if (_winContent.offsetHeight) {
			_winShadow.style.height = _winContent.offsetHeight;
			_winShadow.style.width = _winContent.offsetWidth;
		}
	}
}

/* Support for Technihow Calendar */
var RE_NUM = /^\-?\d+$/;

var ARR_MONTHS = ["January", "February", "March", "April", "May", "June",
		"July", "August", "September", "October", "November", "December"];
// week day titles as they appear on the calendar
var ARR_WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
// day week starts from (normally 0-Su or 1-Mo)
var NUM_WEEKSTART = 1;
// path to the directory where calendar images are stored. trailing slash req.
var STR_ICONPATH = '/images/generic/';

var _calDiv = null;
var _calField = null;

function showCalendar(cDateFieldID) {
	_calDiv = document.getElementById("tcCalendar");
	
	if (!_calDiv) {
		// Calendar doesn't exist so create it
		_calDiv = document.createElement("div");
		_calDiv.setAttribute("id", "tcCalendar");
	  	_calDiv.className = "calendar";
	
		_calDiv.style.position = "absolute";
		_calDiv.style.width = 190;
		_calDiv.style.display = "block";
		document.getElementsByTagName("body")[0].appendChild(_calDiv);
	} else {
		// Calendar exists - check if its for this field
		if (_calField.id == cDateFieldID) {
			// It is, so we've clicked calendar again - lets kill it
			document.getElementsByTagName("body")[0].removeChild(_calDiv);
			return;
		}
	}

	_calField = document.getElementById(cDateFieldID);
	setRelativePosition(_calDiv, _calField, 160 , -4); 
	
	drawCalendar();
}	

function drawCalendar(xDate) {
	if (!xDate) {
		// No Date Passed - get date from attached field
		xDate = parseDate(_calField.value);
		
		if (!xDate) {
			// Must have been an error
			// Kill the calendar
			document.getElementsByTagName("body")[0].removeChild(_calDiv);
			return;
		}
	}
	
	dateParam = new Date(xDate);

	datePrevYear = new Date(xDate);
	datePrevYear.setFullYear(datePrevYear.getFullYear() - 1);
	
	dateNextYear = new Date(xDate);
	dateNextYear.setFullYear(dateNextYear.getFullYear() + 1);

	datePrevMonth = new Date(xDate);
	datePrevMonth.setMonth(datePrevMonth.getMonth() - 1);
	
	dateNextMonth = new Date(xDate);
	dateNextMonth.setMonth(dateNextMonth.getMonth() + 1);

	// get first day to display in the grid for current month
	dateFirstDay = new Date(xDate);
	dateFirstDay.setDate(1);
	dateFirstDay.setDate(1 - (7 + dateFirstDay.getDay() - NUM_WEEKSTART) % 7);

	var cHTML = '';
	cHTML += '<table class="calendar" cellspacing="0" width="100%">';
	
	// Top Row
	cHTML += '<tr><td colspan="7"><table class="calBody" cellspacing="0" width="100%"><tr class="calHeader">';
	cHTML += '<td class="calNav"><nobr><a href="javascript:drawCalendar('+datePrevYear.valueOf()+
			 ')"><img src="'+STR_ICONPATH+
			 'fast-backward.gif" width="16" height="16" border="0" alt="previous year" /></a>&nbsp;'+
			 '<a href="javascript:drawCalendar('+datePrevMonth.valueOf()+')"><img src="'+STR_ICONPATH+
			 'backward.gif" width="16" height="16" border="0" alt="previous month" /></a></nobr></td>';
	cHTML += '<td class="calNav" align="center" width="100%">'+ARR_MONTHS[dateParam.getMonth()]+
			 ' '+dateParam.getFullYear()+'</td>';
	cHTML += '<td class="calNav"><nobr><a href="javascript:drawCalendar('+dateNextMonth.valueOf()+
			 ')"><img src="'+STR_ICONPATH+
			 'forward.gif" width="16" height="16" border="0" alt="next month" /></a>&nbsp;'+
			 '<a href="javascript:drawCalendar('+dateNextYear.valueOf()+')"><img src="'+STR_ICONPATH+
			 'fast-forward.gif" width="16" height="16" border="0" alt="next year" /></a></nobr></td>';
	cHTML += '</tr></table></td></tr>';
	// End of Top Row

	// Weekday Titles
	cHTML += '<tr class="calHeader">';
	for (var n=0; n<7; n++) {
		cHTML += '<td class="calDays">' + 
				 ARR_WEEKDAYS[(NUM_WEEKSTART+n)%7] + '</td>';
	}
	cHTML += '</tr>';

	// Calendar Table
	var dateCurrent = new Date(dateFirstDay);
	while (dateCurrent.getMonth() == dateParam.getMonth() || dateCurrent.getMonth() == dateFirstDay.getMonth()) {
		cHTML += '<tr>';
		for (var n=0; n<7; n++) {
			if (dateCurrent.getDate() == dateParam.getDate() &&	dateCurrent.getMonth() == dateParam.getMonth()) {
				// Current Date
				cTDClass = "calCurrent"
			} else if (dateCurrent.getDay() == 0 || dateCurrent.getDay() == 6) {
				// Weekend
				cTDClass = "calWeekend"
			} else {
				// Working Day
				cTDClass = "calWorking"
			}
			cHTML += '<td class="' + cTDClass + '" align="center">' + 
					 '<a class="calDate" href="javascript:setCalendarDate('+dateCurrent.valueOf() +');">'
					 + dateCurrent.getDate() + '</a></td>';

			dateCurrent.setDate(dateCurrent.getDate()+1);
		}
		cHTML += '</tr>';
	}
	cHTML += '</table>';

	_calDiv.innerHTML = ShadowWindow(cHTML);
	ShadowFix();
}

function setCalendarDate(xDate) {
	var dateParam = new Date(xDate);
	var day = dateParam.getDate();
	var month = dateParam.getMonth();
	
	_calField.value = (((day < 10?'0':'')+day) + "-" + ((month<9?'0':'')+(month+1)) + "-" + dateParam.getFullYear());

	// Deals with triggering the change event listeners after updating the value of the date field
	ForceChangeEvent(_calField);

	document.getElementsByTagName("body")[0].removeChild(_calDiv);
}	
	
function parseDate(cDate) {
	// Deals with error-checking passed in date field,
	// and also resolves UK format - assumes dd/mm/yy or dd-mm-yy or dd/mm/yyyy or dd-mm-yyyy
	
	var cDateError = "Invalid date: '" + cDate + "'";
	var cDelim = '';
	
	if ((!cDate) || (cDate == '00-00-0000')) return new Date();
	
	if (cDate.indexOf('-') > 0) {
		cDelim = '-';
	} else if (cDate.indexOf('/') > 0) {
		cDelim = '/';
	} else {
		return displayError(cDateError);
	}
	
	var aDate = cDate.split(cDelim);
	
	if (aDate.length != 3) return displayError(cDateError);
	if (!aDate[0]) return displayError(cDateError);
	if (!RE_NUM.exec(aDate[0])) return displayError(cDateError);
	if (!aDate[1]) return displayError(cDateError);
	if (!RE_NUM.exec(aDate[1])) return displayError(cDateError);
	if (!aDate[2]) return displayError(cDateError);
	if (!RE_NUM.exec(aDate[2])) return displayError(cDateError);

	var dateResult = new Date();
	dateResult.setDate(1);

	if (aDate[1] < 1 || aDate[1] > 12) return displayError(cDateError);
	dateResult.setMonth(aDate[1]-1);
	 
	if (aDate[2] < 100) aDate[2] = Number(aDate[2]) + (aDate[2] < 50 ? 2000 : 1900);
	dateResult.setFullYear(aDate[2]);

	dateResult.setDate(aDate[0]);
	if (dateResult.getMonth() != (aDate[1]-1)) return displayError(cDateError);

	return (dateResult);
}

function displayError(cMessage) {
	alert (cMessage);
	return null;
}
