// dom 3.7
//	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) {
		// Fixes event compatibility, returns compatible Event object
		if (typeof e == "undefined") e = window.event;
		if (typeof e.target == "undefined") e.target = e.srcElement;
		if (typeof e.layerX == "undefined") e.layerX = e.offsetX;
		if (typeof e.layerY == "undefined") e.layerY = e.offsetY;
		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.dom_evt_storage) != "undefined") && (typeof (actualNode.dom_evt_storage[e.type]) != "undefined")) {
				for (i in actualNode.dom_evt_storage[e.type]) {
					if (actualNode.dom_evt_storage[e.type][i].phase == phase) {
						if (e.dom_evt_stop) {
							return true; // look if handler stopped an event
						}
						e.dom_evt_data = actualNode.dom_evt_storage[e.type][i];
						actualNode.dom_evt_storage[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.dom_evt_stop)
						break bubble;
					evtChain[chainIndex++] = actualNode;
					actualNode = actualNode.parentNode;
				} while (actualNode && (actualNode.parentNode));
		}
		handleEventAtNode(e, actualNode, evt.bubble);
		
		capture:
			do {
				if (actualNode.dom_evt_stop)
					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.dom_evt_storage == "undefined")
			obj.dom_evt_storage = new Array();
		if (typeof obj.dom_evt_storage[evType] == "undefined") {
			obj.dom_evt_storage[evType] = new Array();
		}
		obj.dom_evt_storage[evType][obj.dom_evt_storage[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.dom_evt_data)
			return e.dom_evt_data.obj;
		return null;
	},
	
	stop: function(e) {
		// stops event propagation
		e.dom_evt_stop = true;
		return true;
	},
	
	cancel: function(e) {
		// stops event propagation
		// and cancels processing of default event handler
		e.dom_evt_stop = true;
		if (e.preventDefault)
			e.preventDefault();
		else
			e.returnValue = false;
		return true;
	}
};




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, newValue) {
		// String elm.getValue(HtmlElement srcElm)
		// Reads element value. Can read inputs and selects
		// TODO - what about radiobuttons and checkboxes?
		if (typeof targetElm.value != "undefined") { // element is standard INPUT
			targetElm.value = newValue;
			return true;
		}
		if (targetElm.tagName.toLowerCase() == "select") {
			if (typeof targetElm.options != "undefined") { // element is SELECT
				var i;
				for (i = 0; i < targetElm.options; i++) {
					if (targetElm.options[i].value == newValue) {
						targetElm.options[i].selected = true;
					} else {
						targetElm.options[i].selected = false;
					}
				}
				return true;
			}
		}
		return false;
	},
	
	getAttribute: function(srcElm, attrName) {
		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);
	},
	
	set: 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;
	}
};


