
// Light-weight augmenting of the DOM API, to abstract away differences between
// browsers.
// By Michael J M Thomson

// (See: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/indexOf)
if (!Array.prototype.indexOf) {
	Array.prototype.indexOf = function(searchElement/*, fromIndex */) {
		"use strict";

		if (this === void 0 || this === null)
			throw new TypeError();

		var t = Object(this);
		var length = t.length >>> 0;
		if (length === 0)
			return -1;

		var fromIndex;
		if (arguments.length > 0) {
			fromIndex = Number(arguments[1]);
			if (isNaN(fromIndex))
				fromIndex = 0;
		} else
			fromIndex = 0;

		var i =
			fromIndex >= 0 ? fromIndex
			               : Math.max(length - Math.abs(fromIndex), 0);
		for (; i < length; ++i)
			if (i in t && t[i] === searchElement)
				return i;
		return -1;
	};
}


function spackle_addEventListener(event_type, handler, use_capture) {
	if (use_capture)
		this.attachEvent(event_type, function() {
			handler(window.event);
		});
	else {
		var property_name = "on" + event_type;

		var previous_handler = this[property_name];
		this[property_name] = handler;

		if (typeof(handler.spackle_previous_handler) == "undefined")
			handler.spackle_previous_handler = new Object;
		handler.spackle_previous_handler[property_name] = previous_handler;

		this[property_name] = function() {
			handler(window.event);

			if (previous_handler)
				previous_handler(window.event);
		};
	}
}
function spackle_addEventListener(event_type, handler, use_capture) {
	if (use_capture)
		this.removeEvent(event_type, handler);
	else {
		var property_name = "on" + event_type;

		if (typeof(handler.spackle_previous_handler) == "undefined")
			return;
		var previous_handler = handler.spackle_previous_handler[property_name];
		if (typeof(previous_handler) == "undefined")
			previous_handler = null;

		this[property_name] = previous_handler;
	}
}

if (typeof(window.addEventListener) == "undefined")
	window.addEventListener = spackle_addEventListener;

window.capture_keypress = function(key_code /* 0 for any */, handler) {
	var original_onkeypress = window.onkeypress;
	window.onkeypress = function(event) {
		if (!event) // IE does not pass the event.
			event = window.event;

		if (!key_code ||
		    event.keyCode == key_code) {
			handler(event);

			event.preventDefault();
			return;
		}

		if (original_onkeypress)
			original_onkeypress(event);
	};

	var handler_info = {
		original_onkeypress: original_onkeypress
	};
	return handler_info;
}
window.release_keypress = function(handler_info) {
	if (typeof(handler_info.original_onkeypress) == "undefined")
		return;

	if (handler_info.original_onkeypress) {
		window.onkeypress = handler_info.original_onkeypress;
		handler_info.original_onkeypress = null;
	}
}

if (typeof(window.getComputedStyle) == "undefined")
	window.getComputedStyle = function(element, pseudo_element) {
		if (typeof(document.defaultView) != "undefined" &&
		    typeof(document.defaultView.getComputedStyle) != "undefined")
			return document.defaultView.getComputedStyle(element, pseudo_element);

		if (element.currentStyle)
			return element.currentStyle;

		return element.style;
	}
// Gecko < 2.0 window.getComputedStyle() second argument isn't optional unlike
// other browsers.
else if (navigator.userAgent.match(/\bFirefox\/(\d+\.\d+)/) && parseFloat(RegExp.$1) < 4.0 ||
         navigator.userAgent.match(/\bSea ?[Mm]onkey\/(\d+\.\d+)/) && parseFloat(RegExp.$1) < 2.1) {
	window.real_getComputedStyle = window.getComputedStyle;
	window.getComputedStyle = function(element, pseudo_element) {
		if (typeof(pseudo_element) == "undefined")
			pseudo_element = null;

		return this.real_getComputedStyle(element, pseudo_element);
	}
}


// "Spackle" an element.
// The return value is just the element, not a proxy, so can be ignored, i.e.
// the following is sufficient to augment an element:
//    spackle(some_element);
function spackle(element /* or id */) {
	if (typeof(element) == "string") {
		var id = element;
		element = document.getElementById(id);
	}
	else if (!element)
		return element;

	if (typeof(element.addEventListener) == "undefined")
		element.addEventListener = spackle_addEventListener;

	if (typeof(element.set_opacity) == "undefined") {
		element.set_opacity = spackle._element_set_opacity;
		element.opacity = spackle._element_opacity;

		element.set_left = spackle._element_set_left;
		element.set_top = spackle._element_set_top;

		element.move_to = spackle._element_move_to;
		element.move_by = spackle._element_move_by;

		element.position = spackle._element_position;
		element.absolute_position = spackle._element_absolute_position;

		element.set_width = spackle._element_set_width;
		element.set_height = spackle._element_set_height;

		element.size = spackle._element_size;

		element.resize_to = spackle._element_resize_to;
		element.resize_by = spackle._element_resize_by;

		element.bounds = spackle._element_bounds;
		element.absolute_bounds = spackle._element_absolute_bounds;
	}

	return element;
}

spackle._element_set_opacity = function(opacity) {
	if (typeof(opacity) != "number")
		opacity = parseFloat(opacity);
	if (isNaN(opacity))
		return;

	if      (opacity < 0.0)
		opacity = 0.0;
	else if (opacity > 1.0)
		opacity = 1.0;

	if (typeof(this.style.opacity) != "undefined")
		this.style.opacity = opacity.toFixed(16);

	else // IE instead supports the following:
		this.style.filter = "alpha(opacity = " + (opacity * 100) + ")";
}
spackle._element_opacity = function() {
	var computed_style = window.getComputedStyle(this);
	if (typeof(computed_style.opacity) != "undefined")
		return computed_style.opacity;

	else { // IE instead supports the following:
		var filter = computed_style.filter;
		if (filter.match(/^.*\balpha\s*\(\s*opacity\s*=\s*([0-9]+(?:\.[0-9]*)?)\s*\)/))
			return parseFloat(RegExp.$1) / 100.0;
		else
			return 1.0;
	}
}

spackle._element_set_left = function(left) {
	if (typeof(left) == "number")
		left += "px";

	this.style.left = left;
}
spackle._element_set_top = function(top) {
	if (typeof(top) == "number")
		top += "px";

	this.style.top = top;
}

spackle._element_position = function() {
	return {
		left: this.offsetLeft,
		top:  this.offsetTop
	};
}

spackle._element_absolute_position = function() {
	var result = {
		left: parseInt(this.offsetLeft),
		top:  parseInt(this.offsetTop)
	};
	for (var parent = this.offsetParent; parent != null; parent = parent.offsetParent) {
		result.left += parent.offsetLeft;
		result.top  += parent.offsetTop;
	}
	return result;
}

spackle._element_move_to = function(left, top) {
	this.set_left(left);
	this.set_top(top);
}
spackle._element_move_by = function(dx, dy) {
	var position = this.position();
	if (dx)
		this.set_left(position.left + dx);
	if (dy)
		this.set_top(position.top + dy);
}

spackle._element_set_width = function(width) {
	if (typeof(width) == "number")
		width += "px";

	this.style.width = width;
}
spackle._element_set_height = function(height) {
	if (typeof(height) == "number")
		height += "px";

	this.style.height = height;
}

spackle._element_size = function() {
	return {
		width: this.offsetWidth,
		height: this.offsetHeight
	};
}

spackle._element_resize_to = function(width, height) {
	this.set_width(width);
	this.set_height(height);
}
spackle._element_resize_by = function(dw, dh) {
	var size = this.size();
	if (dw)
		this.set_width(size.width + dw);
	if (dh)
		this.set_height(size.height + dh);
}

spackle._element_bounds = function() {
	var position = this.position(),
	    size = this.size();
	return {
		left: position.left,
		top:  position.top,
		width:  size.width,
		height: size.height
	};
}
spackle._element_absolute_bounds = function() {
	var position = this.absolute_position(),
	    size = this.size();
	return {
		left: position.left,
		top:  position.top,
		width:  size.width,
		height: size.height
	};
}


// Document API.

spackle.document_visible_width = function() {
	if (document.documentElement.clientWidth == 0) // IE 6-8 quirk
		return document.body.clientWidth;

	return document.documentElement.clientWidth;
}
spackle.document_visible_height = function() {
	if (document.documentElement.clientHeight == 0) // IE 6-8 quirk
		return document.body.clientHeight;

	return document.documentElement.clientHeight;
}

if (typeof(document.scroll_to) == "undefined") {
	document.scroll_to = function(left, top) {
		if (document.documentElement) {
			document.documentElement.scrollLeft = left;
			document.documentElement.scrollTop = top;
		}
		else if (window.pageXOffset) {
			window.pageXOffset = left;
			window.pageYOffset = top;
		}
	}
	document.scroll_by = function(dx, dy) {
		if (document.documentElement) {
			document.documentElement.scrollLeft += dx;
			document.documentElement.scrollTop  += dy;
		}
		else if (window.pageXOffset) {
			window.pageXOffset += dx;
			window.pageYOffset += dy;
		}
	}
}


