var _namespaceURI = new Object();
_namespaceURI["DEFAULT"] = document.documentElement.namespaceURI;
_namespaceURI["XML"] = "http://www.w3.org/XML/1998/namespace";
_namespaceURI["XHTML"] = "http://www.w3.org/1999/xhtml";
_namespaceURI["MATHML"] = "http://www.w3.org/1998/Math/MathML";
_namespaceURI["SVG"]    = "http://www.w3.org/2000/svg";

var _DOMImplements = new Object();
_DOMImplements["Document"] = (window.Document) ? true : false;
_DOMImplements["Element"] = (window.Element) ? true : false;
_DOMImplements["Node"] = (window.Node) ? true : false;

if (!_DOMImplements["Document"]) window.Document = function() {
}

if (!_DOMImplements["Element"]) window.Element = function() {
	this.getAttribute = function (attribute) {
		if (attribute == "class" && this.className != undefined) return this.className;
		if (attribute == "for" && this.htmlFor != undefined) return this.htmlFor;
		return this[attribute];
	}

	this.setAttribute = function (attribute, value) {
		if (attribute == "class" && this.className != undefined) {
			this.className = value;
		} else if (attribute == "for" && this.htmlFor != undefined) {
			this.htmlFor = value;
		} else {
			this[attribute] = value;
		}
	}
}

if (!_DOMImplements["Node"]) window.Node = function() {}
function NodeType() {
	if (!this.ELEMENT_NODE) {
		this.ELEMENT_NODE                   = 1;
		this.ATTRIBUTE_NODE                 = 2;
		this.TEXT_NODE                      = 3;
		this.CDATA_SECTION_NODE             = 4;
		this.ENTITY_REFERENCE_NODE          = 5;
		this.ENTITY_NODE                    = 6;
		this.PROCESSING_INSTRUCTION_NODE    = 7;
		this.COMMENT_NODE                   = 8;
		this.DOCUMENT_NODE                  = 9;
		this.DOCUMENT_TYPE_NODE             = 10;
		this.DOCUMENT_FRAGMENT_NODE         = 11;
		this.NOTATION_NODE                  = 12;
	}
}
NodeType.apply(Node);

function NodeList() {
	var nodes = new Array();
	nodes.item = function(index) { return nodes[index]; }
	return nodes;
}
var ElementClassName = (function() {
	function classAttribute(namespaceURI, localName) {
		this.namespaceURI = namespaceURI;
		this.localName = localName
	}
	var classAttributes = new Array();
	classAttributes["mathml"] = new classAttribute(_namespaceURI["MATHML"], "class");
	classAttributes["xhtml"]  = new classAttribute(_namespaceURI["XHTML"],  "class");
	classAttributes["svg"]    = new classAttribute(_namespaceURI["SVG"],    "class");

	var isHTML = (function() {
		var htmlElement = document.getElementsByTagName("html").item(0);
		if (htmlElement) {
			return (htmlElement.tagName == "HTML");
		} else {
			return false;
		}
	})();

	return function() {
		this.hasClassName = function(className) {
			className = className || "";
			if (!className.match(/^(\S+)$/)) return false;
	
			var re = new RegExp("(^|\\s)" + className + "(\\s|$)", "gm");
			var classNames = "";
			if (isHTML) {
				if (this.className) classNames = this.className;
			} else {
				var prefix;
	
				if (this.className) classNames = this.className + " ";

				if (this.namespaceURI && !(this.namespaceURI == classAttributes["xhtml"].namespaceURI && this.className != undefined)) {
					for (prefix in classAttributes) {
						if (this.namespaceURI == classAttributes[prefix].namespaceURI
						 && this.hasAttribute(classAttributes[prefix].localName)) {
							classNames += this.getAttribute(classAttributes[prefix].localName) + " ";
						}
					}
				}

				if (this.getAttributeNS) {
					for (prefix in classAttributes) {
						if (this.hasAttributeNS(classAttributes[prefix].namespaceURI, classAttributes[prefix].localName)) {
							classNames += this.getAttributeNS(classAttributes[prefix].namespaceURI, classAttributes[prefix].localName) + " ";
						}
					}
				}
			}
	
			return re.test(classNames);
		}
	
		this.addClassName = function(className) {
			if (this.hasClassName(className)) return;
			if (isHTML) {
				this.className +=  " " + className;
			} else {
				var prefix;

				if (this.className != undefined) {
					this.className += " " + className;
				} else if (this.namespaceURI) {
					for (prefix in classAttributes) {
						if (this.namespaceURI == classAttributes[prefix].namespaceURI) {
							if (this.hasAttribute(classAttributes[prefix].localName)) {
								this.setAttribute(classAttributes[prefix].localName,
												  this.getAttribute(classAttributes[prefix].localName) + " " + className)
							} else {
								this.setAttribute(classAttributes[prefix].localName, className)
							}
							return true;
						}
					}
					return false;
				} else {
					return false;
				}
			}
		}
	
		this.removeClassName = function(className) {
			var re = RegExp("(^|\\s)" + className + "(\\s|$)", "gm");
			if (isHTML) {
				if (this.className)	this.className = this.className.replace(re, "$1$2");
			} else {
				var prefix, value;
				if (this.className)	this.className = this.className.replace(re, "$1$2");

				if (this.namespaceURI) {
					for (prefix in classAttributes) {
						if (this.namespaceURI == classAttributes[prefix].namespaceURI
						 && this.hasAttribute(classAttributes[prefix].localName)) {
							value = this.getAttribute(classAttributes[prefix].localName).replace(re, "$1$2");
							this.setAttribute(classAttributes[prefix].localName, value);
						}
					}
				}
	
				if (this.getAttributeNS) {
					for (prefix in classAttributes) {
						if (this.hasAttributeNS(classAttributes[prefix].namespaceURI, classAttributes[prefix].localName)) {
							value = this.getAttributeNS(classAttributes[prefix].namespaceURI, classAttributes[prefix].localName).replace(re, "$1$2");
							this.setAttributeNS(classAttributes[prefix].namespaceURI, classAttributes[prefix].localName, value);
						}
					}
				}
			}
		}
	}
})();

function GetElementsByClassName() {
	this.getElementsByClassName = function() {
		var i, j, nodes, element, matches;
		var list = new NodeList();

		if (this.getElementsByTagNameNS) {
			nodes = this.getElementsByTagNameNS("*", "*");
		} else if (this.getElementsByTagName) {
			nodes = this.getElementsByTagName("*");
		} else {
			return null;
		}
		for (i = 0; i < nodes.length; i++) {
			element = nodes.item(i);
			try {
				matches = true;
				for (j = 0; j < arguments.length && matches; j++) {
					if (!element.hasClassName(arguments[j])) matches = false;
				}
				if (matches) list.push(element);
			} catch(e) {
				matches = false;
			}
		}
		return list;
	}
}

function EventListener(type, listener, useCapture) {
	this.type = type;
	this.listener = listener;
	this.useCapture = useCapture;
	this.handleEvent = function(evt) {
		if (!this.removed && evt.type == this.type && !this.useCapture)
			listener.apply(evt.currentTarget, [evt]);
	}
}
function EventListenerList() {
	var list = new Array();
	list.item = function(index) { return list[index]; }
	return list;
}
var Event = (function() {
	var eventType = new Array();
	eventType["click"]     = {bubbles:true, cancelable:true};
	eventType["mousedown"] = {bubbles:true, cancelable:true};
	eventType["mouseup"]   = {bubbles:true, cancelable:true};
	eventType["mouseover"] = {bubbles:true, cancelable:true};
	eventType["mousemove"] = {bubbles:true, cancelable:false};
	eventType["mouseout"]  = {bubbles:true, cancelable:true};
	eventType["keydown"]   = {bubbles:true, cancelable:true};
	eventType["keyup"]     = {bubbles:true, cancelable:true};
	eventType["keypress"]  = {bubbles:true, cancelable:true};
	eventType["load"]      = {bubbles:false,cancelable:false};
	eventType["unload"]    = {bubbles:false,cancelable:false};
	eventType["abort"]     = {bubbles:true, cancelable:false};
	eventType["error"]     = {bubbles:true, cancelable:false};
	eventType["select"]    = {bubbles:true, cancelable:false};
	eventType["change"]    = {bubbles:true, cancelable:false};
	eventType["submit"]    = {bubbles:true, cancelable:true};
	eventType["reset"]     = {bubbles:true, cancelable:false};
	eventType["focus"]     = {bubbles:false,cancelable:false};
	eventType["blur"]      = {bubbles:false,cancelable:false};
	eventType["resize"]    = {bubbles:true, cancelable:false};
	eventType["scroll"]    = {bubbles:true, cancelable:false};

	return function(currentTarget) {
		if (this.bubbles == undefined)    this.bubbles =         (eventType[this.type])    ? eventType[this.type].bubbles    : true;
		if (this.cancelable == undefined) this.cancelable =      (eventType[this.type])    ? eventType[this.type].cancelable : true;
		if (!this.currentTarget)          this.currentTarget =   (currentTarget == window) ? document : currentTarget;
		if (!this.target && window.event) this.target =          (window.event.srcElement) ? window.event.srcElement : document;
		if (this.timeStamp == undefined)  this.timeStamp =       new Date().valueOf();
		if (!this.preventDefault)         this.preventDefault =  function() { if (this.cancelable) this.returnValue = false; };
		if (!this.stopPropagation)        this.stopPropagation = function() { this.cancelBubble = true; }
		if (!this.eventPhase) {
			this.eventPhase = (this.currentTarget == this.target) ? Event.AT_TARGET : Event.BUBBLING_PHASE;
		} else if (this.eventPhase == Event.AT_TARGET && currentTarget != this.target) {
			this.eventPhase = Event.BUBBLING_PHASE;
		}
	}
})();
function MouseEvent() { /* To be written later */ }
function KeyboardEvent() { /* To be written later */ }
function PhaseType() {
	this.CAPTURING_PHASE = 1;
	this.AT_TARGET       = 2;
	this.BUBBLING_PHASE  = 3;
}
PhaseType.apply(Event);

function EventTarget() {
	function isMatch(eventListener, type, listener, useCapture) {
		return (eventListener.type == type && eventListener.listener == listener && eventListener.useCapture == useCapture); 
	}
	this.eventListenerList = null;
	if (this == document || !this.addEventListener) {
		var _addEventListener = (this == document) ? this.addEventListener || null : null;
		this.addEventListener = function(type, listener, useCapture) {
			if (!_addEventListener || (this == document && type == "load" && !useCapture)) {
				var obj = (this == document && type == "load") ? window : this;
				if (!this.eventListenerList) {
					this.eventListenerList = new EventListenerList();
				}

				for (var i = 0; i < this.eventListenerList.length; i++) {
					if (isMatch(this.eventListenerList.item(i), type, listener, useCapture)) {
						return;
					}
				}

				this.eventListenerList.push(new EventListener(type, listener, useCapture));

				var eventName = "on" + type;
				if (!obj[eventName]) {
					obj[eventName] = function(evt) {
						var currentTarget = (this == window) ? document : this;
						currentTarget.dispatchEvent(evt);
					}
				}
			} else if (_addEventListener) {
				_addEventListener(type, listener, useCapture);
			}
		}
	}
	if (this == document || !this.removeEventListener) {
		var _removeEventListener = (this == document) ? this.removeEventListener || null : null;
		this.removeEventListener = function(type, listener, useCapture) {
			if (!_removeEventListener || (this == document && type == "load")) {
				var filteredList = new EventListenerList()
				for (var i = 0; i < this.eventListenerList.length; i++) {
					if (isMatch(this.eventListenerList.item(i), type, listener, useCapture))
						this.eventListenerList.item(i).removed = true;
					else
						filteredList.push(this.eventListenerList.item(i));
				}
				this.eventListenerList = filteredList;
			} else {
				_removeEventListener(type, listener, useCapture);
			}
		}
	}
	if (this == document || !this.dispatchEvent) {
		this.dispatchEvent = function(evt) {
			if (!evt) evt = window.event;
			Event.apply(evt, [this]);
			MouseEvent.apply(evt);
			KeyboardEvent.apply(evt);
			if (this.eventListenerList) {
				var list = new Array().concat(this.eventListenerList)
				for (var i = 0; i < list.length; i++)
					list[i].handleEvent(evt);
			}
		}
	}
}

if (!_DOMImplements["Document"]) {
	Document.prototype = document;
	Document.apply(Document.prototype);
}
ElementClassName.apply(Element.prototype);
GetElementsByClassName.apply(Document.prototype);
GetElementsByClassName.apply(Element.prototype);
EventTarget.apply(document);
EventTarget.apply(Node.prototype);
document.addEventListener("load", function() {
	var elements = document.getElementsByTagName("*");
	for (var i = 0; i < elements.length; i++) {
		_applyMethods(elements.item(i));
	}
}, false);

function _applyMethods(element) {
	try {
		if (!_DOMImplements["Element"]) {
			var el = new Element();
			for (method in el) {
				if (!element[method] || method == "setAttribute" || method == "getAttribute") {
					element[method] = el[method];
				}
			}
		}
		if (!_DOMImplements["Node"]) {
			var node = new Node();
			for (method in node) {
				if (!element[method]) element[method] = node[method];
			}
		}
		return true;
	} catch(e) {
		return false;
	}
}
