/*
  dragtable v1.0
  June 26, 2008
  Dan Vanderkam, http://danvk.org/dragtable/
                 http://code.google.com/p/dragtable/

  Instructions:
    - Download this file
    - Add <script src="dragtable.js"></script> to your HTML.
    - Add class="draggable" to any table you might like to reorder.
    - Drag the headers around to reorder them.

  This is code was based on:
    - Stuart Langridge's SortTable (kryogenix.org/code/browser/sorttable)
    - Mike Hall's draggable class (http://www.brainjar.com/dhtml/drag/)
    - A discussion of permuting table columns on comp.lang.javascript

  Licensed under the MIT license.
 */

// Here's the notice from Mike Hall's draggable script:
//*****************************************************************************
// Do not remove this notice.
//
// Copyright 2001 by Mike Hall.
// See http://www.brainjar.com for terms of use.
//*****************************************************************************
dragtable = {
	// How far should the mouse move before it's considered a drag, not a click?
	dragRadius2 : 100,
	setMinDragDistance : function(x) {
		dragtable.dragRadius2 = x * x;
	},

	// How long should cookies persist? (in days)
	cookieDays : 365,
	setCookieDays : function(x) {
		dragtable.cookieDays = x;
	},

	// Determine browser and version.
	// TODO: eliminate browser sniffing except where it's really necessary.
	Browser : function() {
		var ua, s, i;

		this.isIE = false;
		this.isNS = false;
		this.version = null;
		ua = navigator.userAgent;

		s = "MSIE";
		if ((i = ua.indexOf(s)) >= 0) {
			this.isIE = true;
			this.version = parseFloat(ua.substr(i + s.length));
			return;
		}

		s = "Netscape6/";
		if ((i = ua.indexOf(s)) >= 0) {
			this.isNS = true;
			this.version = parseFloat(ua.substr(i + s.length));
			return;
		}

		// Treat any other "Gecko" browser as NS 6.1.
		s = "Gecko";
		if ((i = ua.indexOf(s)) >= 0) {
			this.isNS = true;
			this.version = 6.1;
			return;
		}
	},
	browser : null,

	// Detect all draggable tables and attach handlers to their headers.
	init : function() {
		// Don't initialize twice
		if (arguments.callee.done)
			return;
		arguments.callee.done = true;
		if (_dgtimer)
			clearInterval(_dgtimer);
		if (!document.createElement || !document.getElementsByTagName)
			return;

		dragtable.dragObj.zIndex = 0;
		dragtable.browser = new dragtable.Browser();
		forEach(document.getElementsByTagName('table'), function(table) {
			if (table.className.search(/\bdraggable\b/) != -1) {
				dragtable.makeDraggable(table);
			}
		});

	},

	// The thead business is taken straight from sorttable.
	makeDraggable : function(table) {
		if (table.getElementsByTagName('thead').length == 0) {
			the = document.createElement('thead');
			the.appendChild(table.rows[0]);
			table.insertBefore(the, table.firstChild);
		}

		// Safari doesn't support table.tHead, sigh
		if (table.tHead == null) {
			table.tHead = table.getElementsByTagName('thead')[0];
		}

		var headers = table.tHead.rows[0].cells;
		for ( var i = 0; i < headers.length; i++) {
			headers[i].onmousedown = dragtable.dragStart;
		}

		// Replay reorderings from cookies if there are any.
		if (dragtable.cookiesEnabled() && table.id
				&& table.className.search(/\bforget-ordering\b/) == -1) {
			dragtable.replayDrags(table);
		}

		// add event to add column
		var divId = table.up('div');
		var selectId = divId.down('select');
		selectId.options[0].selected = true;
		selectId.observe('change', zfy);

		// add click events to remove columns from view
		var link;
		var attrs = {
			src : zfurl + '../images/remove.png',
			height : '12px',
			align : 'absmiddle'
		};
		table.select('th').each(function(item) {
			if (item.nextSiblings().length >= 0) {
				link = new Element('img', attrs);
				item.insert(link);
				link.observe('click', zfx);
			}
		}

		)

	},

	// Global object to hold drag information.
	dragObj : new Object(),

	// Climb up the DOM until there's a tag that matches.
	findUp : function(elt, tag) {
		do {
			if (elt.nodeName && elt.nodeName.search(tag) != -1)
				return elt;
		} while (elt = elt.parentNode);
		return null;
	},

	// clone an element, copying its style and class.
	fullCopy : function(elt, deep) {
		var new_elt = elt.cloneNode(deep);
		new_elt.className = elt.className;
		forEach(elt.style, function(value, key, object) {
			if (value == null)
				return;
			if (typeof (value) == "string" && value.length == 0)
				return;

			new_elt.style[key] = elt.style[key];
		});
		return new_elt;
	},

	eventPosition : function(event) {
		var x, y;
		if (dragtable.browser.isIE) {
			x = window.event.clientX + document.documentElement.scrollLeft
					+ document.body.scrollLeft;
			y = window.event.clientY + document.documentElement.scrollTop
					+ document.body.scrollTop;
			return {
				x : x,
				y : y
			};
		}
		return {
			x : event.pageX,
			y : event.pageY
		};
	},

	// Determine the position of this element on the page. Many thanks to Magnus
	// Kristiansen for help making this work with "position: fixed" elements.
	absolutePosition : function(elt, stopAtRelative) {
		var ex = 0, ey = 0;
		do {
			var curStyle = dragtable.browser.isIE ? elt.currentStyle : window
					.getComputedStyle(elt, '');
			var supportFixed = !(dragtable.browser.isIE && dragtable.browser.version < 7);
			if (stopAtRelative && curStyle.position == 'relative') {
				break;
			} else if (supportFixed && curStyle.position == 'fixed') {
				// Get the fixed el's offset
				ex += parseInt(curStyle.left, 10);
				ey += parseInt(curStyle.top, 10);
				// Compensate for scrolling
				ex += document.body.scrollLeft;
				ey += document.body.scrollTop;
				// End the loop
				break;
			} else {
				ex += elt.offsetLeft;
				ey += elt.offsetTop;
			}
		} while (elt = elt.offsetParent);
		return {
			x : ex,
			y : ey
		};
	},

	// MouseDown handler -- sets up the appropriate mousemove/mouseup handlers
	// and fills in the global dragtable.dragObj object.
	dragStart : function(event, id) {
		var el;
		var x, y;
		var dragObj = dragtable.dragObj;

		var browser = dragtable.browser;
		if (browser.isIE)
			dragObj.origNode = window.event.srcElement;
		else
			dragObj.origNode = event.target;
		var pos = dragtable.eventPosition(event);

		// Drag the entire table cell, not just the element that was clicked.
		dragObj.origNode = dragtable.findUp(dragObj.origNode, /T[DH]/);

		// Since a column header can't be dragged directly, duplicate its
		// contents
		// in a div and drag that instead.
		// TODO: I can assume a tHead...
		var table = dragtable.findUp(dragObj.origNode, "TABLE");
		dragObj.table = table;
		dragObj.startCol = dragtable.findColumn(table, pos.x);
		if (dragObj.startCol == -1)
			return;

		var new_elt = dragtable.fullCopy(table, false);
		new_elt.style.margin = '0';

		// Copy the entire column
		var copySectionColumn = function(sec, col) {
			var new_sec = dragtable.fullCopy(sec, false);
			forEach(sec.rows, function(row) {
				var cell = row.cells[col];
				var new_tr = dragtable.fullCopy(row, false);
				if (row.offsetHeight)
					new_tr.style.height = row.offsetHeight + "px";
				var new_td = dragtable.fullCopy(cell, true);
				if (cell.offsetWidth)
					new_td.style.width = cell.offsetWidth + "px";
				new_tr.appendChild(new_td);
				new_sec.appendChild(new_tr);
			});
			return new_sec;
		};

		// First the heading
		if (table.tHead) {
			new_elt
					.appendChild(copySectionColumn(table.tHead,
							dragObj.startCol));
		}
		forEach(table.tBodies, function(tb) {
			new_elt.appendChild(copySectionColumn(tb, dragObj.startCol));
		});
		if (table.tFoot) {
			new_elt
					.appendChild(copySectionColumn(table.tFoot,
							dragObj.startCol));
		}

		var obj_pos = dragtable.absolutePosition(dragObj.origNode, true);
		new_elt.style.position = "absolute";
		new_elt.style.left = obj_pos.x + "px";
		new_elt.style.top = obj_pos.y + "px";
		new_elt.style.width = dragObj.origNode.offsetWidth + "px";
		new_elt.style.height = dragObj.origNode.offsetHeight + "px";
		new_elt.style.opacity = 0.7;

		// Hold off adding the element until this is clearly a drag.
		dragObj.addedNode = false;
		dragObj.tableContainer = dragObj.table.parentNode || document.body;
		dragObj.elNode = new_elt;

		// Save starting positions of cursor and element.
		dragObj.cursorStartX = pos.x;
		dragObj.cursorStartY = pos.y;
		dragObj.elStartLeft = parseInt(dragObj.elNode.style.left, 10);
		dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10);

		if (isNaN(dragObj.elStartLeft))
			dragObj.elStartLeft = 0;
		if (isNaN(dragObj.elStartTop))
			dragObj.elStartTop = 0;

		// Update element's z-index.
		dragObj.elNode.style.zIndex = ++dragObj.zIndex;

		// Capture mousemove and mouseup events on the page.
		if (browser.isIE) {
			document.attachEvent("onmousemove", dragtable.dragMove);
			document.attachEvent("onmouseup", dragtable.dragEnd);
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		} else {
			document.addEventListener("mousemove", dragtable.dragMove, true);
			document.addEventListener("mouseup", dragtable.dragEnd, true);
			event.preventDefault();
		}
	},

	// Move the floating column header with the mouse
	// TODO: Reorder columns as the mouse moves for a more interactive feel.
	dragMove : function(event) {
		var x, y;
		var dragObj = dragtable.dragObj;

		// Get cursor position with respect to the page.
		var pos = dragtable.eventPosition(event);

		var dx = dragObj.cursorStartX - pos.x;
		var dy = dragObj.cursorStartY - pos.y;
		if (!dragObj.addedNode && dx * dx + dy * dy > dragtable.dragRadius2) {
			dragObj.tableContainer.insertBefore(dragObj.elNode, dragObj.table);
			dragObj.addedNode = true;
		}

		// Move drag element by the same amount the cursor has moved.
		var style = dragObj.elNode.style;
		style.left = (dragObj.elStartLeft + pos.x - dragObj.cursorStartX)
				+ "px";
		style.top = (dragObj.elStartTop + pos.y - dragObj.cursorStartY) + "px";

		if (dragtable.browser.isIE) {
			window.event.cancelBubble = true;
			window.event.returnValue = false;
		} else {
			event.preventDefault();
		}
	},

	// Stop capturing mousemove and mouseup events.
	// Determine which (if any) column we're over and shuffle the table.
	dragEnd : function(event) {

		if (dragtable.browser.isIE) {
			document.detachEvent("onmousemove", dragtable.dragMove);
			document.detachEvent("onmouseup", dragtable.dragEnd);
		} else {
			document.removeEventListener("mousemove", dragtable.dragMove, true);
			document.removeEventListener("mouseup", dragtable.dragEnd, true);
		}

		// If the floating header wasn't added, the mouse didn't move far
		// enough.
		var dragObj = dragtable.dragObj;
		if (!dragObj.addedNode) {
			return;
		}
		dragObj.tableContainer.removeChild(dragObj.elNode);

		// Determine whether the drag ended over the table, and over which
		// column.
		var pos = dragtable.eventPosition(event);
		var table_pos = dragtable.absolutePosition(dragObj.table);
		if (pos.y < table_pos.y
				|| pos.y > table_pos.y + dragObj.table.offsetHeight) {
			return;
		}
		var targetCol = dragtable.findColumn(dragObj.table, pos.x);
		if (targetCol != -1 && targetCol != dragObj.startCol) {
			dragtable.moveColumn(dragObj.table, dragObj.startCol, targetCol);

			if (dragObj.table.id
					&& dragtable.cookiesEnabled()
					&& dragObj.table.className.search(/\bforget-ordering\b/) == -1) {
				dragtable.rememberDrag(dragObj.table.id, dragObj.startCol,
						targetCol);
			}
		}
	},

	// Which column does the x value fall inside of? x should include
	// scrollLeft.
	findColumn : function(table, x) {
		var header = table.tHead.rows[0].cells;
		for ( var i = 0; i < header.length; i++) {
			// var left = header[i].offsetLeft;
			var pos = dragtable.absolutePosition(header[i]);
			// if (left <= x && x <= left + header[i].offsetWidth) {
			if (pos.x <= x && x <= pos.x + header[i].offsetWidth) {
				return i;
			}
		}
		return -1;
	},

	// Move a column of table from start index to finish index.
	// Based on the "Swapping table columns" discussion on comp.lang.javascript.
	// Assumes there are columns at sIdx and fIdx
	moveColumn : function(table, sIdx, fIdx) {
		var row, cA;
		var i = table.rows.length;
		while (i--) {
			row = table.rows[i]
			var x = row.removeChild(row.cells[sIdx]);
			if (fIdx < row.cells.length) {
				row.insertBefore(x, row.cells[fIdx]);
			} else {
				row.appendChild(x);
			}
		}

		// For whatever reason, sorttable tracks column indices this way.
		// Without a manual update, clicking one column will sort on another.
		var headrow = table.tHead.rows[0].cells;
		for ( var i = 0; i < headrow.length; i++) {
			headrow[i].sorttable_columnindex = i;
		}
	},

	zfMoveHeader : function(form, a, b) {
		var e = $(form);
		var h = new Array();
		var z = $(form).select('th');

		z.each(function(item) {
			// alert("="+item.id);
				if (!item.id.blank())
					h.push(item.id);
			});
		new Ajax.Request(zfurl + "moveHeader.php", {
			method : "post",
			parameters : {
				zfFormName : form,
				zf_apps_system : zfAppsSystem,
				zf_apps_custom : zfAppsCustom,				
				zflist : Object.toJSON(h)
			},
			onComplete : function(request) {
				//alert(request.responseText);
			}
		});
	},

	// Are cookies enabled? We should not attempt to set cookies on a local
	// file.
	cookiesEnabled : function() {
		return (window.location.protocol != 'file:') && navigator.cookieEnabled;
	},

	// Store a column swap in a cookie for posterity.
	rememberDrag : function(id, a, b) {
		this.zfMoveHeader(id, a, b);
		var cookieName = "dragtable-" + id;
		var prev = dragtable.readCookie(cookieName);
		var new_val = "";
		if (prev)
			new_val = prev + ",";
		new_val += a + "/" + b;
		dragtable.createCookie(cookieName, new_val, dragtable.cookieDays);
	},

	// Replay all column swaps for a table.
	replayDrags : function(table) {
		// FIXME
		return;
		if (!dragtable.cookiesEnabled())
			return;
		var dragstr = dragtable.readCookie("dragtable-" + table.id);
		if (!dragstr)
			return;
		var drags = dragstr.split(',');
		for ( var i = 0; i < drags.length; i++) {
			var pair = drags[i].split("/");
			if (pair.length != 2)
				continue;
			var a = parseInt(pair[0]);
			var b = parseInt(pair[1]);
			if (isNaN(a) || isNaN(b))
				continue;
			dragtable.moveColumn(table, a, b);
		}
	},

	// Cookie functions based on http://www.quirksmode.org/js/cookies.html
	// Cookies won't work for local files.
	cookiesEnabled : function() {
		return (window.location.protocol != 'file:') && navigator.cookieEnabled;
	},

	createCookie : function(name, value, days) {
		if (days) {
			var date = new Date();
			date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
			var expires = "; expires=" + date.toGMTString();
		} else
			var expires = "";

		var path = document.location.pathname;
		document.cookie = name + "=" + value + expires + "; path=" + path
	},

	readCookie : function(name) {
		var nameEQ = name + "=";
		var ca = document.cookie.split(';');
		for ( var i = 0; i < ca.length; i++) {
			var c = ca[i];
			while (c.charAt(0) == ' ')
				c = c.substring(1, c.length);
			if (c.indexOf(nameEQ) == 0)
				return c.substring(nameEQ.length, c.length);
		}
		return null;
	},

	eraseCookie : function(name) {
		dragtable.createCookie(name, "", -1);
	}

}

/*******************************************************************************
 * Supporting functions: bundled here to avoid depending on a library
 ******************************************************************************/

// Dean Edwards/Matthias Miller/John Resig
// has a hook for dragtable.init already been added? (see below)
var dgListenOnLoad = false;

/* for Mozilla/Opera9 */
if (document.addEventListener) {
	dgListenOnLoad = true;
	document.addEventListener("DOMContentLoaded", dragtable.init, false);
}

/* for Internet Explorer */
/* @cc_on @ */
/*
 * @if (@_win32) dgListenOnLoad = true; document.write("<script id=__dt_onload
 * defer src=//0)><\/script>"); var script =
 * document.getElementById("__dt_onload"); script.onreadystatechange =
 * function() { if (this.readyState == "complete") { dragtable.init(); // call
 * the onload handler } }; /*@end @
 */

/* for Safari */
if (/WebKit/i.test(navigator.userAgent)) { // sniff
	dgListenOnLoad = true;
	var _dgtimer = setInterval(function() {
		if (/loaded|complete/.test(document.readyState)) {
			dragtable.init(); // call the onload handler
		}
	}, 10);
}

/* for other browsers */
/* Avoid this unless it's absolutely necessary (it breaks sorttable) */
if (!dgListenOnLoad) {
	window.onload = dragtable.init;
}

// Dean's forEach: http://dean.edwards.name/base/forEach.js
/*
 * forEach, version 1.0 Copyright 2006, Dean Edwards License:
 * http://www.opensource.org/licenses/mit-license.php
 */

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
	Array.forEach = function(array, block, context) {
		for ( var i = 0; i < array.length; i++) {
			block.call(context, array[i], i, array);
		}
	};
}

// generic enumeration
Function.prototype.forEach = function(object, block, context) {
	for ( var key in object) {
		if (typeof this.prototype[key] == "undefined") {
			block.call(context, object[key], key, object);
		}
	}
};

// character enumeration
String.forEach = function(string, block, context) {
	Array.forEach(string.split(""), function(chr, index) {
		block.call(context, chr, index, string);
	});
};

// globally resolve forEach enumeration
var forEach = function(object, block, context) {
	if (object) {
		var resolve = Object; // default
		if (object instanceof Function) {
			// functions have a "length" property
			resolve = Function;
		} else if (object.forEach instanceof Function) {
			// the object implements a custom forEach method so use that
			object.forEach(block, context);
			return;
		} else if (typeof object == "string") {
			// the object is a string
			resolve = String;
		} else if (typeof object.length == "number") {
			// the object is array-like
			resolve = Array;
		}
		resolve.forEach(object, block, context);
	}
};

function zfx(e) {
	// Event.stop(e);
	elt = Event.element(e);

	// elt.up('th').hide();
	// alert(elt.up('th').id);
	tblId = elt.up('table');
	// alert(elt.up('div').id);

	var pos = elt.up('th').previousSiblings().length;
	var next = elt.up('th').nextSiblings().length;
	if (next == 0)
		return;

	new Ajax.Request(zfurl + "deleteHeader.php", {
		method : "post",
		parameters : {
			zfFormName : tblId.id,
			zf_apps_system : zfAppsSystem,
			zf_apps_custom : zfAppsCustom,				
			zfkey : elt.up('th').id
		}
	});

	var allRows = $(tblId).rows;
	for ( var i = 0; i < allRows.length; i++) {
		if (allRows[i].cells.length > 1) {
			// allRows[i].deleteCell(pos);
			x = allRows[i].select('th');
			if (x.length == 0)
				x = allRows[i].select('td');
			if (x.length > 0) {
				// allRows[i].down('td',pos).hide();
				x[pos].hide();
			}
		}
	}
}

function zfy(e, id) {
	elt = Event.element(e);
	v = elt.value;
	tblId = elt.up('div').down('table');
	h = tblId.down('#' + v);
	
	new Ajax.Request(zfurl + "addHeader.php", {
		method : "post",
		parameters : {
			zfFormName : tblId.id,
			zf_apps_system : zfAppsSystem,
			zf_apps_custom : zfAppsCustom,				
			zfkey : v
		},
		onComplete : function(request) {
			if (h == null) location.reload(true);
		}
	});

	if (h != null) {
		var pos = h.previousSiblings().length;

		var allRows = tblId.rows;
		for ( var i = 0; i < allRows.length; i++) {
			if (allRows[i].cells.length > 1) {
				// allRows[i].deleteCell(pos);
				x = allRows[i].select('th');
				if (x.length == 0)
					x = allRows[i].select('td');
				if (x.length > 0) {
					// allRows[i].down('td',pos).hide();
					x[pos].show();
				}
			}
		}
	}

	/*
	 * var attrs = { src : zfurl+'../images/remove.png', height : '12px', align :
	 * 'absmiddle' };
	 * 
	 * var tblHeadObj = tblId.tHead; for (var h=0; h<tblHeadObj.rows.length;
	 * h++) { var newTH = document.createElement('th');
	 * tblHeadObj.rows[h].appendChild(newTH); newTH.innerHTML = '[th] row:' + h + ',
	 * cell: ' + (tblHeadObj.rows[h].cells.length - 1)
	 * newTH.observe("mousedown",dragtable.dragStart); var link=new
	 * Element('img',attrs); newTH.insert(link); link.observe('click',zfx); }
	 * 
	 * var tblBodyObj = tblId.tBodies[0]; for (var i=0; i<tblBodyObj.rows.length;
	 * i++) { var newCell = tblBodyObj.rows[i].insertCell(-1); newCell.innerHTML =
	 * '[td] row:' + i + ', cell: ' + (tblBodyObj.rows[i].cells.length - 1) }
	 */

}
