/* Global variables */
var isNav4, isNav6, isIE4, hasDOM;
var intervalID;
var coll = "";
var styleObj = "";
if(parseInt(navigator.appVersion) >= 5 && document.getElementsByTagName("*")) {
	hasDOM = true;
	coll = 'getElementById("';
	styleObj = '").style';
	if(navigator.appName == "Netscape") {
		isNav6 = true;
	}
}else if(parseInt(navigator.appVersion) >= 4){
	if(navigator.appName == "Netscape") {
		isNav4 = true;
	} else {
		isIE4 = true;
		coll = "all.";
		styleObj = ".style";
	}
}

var agt=navigator.userAgent.toLowerCase();
var isMac = (agt.indexOf("mac")!=-1);

var agentP = navigator.userAgent;
var macIE=false;
if ((agentP.indexOf("Mac")!=-1) && isIE4) {
  macIE=true;
}

function addWindowLoadEvent(func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
  }
}

function popUp(theURL,name,width,height,more) {
		var popup=window.open(theURL,name,'width='+width+',height='+height+','+more);
		popup.moveTo((screen.width-width)/2, (screen.height-height)/2);
		popup.focus();
}

function isValidEmail(email)
{
	var x = email;
	//var filter  = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9])+$/;
	var filter  = /^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
	if (filter.test(x)) return true;
	else return false;
}

function insertImage(sSRC) {
	return "<IMG SRC='" + sSRC + "' BORDER='0'>"
}

function writeFlashContent(swf, width, height) {
	
	var strOut="";
	
	strOut =  "<object classid='clsid:D27CDB6E-AE6D-11cf-96B8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=4,0,2,0' width='" + width + "' height='" + height + "' id='" + swf + "'><param name='movie' value='" + swf + "'><param name='quality' value='high'><embed src='" + swf + "' quality=high pluginspage='http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash' type='application/x-shockwave-flash' width='" + width + "' height='" + height + "'></embed></object>"

	return strOut;
}

function ToggleDivVisibility(divRef) {
	if (document.getElementById(divRef).style.visibility == "visible")
		hide(divRef);
	else
		show(divRef);
}

function ToggleDivDisplay(divRef) {
	if (document.getElementById(divRef).style.display == "block")
		hideDisplay(divRef);
	else
		showDisplay(divRef);
}

//This function is used to store a caret position of a textbox and to insert text at the caret position of a textbox
//Note: To use these functions: ensure the textarea input control contains the attributes:
//								ONSELECT="storeTextBoxCaret(this);"
//								ONCLICK="storeTextBoxCaret(this);"
//								ONKEYUP="storeTextBoxCaret(this);"
//		To execute the insert at the carat, for example if a button where to perform this action, then ensure the button contains the attribute:
//								ONCLICK="insertAtTextBoxCaret(this.form.aTextArea, this.form.aText.value);"
function storeTextBoxCaret (txtTextBox) {
	if (txtTextBox.createTextRange)
		txtTextBox.caretPos = document.selection.createRange().duplicate();
}
function insertAtTextBoxCaret (txtTextBox, text) {
	if (txtTextBox.createTextRange && txtTextBox.caretPos) {
		var caretPos = txtTextBox.caretPos;
		caretPos.text = caretPos.text.charAt(caretPos.text.length - 1) == ' ' ? text + ' ' : text;
	}
	else
		txtTextBox.value  += text;
}

//This function shifts one table row at a time in a specific direction
function shiftTableRow(tblId, direction, fldCellToSwap) {
    var tblRef = document.getElementById(tblId);
    
    rowIndex = parseInt(window.event.srcElement.parentElement.parentElement.index);
    if((rowIndex == 0 && direction > 0) || (rowIndex == tblRef.rows.length - 1 && direction < 0) || (rowIndex > 0 && rowIndex < tblRef.rows.length - 1)) {
		if(tblRef.rows[rowIndex].cells[fldCellToSwap].swapNode) {
			tblRef.rows[rowIndex + direction].cells[fldCellToSwap].swapNode(tblRef.rows[rowIndex].cells[fldCellToSwap]);
		}
		else {
			swapHTML = tblRef.rows[rowIndex + direction].cells[fldCellToSwap].innerHTML;
			tblRef.rows[rowIndex + direction].cells[fldCellToSwap].innerHTML = tblRef.rows[rowIndex].cells[fldCellToSwap].innerHTML;
			tblRef.rows[rowIndex].cells[fldCellToSwap].innerHTML = swapHTML;
		}
    }
}

//This function takes in an ordered list of table row id's and sets each row to their respective position
function shiftTableRows(tblId, tblPositionIds, fldCellToSwap) {
	var tblRef = document.getElementById(tblId);
	var arrPositionIds = tblPositionIds.split(',');
	
	for(i=0; i<=tblRef.rows.length-1; i++) {
		var curRow = tblRef.rows[i];
		var curRowId = curRow.id;
		
		for(j=0; j<=arrPositionIds.length-1; j++) {
			if (curRowId == arrPositionIds[j]) {
				curRow.cells[fldCellToSwap].swapNode(tblRef.rows[j].cells[fldCellToSwap]);
				
				curRow.id = tblRef.rows[j].id;
				tblRef.rows[j].id = curRowId;
				
				break;
			}
		}
	}
}

function positionElement_CentreScreen(element) {
    
    if (element) {
        element.style.display = "block";
        element.style.position = "absolute";
        var IpopLeft = (document.documentElement.scrollWidth - element.offsetWidth) / 2;
        if (IpopLeft < 0) { IpopLeft = 0; }
        element.style.left = IpopLeft + "px";

        var IpopTop = (document.documentElement.clientHeight - element.offsetHeight) / 2
        + ((document.documentElement && document.documentElement.scrollTop) ? document.documentElement.scrollTop : document.body.scrollTop);
        if (IpopTop < 0) { IpopTop = 0; }
        element.style.top = IpopTop + "px";
        ;
    }

    /*
    var scrollTop = document.body.scrollTop;
    var scrollLeft = document.body.scrollLeft;
    var viewPortHeight = document.body.clientHeight;
    var viewPortWidth = document.body.clientWidth;
    if (document.compatMode == "CSS1Compat") {
        viewPortHeight = document.documentElement.clientHeight;
        viewPortWidth = document.documentElement.clientWidth;
        //if (!navigator.userAgent.indexOf("Safari") > -1) {
            scrollTop = document.documentElement.scrollTop;
            scrollLeft = document.documentElement.scrollLeft;
        //}
    }
    var topOffset = Math.ceil(viewPortHeight / 2 - element.offsetHeight / 2);
    var leftOffset = Math.ceil(viewPortWidth / 2 - element.offsetWidth / 2);

    var elementWidth = parseInt(element.style.width);
    var elementHeight = parseInt(element.style.height);

    if (isNaN(elementWidth)) {
        elementWidth = 0;
    }

    if (isNaN(elementHeight)) {
        elementHeight = 0;
    }
    
    var top = scrollTop + topOffset - (elementHeight / 2);
    var left = scrollLeft + leftOffset - (elementWidth / 2);
    element.style.position = "absolute";
    element.style.top = top + "px";
    element.style.left = left + "px";
    */
}

//To enable, set: rcbCombo.OnClientBlur = "RadComboBox_EnsureItemTextExists_OnClientBlur" for a RadComboBox
//This will ensure that the ComboBox will only contain a valid item (by resetting text and value to empty string if invalid), however a postback may still occur
function RadComboBox_EnsureItemTextExists_OnClientBlur(sender, eventArgs) {
    var textInTheCombo = sender.get_text();
    var item           = sender.findItemByText(textInTheCombo);
    var inputElement   = sender.get_inputDomElement();

    //if there is no item with that text
    if (!item) {
        sender.set_text("");
        sender.set_value("");

        /*
        setTimeout(function () {
            inputElement.focus();
            inputElement.style.backgroundColor = "red";
        }, 20);
        */
    }
}

function getWindowSize () {
    theHeight = ( typeof( window.innerHeight ) == 'number' )?window.innerHeight:( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )?document.documentElement.clientHeight:document.body.clientHeight;
    theWidth = ( typeof( window.innerWidth ) == 'number' )?window. innerWidth:( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )?document.documentElement.clientWidth:document.body. clientWidth;
    return [ scrOfX, scrOfY ];
}

function renderProductSearchScript(curPath, resultListCreatedFromSearchQualifier, resultListCreatedFromSearchValue, searchTermQualifier, productCategoryQualifier, resultsPerPageQualifier, txtSearchTerm, productCategoryDropDownListId, resultsPerPageDropDownListId, arrProductPropertiesDropDownClientIds, arrProductPropertiesDropDownDataBaseIds, sortOrderQualifier, sortOrderDropDownListId) {
	var searchURL;

	//Note: For this to function correctly, ensure the SearchResults Product Search control is rendered to the screen (so that document.getElementById(txtSearchTerm) is valid),
	//      and that productCategoryDropDownListId is also rendered to the screen (so that document.getElementById(productCategoryDropDownListId) is valid) [however both can be hidden with CSS]
	//      That is, ensure: SearchControl_Product_SearchTerm_Visible="True" and SearchControl_Product_ProductCategory_Visible = "True"

	searchURL  = curPath + '?' + resultListCreatedFromSearchQualifier + '=' + resultListCreatedFromSearchValue;
	searchURL += '&' + searchTermQualifier + '=' + ((document.getElementById(txtSearchTerm) != null) ? escape(document.getElementById(txtSearchTerm).value) : "");
	searchURL += '&' + productCategoryQualifier + '=' + ((document.getElementById(productCategoryDropDownListId) != null) ? document.getElementById(productCategoryDropDownListId).options[document.getElementById(productCategoryDropDownListId).selectedIndex].value : "");
	searchURL += '&' + resultsPerPageQualifier + '=' + ((document.getElementById(resultsPerPageDropDownListId) != null) ? document.getElementById(resultsPerPageDropDownListId).options[document.getElementById(resultsPerPageDropDownListId).selectedIndex].value : "");

	if (sortOrderQualifier) {
	    searchURL += '&' + sortOrderQualifier + '=' + ((document.getElementById(sortOrderDropDownListId) != null) ? document.getElementById(sortOrderDropDownListId).options[document.getElementById(sortOrderDropDownListId).selectedIndex].value : "");
	}

	for(i=0; i<arrProductPropertiesDropDownClientIds.length; i++) {
		searchURL += '&' + arrProductPropertiesDropDownDataBaseIds[i] + '=' + document.getElementById(arrProductPropertiesDropDownClientIds[i]).options[document.getElementById(arrProductPropertiesDropDownClientIds[i]).selectedIndex].value;
	}

	return searchURL;
}

function getCoordinate(sCollection, delimiter, element) {
	var aCollection = sCollection.split(delimiter);
	return aCollection[element-1];
}

function changeImage(row,s){
	var h = (s==1)?"r":"";
	if(document.images){
		document.images[row].src = eval(h + row + ".src");
	}
}

/*Function to convert object name string or object reference into valid object reference */
function getObjectRef(obj){
	var theObj;
	if(typeof obj == "string"){
		theObj = eval("document." + coll + obj + styleObj);
	}else{
		theObj = obj;
	}
	return theObj;
}

/*Function to convert object name string or object reference into valid object reference */
/* modified to work with nested layers in Netscape 4, if you have more than 8 nested layers, you're insane */
function getNestObjectRef(obj,nst1,nst2,nst3,nst4,nst5,nst6,nst7,nst8){
	var theObj;
	var objNest = '';
	if(typeof obj == "string"){
		theObj = eval("document." + coll + obj + styleObj);
	}else{
		theObj = obj;
	}
	if(isNav4){
		for(i=1;i<=arguments.length-1;i++){
			currArg = eval('nst'+i);
			objNest += 'document.'+currArg+'.';
		}
		theObj = eval(objNest + 'document.' + obj);
	}
	return theObj;
}

/* functions to show an object */
function showDisplay(obj){
	var theObj = getObjectRef(obj);
	theObj.display = "block";
}

/* utility function to hide an object */
function hideDisplay(obj){
	var theObj = getObjectRef(obj);
	if(theObj)theObj.display = "none";
}

/* functions to show an object */
function show(obj){
	var theObj = getObjectRef(obj);
	theObj.visibility = "visible";
}

/* utility function to hide an object */
function hide(obj){
	var theObj = getObjectRef(obj);
	if(theObj)theObj.visibility = "hidden";
}

/* utility function that sets the visibility of an object */
function showHide(obj,v){
	var theObj = getObjectRef(obj);
	if(v == 1){
		theObj.visibility = "visible";
	}else{
		theObj.visibility = "hidden";
	}
}

//Assumes this script has been rendered: <scr ipt src="http://maps.google.com/maps?file=api&v=2&key={key}" type="text/javascript"></scr ipt>
//Note: {key} is stored in: [Shared].ApplicationVariable(Constants.DATABASE_FIELDMAPPING_SITE_Provider_Google_MapKey)
function renderGoogleMap(lat, lon, divMap) {

    if (GBrowserIsCompatible()) {
        var map = new GMap2(document.getElementById(divMap));

        map.addControl(new GSmallMapControl());
        map.addControl(new GMapTypeControl());

        var geocoder = new GClientGeocoder();

        map.setCenter(new GLatLng(lat, lon), 16);

        var marker = new GMarker(new GLatLng(lat, lon));
        map.addOverlay(marker);
        GEvent.addListener(marker, "click", function () {

        });

    }
}

function getQuerystringParameterByName(name) {
    name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
    var regexS = "[\\?&]" + name + "=([^&#]*)";
    var regex = new RegExp(regexS);
    var results = regex.exec(window.location.href);
    if (results == null)
        return "";
    else
        return decodeURIComponent(results[1].replace(/\+/g, " "));
}

/* utility fn to set x coordinate of positionable object */
function setObjLeft(obj,x){
	var theObj = getObjectRef(obj);
	if(isNav4||hasDOM){
		theObj.left = x;
	}else{
		theObj.pixelLeft = x;
	}
}


/* utility fn to set y coordinate of positionable object */
function setObjTop(obj,y){
	var theObj = getObjectRef(obj);
	if(isNav4||hasDOM){
		theObj.top = y;
	}else{
		theObj.pixelTop = y;
	}
}

/* function to retrieve the y-coordinate of a positionable object */
function getObjTop(obj){
	var theObj = getObjectRef(obj);
	if(isNav4){
		return theObj.top;
	}else if(hasDOM){
		return parseInt(theObj.top);
	}else{
		return theObj.pixelTop;
	}
}

/* utility function that returns the width of an object in pixels */
function getObjWidth(obj){
	var theObj = getObjectRef(obj);
	if(isNav4){
		return theObj.clip.width;
	}else if(hasDOM){
		return parseInt(theObj.width);
	} else {
		var ox = theObj.pixelWidth;
		return ox;
	}
}

/* utility function that returns the height of the object in pixels */
function getObjHeight(obj){
	var theObj = getObjectRef(obj);
	if(isNav4){
		return theObj.clip.height;
	}else{
		return theObj.clientHeight;
	}
}

/* function to retrieve the x-coordinate of a positionable object */
function getObjLeft(obj){
	var theObj = getObjectRef(obj);
	if(isNav4){
		return theObj.left;
	}else if(hasDOM){
		return parseInt(theObj.left);
	}else{
		var opx1 = theObj.pixelLeft;
		return opx1;
	}
}

/* utility function that moves the specified object by dx pixels to the left;*/
function shiftLeft(obj,dx){
	var theObj = getObjectRef(obj);
	if(isNav4){
		theObj.left -= dx;
	}else if(hasDOM){
		theObj.left = parseInt(theObj.left)-dx;
	}else{
		theObj.pixelLeft -= dx;
	}	
}

/* function to position an object at a specified pixel co-ordinate */
function shiftTo(obj,x,y){
	var theObj = getObjectRef(obj);
	if(isNav4){
		theObj.moveTo(x,y);
	}
	else if(isNav6) {
		theObj.left=x;
		theObj.top=y;
	}
	else{
		theObj.pixelLeft = x;
		theObj.pixelTop = y;
	}
}

/* object clipping function */
function clipObj(obj,cTop,cRight,cBottom,cLeft) {
	var theObj = getObjectRef(obj);
	if(isNav4) {
		theObj.clip.top = cTop;
		theObj.clip.right = cRight;
		theObj.clip.bottom = cBottom;
		theObj.clip.left = cLeft;
	}else {
		theObj.clip = 'rect('+cTop+','+cRight+','+cBottom+','+cLeft+')';
	}
}

/* utility function that returns the available content width space in the browser window */
function getInsideWindowWidth(){
	if(isNav4||isNav6) {
		return window.innerWidth;
	} else {
		var wx = document.body.clientWidth;
		return wx;
	}
}

/* utility function that returns the available content height space in the browser window */
function getInsideWindowHeight(){
	if(isNav4||isNav6) {
		return window.innerHeight;
	} else {
		var wx = document.body.clientHeight;
		return wx;
	}
}

function getLyrHeight(obj){
	var theObj = getObjectRef(obj);
	if(isNav4){
		return theObj.clip.height;
	}else{
		return eval('document.all.' + obj + '.scrollHeight');
	}
}

//This function will set objImg.src to imgSrc
function setImageSrc(objImg, imgSrc) {
	if (!objImg=="")
		window.document.images[objImg].src=imgSrc;
}

//Generic Hex Functions
var hexValues = new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","a","b","c","d","e","f");
var intValues = new Array("0","1","2","3","4","5","6","7","8","9");

function validChar(sChar, availChars) {
	for (j = 0; j < availChars.length; j++) {
		if (sChar == availChars[j]) {
			return true;
	   }
	}
	return false;
}
function isHex(string) {
	string = string.replace("#", "");
	
	if (string.length != 6) { 
		return false;
	}
	for (k = 0; k < 6; k++) {
		if (! validChar(string.charAt(k), hexValues)) {
			return false;
	   }
	}
	return true;
}
function changeColor(elementID, sCol) {
	sCol = sCol.toUpperCase();
	sCol = sCol.replace('#', '');
		
	if (isHex(sCol)) {
		eval(elementID + '.style').backgroundColor = sCol;
	}
}

// Since intValues does not contain a period (.) character, this function will return
// whether or not the value is purely numeric (ie: an Integer or not)
function isNumeric(string) {
	if (string.length == 0) { 
		return false;
	}
	for (k = 0; k < string.length; k++) {
		if (! validChar(string.charAt(k), intValues)) {
			return false;
	   }
	}
	return true;
}

function isEmpty(string) {
	if ((string.length==0) || (string==null)) {
		return true;
	}
	else { 
		return false;
	}
}

function validateQuantityInput(sQuantity, sIdentifier) {
	if (isNumeric(sQuantity) && parseInt(sQuantity) > 0) {
		return true;
	}
	else {
		alert('An invalid ' + sIdentifier + ' was entered');
		return false;
	}
}

function pxToNum(pxStr)
{ // pxStr == 27px, we want 27.
	if (pxStr.length > 2) {
		n = Number(pxStr.substr(0, pxStr.length-2));
		return(n);
	}
	return(0);
}

//Change a forms action property
function changeAction(frmName, newAction) {
	var objForm;
	objForm=eval('document.' + frmName);
	objForm.action=newAction;
}

//Change a forms field property
function changeFormField(frmName, fieldName, newValue) {
	var objForm;
	objForm=eval('document.forms["' + frmName + '"].elements["' + fieldName + '"]');
	objForm.value=newValue;
}

function disableForm(theform) {
	if (document.all || document.getElementById) {
		for (i = 0; i < theform.length; i++) {
			var tempobj = theform.elements[i];
			if (tempobj.type.toLowerCase() == "submit" || tempobj.type.toLowerCase() == "image" || tempobj.type.toLowerCase() == "reset")
				tempobj.disabled = true;
		}
	}
}

function elementExists(sCollection, delimiter, element) {
	var found = false;
	var aCollection = sCollection.split(delimiter);
	for (var i=0; i<aCollection.length; i++) {
		if (element == aCollection[i]) {
			found = true;
			break;
		}
	}
	
	return found;
}

function dynamicLayerWrite(txt,layerid) {
	if (isNav6)
	{
		var x = document.getElementById(layerid);
		if(x)x.innerHTML = txt;
	}
	else if (isIE4)
	{
		var x = document.all[layerid];
		if(x)x.innerHTML = txt;
	}
}

function dynamicLayerWriteWindowOpener(txt,layerid) {
	if (isNav6)
	{
		var x = window.opener.document.getElementById(layerid);
		if(x)x.innerHTML = txt;
	}
	else if (isIE4)
	{
		var x = window.opener.document.all[layerid];
		if(x)x.innerHTML = txt;
	}
}

//Check or uncheck a range of checkboxes
var allChecked=false;		
function runCheck(frmName) {
	if (allChecked==true) {
		allChecked=false
		chkOnOff=false
	}
	else {
		allChecked=true
		chkOnOff=true
	}
			
	checkAll(frmName, chkOnOff);	
}

function checkAll(field, chkOnOff) {
	for (i = 0; i < field.length; i++)
		field[i].checked = chkOnOff ;
}

//Given a txt, this will find the item in the list with the text txt and set it to selected
function setDropDownSelectedItemByName(dropDown, txt) {
	var i;
	var curSelectedIndex = -1;
	
	for (i=0;i<dropDown.options.length;i++) {
		if (dropDown.options[i].value == txt) {
			curSelectedIndex = i;
			break;
		}
	}
	
	if (curSelectedIndex >= 0)
		dropDown.selectedIndex = curSelectedIndex;
}


function MM_swapImgRestore() { //v3.0
  var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
}
function MM_preloadImages() { //v3.0
  var d=document; if(d.images){ if(!d.MM_p) d.MM_p=new Array();
	var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++)
	if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
}

function MM_findObj(n, d) { //v4.01
  var p,i,x;  if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
	d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
  if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
  for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
  if(!x && d.getElementById) x=d.getElementById(n); return x;
}

function MM_swapImage() { //v3.0
  var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
   if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
}




/*
============================================================================
Begin: Table Drag and Drop
============================================================================

'Initialise
	<script type="text/javascript">
		var table = document.getElementById('table-1');
		var tableDnD = new TableDnD();
		tableDnD.init(table);

		// Redefine the onDrop so that we can display something
		tableDnD.onDrop = function(table, row) {
			var rows = this.table.tBodies[0].rows;
			var debugStr = "rows now: ";
			for (var i=0; i<rows.length; i++) {
				debugStr += rows[i].id+" ";
			}
			document.getElementById(’debug’).innerHTML = 'row['+row.id+'] dropped<br>'+debugStr;
		}
	</script>

*/

/** Keep hold of the current table being dragged */
var currenttable = null;

/** Capture the onmousemove so that we can see if a row from the current
 *  table if any is being dragged.
 * @param ev the event (for Firefox and Safari, otherwise we use window.event for IE)
 */
document.onmousemove = function(ev){
    if (currenttable && currenttable.dragObject) {
        ev   = ev || window.event;
        var mousePos = currenttable.mouseCoords(ev);
        var y = mousePos.y - currenttable.mouseOffset.y;
        if (y != currenttable.oldY) {
            // work out if we're going up or down...
            var movingDown = y > currenttable.oldY;
            // update the old value
            currenttable.oldY = y;
            // update the style to show we're dragging
            currenttable.dragObject.style.backgroundColor = "#eee";
            // If we're over a row then move the dragged row to there so that the user sees the
            // effect dynamically
            var currentRow = currenttable.findDropTargetRow(y);
            if (currentRow) {
                if (movingDown && currenttable.dragObject != currentRow) {
                    currenttable.dragObject.parentNode.insertBefore(currenttable.dragObject, currentRow.nextSibling);
                } else if (! movingDown && currenttable.dragObject != currentRow) {
                    currenttable.dragObject.parentNode.insertBefore(currenttable.dragObject, currentRow);
                }
            }
        }

        return false;
    }
}

// Similarly for the mouseup
document.onmouseup   = function(ev){
    if (currenttable && currenttable.dragObject) {
        var droppedRow = currenttable.dragObject;
        // If we have a dragObject, then we need to release it,
        // The row will already have been moved to the right place so we just reset stuff
        droppedRow.style.backgroundColor = 'transparent';
        currenttable.dragObject   = null;
        // And then call the onDrop method in case anyone wants to do any post processing
        currenttable.onDrop(currenttable.table, droppedRow);
        currenttable = null; // let go of the table too
    }
}


/** get the source element from an event in a way that works for IE and Firefox and Safari
 * @param evt the source event for Firefox (but not IE--IE uses window.event) */
function getEventSource(evt) {
    if (window.event) {
        evt = window.event; // For IE
        return evt.srcElement;
    } else {
        return evt.target; // For Firefox
    }
}

/**
 * Encapsulate table Drag and Drop in a class. We'll have this as a Singleton
 * so we don't get scoping problems.
 */
function TableDnD() {
    /** Keep hold of the current drag object if any */
    this.dragObject = null;
    /** The current mouse offset */
    this.mouseOffset = null;
    /** The current table */
    this.table = null;
    /** Remember the old value of Y so that we don't do too much processing */
    this.oldY = 0;

    /** Initialise the drag and drop by capturing mouse move events */
    this.init = function(table) {
        this.table = table;
        var rows = table.tBodies[0].rows; //getElementsByTagName("tr")
        for (var i=0; i<rows.length; i++) {
			// John Tarr: added to ignore rows that I've added the NoDnD attribute to (Category and Header rows)
			var nodrag = rows[i].getAttribute("NoDrag")
			if (nodrag == null || nodrag == "undefined") { //There is no NoDnD attribute on rows I want to drag
				this.makeDraggable(rows[i]);
			}
        }
    }

    /** This function is called when you drop a row, so redefine it in your code
        to do whatever you want, for example use Ajax to update the server */
    this.onDrop = function(table, droppedRow) {
        // Do nothing for now
    }

	/** Get the position of an element by going up the DOM tree and adding up all the offsets */
    this.getPosition = function(e){
        var left = 0;
        var top  = 0;
		/** Safari fix -- thanks to Luis Chato for this! */
		if (e.offsetHeight == 0) {
			/** Safari 2 doesn't correctly grab the offsetTop of a table row
			    this is detailed here:
			    http://jacob.peargrove.com/blog/2006/technical/table-row-offsettop-bug-in-safari/
			    the solution is likewise noted there, grab the offset of a table cell in the row - the firstChild.
			    note that firefox will return a text node as a first child, so designing a more thorough
			    solution may need to take that into account, for now this seems to work in firefox, safari, ie */
			e = e.firstChild; // a table cell
		}

        while (e.offsetParent){
            left += e.offsetLeft;
            top  += e.offsetTop;
            e     = e.offsetParent;
        }

        left += e.offsetLeft;
        top  += e.offsetTop;

        return {x:left, y:top};
    }

	/** Get the mouse coordinates from the event (allowing for browser differences) */
    this.mouseCoords = function(ev){
        if(ev.pageX || ev.pageY){
            return {x:ev.pageX, y:ev.pageY};
        }
        return {
            x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
            y:ev.clientY + document.body.scrollTop  - document.body.clientTop
        };
    }

	/** Given a target element and a mouse event, get the mouse offset from that element.
		To do this we need the element's position and the mouse position */
    this.getMouseOffset = function(target, ev){
        ev = ev || window.event;

        var docPos    = this.getPosition(target);
        var mousePos  = this.mouseCoords(ev);
        return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
    }

	/** Take an item and add an onmousedown method so that we can make it draggable */
    this.makeDraggable = function(item) {
        if(!item) return;
        var self = this; // Keep the context of the TableDnd inside the function
        item.onmousedown = function(ev) {
            // Need to check to see if we are an input or not, if we are an input, then
            // return true to allow normal processing
            var target = getEventSource(ev);
            if (target.tagName == 'INPUT' || target.tagName == 'SELECT') return true;
            currenttable = self;
            self.dragObject  = this;
            self.mouseOffset = self.getMouseOffset(this, ev);
            return false;
        }
        item.style.cursor = "move";
    }

    /** We're only worried about the y position really, because we can only move rows up and down */
    this.findDropTargetRow = function(y) {
        var rows = this.table.tBodies[0].rows;
		for (var i=0; i<rows.length; i++) {
			var row = rows[i];
			// John Tarr added to ignore rows that I've added the NoDnD attribute to (Header rows)
			var nodrop = row.getAttribute("NoDrop");
			if (nodrop == null || nodrop == "undefined") {  //There is no NoDnD attribute on rows I want to drag
				var rowY    = this.getPosition(row).y;
				var rowHeight = parseInt(row.offsetHeight)/2;
				if (row.offsetHeight == 0) {
					rowY = this.getPosition(row.firstChild).y;
					rowHeight = parseInt(row.firstChild.offsetHeight)/2;
				}
				// Because we always have to insert before, we need to offset the height a bit
				if ((y > rowY - rowHeight) && (y < (rowY + rowHeight))) {
					// that's the row we're over
					return row;
				}
			}
		}
		return null;
	}
}

/*
============================================================================
End: Table Drag and Drop
============================================================================
*/


