// dom 3.8
//	3.8	added evt.isEvent method
//	3.7	fixed problem in event bubbling chain
//	3.6	fixed problem with stopping event
//	3.5	elm.get modified to search for event triggers too
//	3.4	elm.getAncestorByTag modified to use elm.getParent method
//	3.4	added elm.getParent method
//	3.3	modified evt.handler method
//	3.3	added evt.getTarget method
//	3.3	added getAncestorByTag method (it is useful)
//	3.2	evt object rewritten. Now it uses standard event handler instead event listeners
//	3.1	Methods evt.add and evt.cancel modified
//	3.0	Entire library rewritten to work with JAX, object cook removed (never used)
//		 !!! This version is not backwards compatible !!!

var evt = {
	// Event handling
	capture: true,
	bubble:  false,
	
	storage: new Array(), // stores all event handlers
	
	fix: function(e) {
		// remove it, legacy interface
		return e;
	},

	handler: function(e) {
		// standard event handler. Could be overriden if necessary
		var obj = this;
		if ((typeof obj == "undefined") || (obj == null)) return false;
		if (typeof e == "undefined") e = window.event;
		
		// prevent usage of built-in event propagation mechanism
		
		if (typeof e.stopPropagation == "function")
			e.stopPropagation(); // standard way
		else if (typeof e.cancelBubble != "undefined")
			e.cancelBubble = true; // MSIE hack
		
		// own event propagation mechanism
		var propagationPath = new Array();
		var actualNode = obj;
		
		// bubble up to document and process every event handler on your way
		var evtChain = new Array();
		var chainIndex = 0; // beware, chainIndex is used in both "bubble" and "capture" loop
		
		function handleEventAtNode(e, actualNode, phase) {
			if (e == null) return false;
			if (actualNode == null) return false;
			if ((typeof (actualNode.evtStorage) != "undefined") && (typeof (actualNode.evtStorage[e.type]) != "undefined")) {
				for (i in actualNode.evtStorage[e.type]) {
					if (actualNode.evtStorage[e.type][i].phase == phase) {
						if (e.evtStopped) {
							return true; // look if handler stopped an event
						}
						e.evtData = actualNode.evtStorage[e.type][i];
						actualNode.evtStorage[e.type][i].fn(e);
					}
				}
			}
			return true;
		}
		
		// window is not a part of DOM tree, so we must process it separate
		if (actualNode == window) {
			handleEventAtNode(e, actualNode, evt.bubble);
			handleEventAtNode(e, actualNode, evt.capture);
			return true;
		}
		
		// bubbling phase
		if (actualNode != document) {
			bubble:
				do {
					handleEventAtNode(e, actualNode, evt.bubble);
					if (actualNode.evtStopped)
						break bubble;
					evtChain[chainIndex++] = actualNode;
					actualNode = actualNode.parentNode;
				} while (actualNode && (actualNode.parentNode));
		}
		handleEventAtNode(e, actualNode, evt.bubble);
		
		capture:
			do {
				if (actualNode.evtStopped)
					break capture;
				handleEventAtNode(e, actualNode, evt.capture); // third parameter indicates capture phase
				actualNode = evtChain[--chainIndex];
			} while (chainIndex >= 0);
		
		return true;
	},
	
	add: function(obj, evType, fn, phase, evId, evGrp) {
		// Attaches event listener to element "obj".
		// evId - unique identifier of event handler
		// evGrp - identifier of event handler group
		if (typeof phase == "undefined")      phase = evt.bubble; // bubbling is default
		if (typeof evId == "undefined")       evId = ""; // default id is empty
		if (typeof evGrp == "undefined")      evGrp = ""; // default group is empty
		var i = evt.storage.length;
		evt.storage[i] = {
			obj:        obj,
			evType:     evType,
			fn:         fn,
			phase:      phase,
			evId:       evId,
			evGrp:      evGrp
		}
		// append event handler definition to element's handler storage
		if (typeof obj.evtStorage == "undefined")
			obj.evtStorage = new Array();
		if (typeof obj.evtStorage[evType] == "undefined") {
			obj.evtStorage[evType] = new Array();
		}
		obj.evtStorage[evType][obj.evtStorage[evType].length] = evt.storage[i];
		obj["on" + evType] = evt.handler;
		return true;
	},
	
	remove: function(obj, evType, fn, phase) {
		// Removes event listener from object
		//	Parameter "fn" is the same function that has been added by evt.add. If the function to remove
		//	is not the same, it will not be removed, even if the two functions had identical syntax.
		// TODO
		return false; // not removed
	},

	flush: function() {
		// removes all event handlers from storage
		if (delete evt.storage)
			return true;
		else {
			evt.storage = null;
			return true;
		}
		return false;
	},
	
	getTarget: function(e) {
		//	Finds event target
		if (typeof e.target == "undefined") e.target = e.srcElement;
		return e.target;
	},
	
	getTrigger: function(e) {
		// Finds element that owns the event handler
		if (e.evtData)
			return e.evtData.obj;
		return null;
	},
	
	stop: function(e) {
		// stops event propagation
		e.evtStopped = true;
		return true;
	},
	
	cancel: function(e) {
		// stops event propagation
		// and cancels processing of default event handler
		e.evtStopped = true;
		if (e.preventDefault)
			e.preventDefault();
		else
			e.returnValue = false;
		return true;
	},
	
	isEvent: function(obj) {
		if (typeof obj.evtData != "undefied")
			return true;
		return false;
	}
};




var cls = {
	// Class manipulations
	get: function(obj) {
		// Array cls.get(HtmlElement obj)
		// Returns all classes of the element "obj" as Array of Strings
		var cl; // String
		if (obj && obj.tagName) {
			cl = obj.className.replace(/\s+/g, " ");
			if (cl == "") return new Array();
			return cl.split(" ");
		}
		return null;
	},
	
	has : function(obj, cl) {
		// Boolean cls.has(HtmlElement obj, String cl)
		// Returns true if element "obj" contains the class "cl"
		var i; // Int
		var actCl; // Array
		if ((typeof cl == "string") && (actCl = cls.get(obj)))
			for (i = 0; i < actCl.length; i++)
				if (actCl[i] == cl)	return true;
		return false;
	},
	
	add : function(obj, cl) {
		// Boolean cls.add(HtmlElement obj, String cl)
		// Adds class "cl" to element "obj" if it is not present
		var actCl = cls.get(obj);
		if ((typeof cl != "undefined") && (actCl != null)) {
			if (!cls.has(obj, cl))
				obj.className += (actCl.length > 0) ? " " + cl : cl;
			return true;
		}
		return false;
	},
	
	remove: function(obj, cl) {
		// Boolean cls.remove(HtmlElement obj, String cl)
		// Removes class "cl" from element "obj"
		var i; // Int iterator
		var actCl = cls.get(obj); // String
		var tempCl = ""; // String
		if ((typeof cl == "string") && (actCl != null)) {
			for (i = 0; i < actCl.length; i++) {
				if (actCl[i] != cl) {
					if (tempCl != "") {tempCl += " ";}
					tempCl += actCl[i];
				}
				obj.className = tempCl;
			}
			return true;
		}
		return false;
	},

	replace: function(obj, oldCl, newCl) {
		// Boolean cls.replace(HtmlElement obj, String oldCl, String newCl)
		// Replaces class "oldCl" by class "newCl". If there is no "oldCl" adds "newCl".
		var i; // Int iterator
		var actCl = cls.get(obj); // String
		var tempCl = ""; // String
		if ((typeof oldCl == "string") && (typeof newCl == "string") && (actCl != null)) {
			if (cls.has(obj, newCl)) {
				cls.remove(obj, oldCl);
			} else if (cls.has(obj, oldCl)) {
				for (i = 0; i < actCl.length; i++) {
					if (tempCl != "") {tempCl += " ";}
					tempCl += (actCl[i] == oldCl) ? newCl : actCl[i];
				}
				obj.className = tempCl;
			} else {
				cls.add(obj, newCl);
			}
			return true;
		}
		return false;
	}
};




var elm = {
	// Elements manipulation object
	get: function(obj) {
		// HtmlElement elm.get(HtmlElement|String|Event obj)
		// Returns HTML element that belongs to object obj.
		if (typeof obj == "undefined") return null;
		if (typeof obj == "string") // if obj is String, returns object with id obj
			return document.getElementById(obj);
		var trigger = evt.getTrigger(obj);
		if (trigger != null) return trigger; // if obj is Event, returns event trigger
		return obj; // if obj is HtmlElement, returns it unchanged
	},
	
	getByTag: function(tagName, srcElm) {
		// returns array of elements with given tagName
		srcElm = (srcElm) ? srcElm : document;
		if (srcElm.all && (tagName == "*"))	// MSIE hack
			return(srcElm.all);
		if (srcElm.getElementsByTagName)	// standard way
			return srcElm.getElementsByTagName(tagName);
		return false;
	},
	
	getParent: function(node) {
		// returns parent element. If there is no parent element it returns null
		if ((typeof node == "undefined") || (node == null)) return null; // node is not defined, there is no parent
		var parent = node.parentNode;
		if ((typeof parent == "undefined") || (parent == null)) return null; // parent node is not defined, there is no parent
		if (typeof parent.tagName == "undefined") return null; // parent node is not HTML element
		return parent;
	},
	
	getAncestorByTag: function(srcElm, tagName) {
		tagName = tagName.toLowerCase();
		var actualTagName = "";
		do {
			srcElm = elm.getParent(srcElm);
			if (srcElm == null) return false; // parent not found
			actualTagName = srcElm.tagName.toLowerCase();
		} while ((actualTagName != tagName) && (actualTagName != "html"));
		if (actualTagName == tagName) return srcElm; // element found
		else return null; // no element found
	},
	
	getValue: function(srcElm) {
		// HtmlCollection elm.getByTag(String tagName[, HtmlElement srcElm])
		// Collects all child elements of srcElm with given tagName
		// TODO - what about radiobuttons and checkboxes?
		if (srcElm.value) return srcElm.value; // element is standard INPUT
		if (srcElm.tagName.toLowerCase() == "select")
			if (typeof (srcElm.selectedIndex != "undefined") && (typeof srcElm.options != "undefined")) { // element is SELECT
				if (srcElm.selectedIndex >= 0)
					return srcElm.options[srcElm.selectedIndex].value; // if select is multiple, returns only first value
			} else return null;	// multiple select, nothing selected
		return srcElm.innerHTML;
	},
	
	setValue: function(targetElm, targetValue) {
		// String elm.getValue(HtmlElement srcElm)
		// Reads element value. Can read inputs and selects
		// TODO - what about radiobuttons and checkboxes?
		
		if (targetElm.options && (targetElm.options.length > 0)) { // element is SELECT
			var i;
			for (i = 0; i < targetElm.options.length; i++) {
				if (targetElm.options[i].value == targetValue) {
					targetElm.options[i].selected = true;
					targetElm.options[i].setAttribute("selected", "selected");
					return true;
				} else {
					targetElm.options[i].selected = false;
					targetElm.options[i].setAttribute("selected", "");
				}
			}
			return false;
		} else if (typeof targetElm.value != "undefined") { // element is input or textarea
			targetElm.value = targetValue;
			return true;
		}
		return false;
	},
	
	getAttribute: function(srcElm, attrName) {
		srcElm = elm.get(srcElm);
		if ((attrName == "class") && (typeof srcElm.getAttribute("class") == "undefined"))
			return srcElm.getAttribute("className");
		if ((attrName == "for") && (srcElm.getAttribute("for") == null))
			return srcElm.getAttribute("htmlFor");
		return srcElm.getAttribute(attrName);
	}
};



// TODO - separate this later

elm.createDiv = function(elmData, parentNode) {
	var div = document.createElement("div");
	if ((typeof elmData != "undefined") && (elmData != null)) {
		if (elmData.id) div.id = elmData.id;
		if (elmData.className) div.className = elmData.className;
		if (elmData.hidden) div.style.display = "none";
		if (elmData.invisible) div.style.visibility = "hidden";
	}
	if ((typeof parentNode != "undefined") && (parentNode != null) && parentNode.appendChild) {
		parentNode.appendChild(div);
	}
	return div;
}

elm.createButton = function(elmData, parentNode) {
	var btn = document.createElement("button");
	var labelText = "";
	if ((typeof elmData != "undefined") && (elmData != null)) {
		if (elmData.title) labelText = elmData.title;
		if (elmData.type) btn.setAttribute("type", elmData.type);
		if (elmData.name) btn.setAttribute("name", elmData.name);
		if (elmData.id) btn.setAttribute("id", btnData.id);
		if (elmData.className) btn.setAttribute("className", elmData.className);
		if (elmData.hidden) btn.style.display = "none";
		if (elmData.invisible) btn.style.visibility = "hidden";
	}
	var span1 = document.createElement("span");
	var span2 = document.createElement("span");
	var labelNode = document.createTextNode(labelText);
	btn.appendChild(span1);
	span1.appendChild(span2);
	span2.appendChild(labelNode);
	
	if ((typeof parentNode != "undefined") && (parentNode != null) && parentNode.appendChild) {
		parentNode.appendChild(btn);
	}
	return btn;
}




elm.getChildrenByTag = function(tagName, srcElm) {
	srcElm = (srcElm) ? srcElm : document;
	srcElm = elm.get(srcElm);
	if(document.getElementsByTagName){
		tagName = tagName.toLowerCase();
		
		foundElements = [];
		if(srcElm.all) allElements = srcElm.all;	// IE hack
		else allElements = srcElm.getElementsByTagName(tagName);
		for(var i = 0; i < allElements.length; i++)
			if((tagName == "*") || (allElements[i].tagName.toLowerCase() == tagName.toLowerCase()))
					if(allElements[i].parentNode == srcElm)
						foundElements[foundElements.length] = allElements[i];
		return(foundElements);
	}
	else return([]);
}

elm.turn = function(obj, flag) {
	// flag = "off" - turns obj off
	// flag = "on"	- turns obj on
	if (flag == "off") obj.style.display = "none";
	var i;
	var fieldsDisabled = false;
	if (flag == "off") fieldsDisabled = true;
	var inputs = obj.getElementsByTagName("input");
	for (i = 0; i < inputs.length; i++) {
		inputs[i].disabled = fieldsDisabled;
	}
	var selects = obj.getElementsByTagName("select");
	for (i = 0; i < selects.length; i++) {
		selects[i].disabled = fieldsDisabled;
	}
	var textareas = obj.getElementsByTagName("textarea");
	for (i = 0; i < textareas.length; i++) {
		textareas[i].disabled = fieldsDisabled;
	}
	if (flag == "on") obj.style.display = "";
	return true;
}

