/*
	Element functions. 
*/

// MSIE Compatibility
if (!HTMLElement)
	var HTMLElement = {};

/*
	Element moving.
	
	This function handles all the parts of starting, a free drag across the screen.
	
	To use it, set the 'movable' attribute of an element to true. You can also add
	onmovestart, onmove and onmoveend DOM0 event handlers. These must be functions.
	
	e.g.
	
	var n = document.getElementById("movey");
	n.draggable = true;
	n.ondragend = function(event) {
		alert("Drag ended " + event.type);
	};
*/
HTMLElement._move = function(event) {
	var event = event || window.event;
	var target = Event.prototype.getTarget.call(event);
	var mouseX = Event.prototype.getMouseX.call(event);
	var mouseY = Event.prototype.getMouseY.call(event);
	
	// Can we start an move?
	if (!this._moving && event.type == "mousedown") {
		// traverse up the heirachy for find a movable element
		while (target && !target.moveHandleFor)
			target = HTMLElement.prototype.getParentNode.call(target);
		
		if (target) {
			this._moving = {	
				"target": target.moveHandleFor,
				"mouseX": mouseX,
				"mouseY": mouseY
			};
			if (target.onmovestart)
				target.onmovestart.call(target, {"target": target, "type": "movestart"});
		}
	} else if (this._moving) {
		var deltaX = mouseX - this._moving.mouseX;
		var deltaY = mouseY - this._moving.mouseY; 
		
		var target = this._moving.target;
		
		// move the element
		target.style.left = 
			(HTMLElement.prototype.getLeft.call(target) + deltaX) + "px";
		target.style.top = 
			(HTMLElement.prototype.getTop.call(target) + deltaY) + "px";
			
		this._moving.mouseX = mouseX;
		this._moving.mouseY = mouseY;
		
		if (event.type == "mousemove" && target.onmove)
			target.onmove.call(target, {"target": target, "type": "move"});
	}	
	
	// Have we finished moving?
	if (this._moving && event.type == "mouseup") {
		target = this._moving.target;
		this._moving = null;
		if (target.onmoveend) 
			target.onmoveend.call(target, {"target": target, "type": "moveend"});
	}
};

/*
	Facilites dragging of elements.
	
	Example:
		document.getElementById("test").draggable = true;
		document.getElementById("test1").dragontopable = true;
		
	Events:
		ondrag
		ondrop
*/
HTMLElement._drag = function(event) {
	var event = event || window.event;
	var target = Event.prototype.getTarget.call(event);
	var relatedTarget = Event.prototype.getRelatedTarget.call(event)
	
	// Can we start an move?
	if (!this._dragging && event.type == "mousedown") {
		// traverse up the heirachy for find a movable element
		while (target && !target.draggable)
			target = HTMLElement.prototype.getParentNode.call(target);
		
		if (target) {
			this._dragging = {	
				"target": target
			};
			if (target.ondrag)
				target.ondrag.call(target, {"target": target, "type": "drag"});
		}
	} else if (this._dragging && (target != this._dragging.target || event.type == "mouseup") && target.dragontopable) {
		var parent = HTMLElement.prototype.getParent.call(this._dragging.target);
		
		parent.removeChild(this._dragging.target);
		target.appendChild(this._dragging.target);
	}
	
	// Have we finished moving?
	if (this._dragging && event.type == "mouseup") {
		if (target.ondrop)
			target.ondrop.call(target, {"target": target, "relatedTarget": this._dragging.target, "type": "drop"});
		this._dragging = null;
	}
};

// MSIE Compatibility
if (!HTMLElement.prototype)
	HTMLElement.prototype = {};


// Get the elements ID, giving it an new unique ID if it doesn't have one.
HTMLElement.prototype.getId = function() {
	// The new ID is an integter (the current date) + a fraction  (random number)
	return this.id || (this.id = new Date().getTime() + Math.random());
};
 
// Get the parent node of an item
HTMLElement.prototype.getParentNode = function() {
	return this.parentElement || this.parentNode;
};


/*
	Element dimension functions.
	
	Mixture of accuracy, followed by best guessing, followed by known lying.
*/
HTMLElement.prototype.getLeft = function() {
	if (this.x)
		return this.x;
		
 	var left = this.offsetLeft;
 	
 	var element;
	while (element = this.offsetParent && element && element.offsetLeft)
		left += element.offsetLeft;
	
	return left;
};

HTMLElement.prototype.getTop = function() {
	if (this.y)
		return this.y;
		
	var top = this.offsetTop;
	
	var element;
	while (element = this.offsetParent && element && element.offsetTop) 
		top += element.offsetTop;
	
	return top;
};

HTMLElement.prototype.getWidth = function() {
	return this.offsetWidth || (
		this.clip ? this.clip.height : (
			this.style.pixelWidth || !isNaN(parseInt(this.style.width)) ? parseInt(this.style.width) : 64
		)
	);
};

HTMLElement.prototype.getHeight = function() {
	return this.offsetHeight || (
		this.clip ? this.clip.width : (
			this.style.pixelHeight || !isNaN(parseInt(this.style.height)) ? parseInt(this.style.height) : 32
		)
	);
};

/*
	Element content functions.
*/
// Set the innerHTMl of an this.
HTMLElement.prototype.setInnerHTML = function(toValue) {
	 // IE has this built in... 
	if (typeof(this.innerHTML) != "undefined") {
		this.innerHTML = toValue; 
	 // Otherwise, try createContextualFragment().
	} else { 
	 	var range = document.createRange(); 
	 	range.selectNodeContents(this); 
	 	range.deleteContents(); 
	 	this.appendChild(range.createContextualFragment(toValue)); 
	} 
};

HTMLElement.prototype.appendInnerHTML = function(appendValue) {
	 // IE has this built in... 
	if (typeof(this.innerHTML) != "undefined") {
		this.innerHTML += appendValue; 
	 // Otherwise, try createContextualFragment().
	} else { 
	 	var range = document.createRange(); 
	 	range.selectNodeContents(this); 
	 	this.appendChild(range.createContextualFragment(appendValue)); 
	} 
};


HTMLElement.prototype.getInnerHTML = function() {
	if (typeof(this.innerHTML) != "undefined")
		return this.innerHTML; 
	else { 
		var returnStr = ""; 
		for (var i = 0; i < this.childNodes.length; i++)
			returnStr += this.childNodes[i].getOuterHTML();
		return returnStr; 
	} 
};

HTMLElement.prototype.getOuterHTML = function() { 
	if (typeof(this.outerHTML) != "undefined")
		return this.outerHTML; 
	
	var str = ""; 
	switch (this.nodeType) { 
		// An this. 
		case 1: 
			str += "<" + this.nodeName;
			for (var i = 0; i < this.attributes.length; i++) {
				if (this.attributes[i].nodeValue != null) 
					str += ' ' + this.attributes[i].nodeName + '="' + 
					this.attributes[i].nodeValue + '"'; 
			} 
			if (this.childNodes.length == 0 && 
				"'hr', 'input', 'img', 'link', 'meta', 'br'".match(this.nodeName.toLowerCase()))
				str += " />";
		else 
			str += ">" + element.getInnerHTML() + "";
		break; 
		// 2 is an attribute. 
		// Just some text.. 
		case 3: 
			str += this.nodeValue; 
			break; 
		// A CDATA section. 
		case 4: 
			str += "";
			break; 
		// Entity reference.. 
		case 5:
			str += "&" + this.nodeName + ";";
			break; 
		// 6 is an actual entity, 7 is a PI. 
		// Comment. 
		case 8: 
			str += ""; 
			break; 
	} 
	return str;
};

if (document.addEventListener) {
	document.addEventListener("mousedown", HTMLElement._move, true);
	document.addEventListener("mousemove", HTMLElement._move, true);
	document.addEventListener("mouseup", HTMLElement._move, true);
} else if (document.attachEvent) {
	document.attachEvent("onmousedown", HTMLElement._move);
	document.attachEvent("onmousemove", HTMLElement._move);
	document.attachEvent("onmouseup", HTMLElement._move);
}

if (document.addEventListener) {
	document.addEventListener("mousedown", HTMLElement._drag, true);
	document.addEventListener("mousemove", HTMLElement._drag, true);
	document.addEventListener("mouseup", HTMLElement._drag, true);
} else if (document.attachEvent) {
	document.attachEvent("onmousedown", HTMLElement._drag);
	document.attachEvent("onmousemove", HTMLElement._drag);
	document.attachEvent("onmouseup", HTMLElement._drag);
}