// lib.evt.compact 1.1.8
// 
// protected by creative commons deed
// under the following conditions: Attribution, Share Alike
// http://creativecommons.org/licenses/by-sa/2.5/
// 
// event handling function has three arguments - fn(elm, e, evtId)
// these arguments are filled automatically when the event is handled
// elm - trigger DOM element, e - triggering event, evtId - event handler ID
// 

var lib = lib || {};
lib.evt = lib.evt || {};
lib.array = lib.array || {};
lib.data = lib.data || {};




lib.evt.name = "lib.evt";
lib.evt.defaultPriority = 0; // when the priority is not set, sets this priority

lib.evt._handlers = []; // array storing all event handling functions. You can find them by evtId.
lib.evt._flushData = []; // this is needed for flushing event handlers on unload (to prevent memory leaks)




lib.evt.add = function (elm, evType, fn, priority, callbackContext) {
	// Attaches event listener to element elm
	// returns event id number
	if ((typeof priority == "undefined") || (priority === null)) {
		priority = lib.evt.defaultPriority;
	}
	if ((typeof callbackContext == "undefined") || (callbackContext === null)) {
		callbackContext = elm;
	}
	
	// create a new queue - f (elm, evType)
	var evQue = lib.data.get(elm, evType, lib.evt.name);
	var evName;
	
	if (evQue === null) {
		evQue = [];
		lib.data.set(elm, evType, evQue, lib.evt.name);
		
		evName = "on" + evType;
		
		// if there is an event handler, preserve it
		if (typeof elm[evName] == "function") {
			lib.data.set(elm, evName, elm[evName], lib.evt.name);
		}
		
		// add a real event handler
		// don't use listeners, or you will be unable to handle custom events
		elm[evName] = lib.evt._handler; // add event handler
		lib.array.push(lib.evt._flushData, {elm: elm, evName: evName});
	}
	// add event data record to the queue 
	var handlerData = {
		elm:        elm,
		evType:     evType,
		priority:   priority,
		context:    callbackContext
	};
	var handlerId = lib.array.insert(lib.evt._handlers, handlerData); // register this handler
	handlerData.id = handlerId;
	handlerData.fn = fn;
	
	lib.array.push(evQue, handlerData);
	evQue = lib.array.sortBy(evQue, "priority");
	
	return handlerData.id;
};

lib.evt.remove = function (evtId) {
	// removes the event specified by id
	var elm = lib.evt._handlers[evtId].elm;
	var evType = lib.evt._handlers[evtId].evType;
	lib.evt._handlers[evtId] = null;
	var evQue = lib.data.get(elm, evType, lib.evt.name);
	var i;
	search:
		for (i = 0, l = evQue.length; i < l; i++) {
			if (evQue[i] && (evQue[i].id == evtId)) {
				evQue[i] = null;
				break search;
			}
		}
	return true;
};

lib.evt.getTarget = function (e) {
	// gets event target element
	var result = window;
	if (e.target) {
		result = e.target;
	} else if (e.srcElement) {
		result = e.srcElement;
	}
	if (result.nodeType && (result.nodeType == 3)) { // defeat Safari bug, thanks to PPK
		result = result.parentNode;
	}
	return result;
};

lib.evt.getType = function (e) {
	// returns the type of the event ("click", "submit" etc.)
	return e.type;
};

lib.evt.getPriority = function (evtId) {
	return lib.evt._handlers[evtId].priority;
};

lib.evt.getMousePosition = function (e) {
	// returns object { int x, int y } that contains the position of the mouse cursor on the screen
	// when the event has been triggered.
	if (!e) { e = window.event; }
	var pos = { x:0, y:0 };
	if (e.pageX || e.pageY) {
		// Mozilla etc.
		pos.x = e.pageX;
		pos.y = e.pageY;
	} else if (e.clientX || e.clientY) {
		// MSIE
		pos.x = e.clientX;
		pos.y = e.clientY;
		if ( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
			// DOM compliant
			pos.x += document.body.scrollLeft;
			pos.y += document.body.scrollTop;
		} else if ( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
			//IE6 standards compliant mode
			pos.x += document.documentElement.scrollLeft;
			pos.y += document.documentElement.scrollTop;
		}
	}
	return pos;
};

lib.evt.getKey = function (e) {
	// if the event type is "keyPress", "keyDn" or "keyUp", returns the key code
	if (window.event) {
		return window.event.keyCode;
	} else if (e) {
		return e.which;
	} else {
		return null;
	}
};

lib.evt.getHandlerData = function (evtId) {
	// returns a reference to the data object that contains all object data
	return lib.evt._handlers[evtId];
};

lib.evt.stop = function (e) {
	// stops event propagation
	if (e.stopPropagation) {
		e.stopPropagation();
	}
	if (typeof e.cancelBubble == "boolean") {
		e.cancelBubble = true;
	}
	lib.data.set(e, "stop", true, lib.evt.name);
	return true;
};

lib.evt.cancel = function (e) {
	// stops event propagation
	// and cancels processing of default event handler
	lib.evt.stop(e);
	if (e.preventDefault) {
		e.preventDefault();
	} else {
		e.returnValue = false;
	}
	return true;
};

lib.evt.flush = function () {
	// this shall be called on unload to prevent memory leaks in MSIE
	for (var i = 0, l = lib.evt._flushData.length; i < l; i++) {
		if (lib.evt._flushData[i].elm) {
			lib.evt._flushData[i].elm[lib.evt._flushData[i].evName] = null; // remove the event handler
		}
	}
	return true;
};

lib.evt.create = function (evType, props) {
	// creates and returns a custom event
	var evt = {};
	if (props) {
		evt = props;
	}
	evt.type = evType;
	evt.preventDefault = function () {}; // custom event doesn't bubble
	evt.isCustomEvent = true;
	return evt;
};

lib.evt.addType = function (elm, evType) {
	// makes the element capable to process another event type.
	if ((typeof elm["on" + evType] == "undefined") || (elm["on" + evType] === null)) {
		elm["on" + evType] = function () {}; // default handler (empty)
	}
	return true;
};

lib.evt.run = function (elm, evType) {
	// runs event handlers of evType on element elm
	var evQue = lib.data.get(elm, evType, lib.evt.name);
	var e;
	if ((typeof evQue != "undefined") && (evQue !== null)) {
		e = lib.evt.create(evType);
		return lib.evt._process(elm, e);
	}
	return false;
};

lib.evt._process = function (elm, e) {
	// process the event
	var evType = lib.evt.getType(e);
	var evQue = lib.data.get(elm, evType, lib.evt.name);
	var i;
	
	// first, process preserved original event handler
	var fn = lib.data.get(elm, "on" + evType, lib.evt.name);
	if (fn !== null) {
		fn.apply(elm, [e]);
	}
	if (evQue) {
		// now process standard event queue
		processEvent:
			for (i = evQue.length - 1; i >= 0; i--) {
				if (lib.data.get(e, "stop", lib.evt.name)) {
					break processEvent;
				} else if (evQue[i]) {
					evQue[i].fn.apply(evQue[i].context, [evQue[i].elm, e, evQue[i].id]);
				}
			}
	}
	return true;
};

lib.evt._handler = function (e) {
	if (typeof e == "undefined") { e = window.event; }
	var elm = this; // handler is attached directly to the trigger object
	return lib.evt._process(elm, e);
};




// support functions

lib.data.name = "lib.data";
lib.data.attribute = "_jsData"; // name of DOM element attribute containing all data from sc framework
lib.data._flushData = []; // this is needed for flushing data on unload (to prevent memory leaks)

lib.data.set = function (elm, fieldName, data, nameSpace) {
	if (typeof nameSpace == "undefined") {
		nameSpace = lib.data.name;
	}
	if (typeof elm[lib.data.attribute] == "undefined") {
		elm[lib.data.attribute] = {};
		lib.array.push(lib.data._flushData, {elm: elm});
	}
	if (typeof elm[lib.data.attribute][nameSpace] == "undefined") {
		elm[lib.data.attribute][nameSpace] = {};
	}
	elm[lib.data.attribute][nameSpace][fieldName] = data;
	return true;
};

lib.data.get = function (elm, fieldName, nameSpace) {
	if (typeof nameSpace == "undefined") {
		nameSpace = lib.data.name;
	}
	var data = null;
	if (
		(typeof elm[lib.data.attribute] != "undefined") &&
		(typeof elm[lib.data.attribute][nameSpace] != "undefined") &&
		(typeof elm[lib.data.attribute][nameSpace][fieldName] != "undefined")
	) {
		data = elm[lib.data.attribute][nameSpace][fieldName];
	}
	return data;
};

lib.data.flush = function () {
	// this shall be called on unload to prevent memory leaks in MSIE
	// this is called in lib.evt on unload by default
	var i, l;
	for (i = 0, l = lib.data._flushData.length; i < l; i++) {
		if (lib.data._flushData[i].elm) {
			lib.data._flushData[i].elm[lib.data.attribute] = null; // remove the reference
		}
	}
	return true;
};

lib.array.push = function (arr, item) {
	// add an element to the end of an array, return the new array length
	var arg = arguments;
	var len = arr.length;
	for (var i = 1, l = arg.length; i < l; i++) {
		arr[len + i - 1] = arg[i];
	}
	return arr.length;
};

lib.array.insert = function (arr, newValue, emptyValue) {
	// inserts the newValue to array to first empty place (place that has value emptyValue)
	// if there is no such value, adds newValue to the end of the array --- RETURNS i (1)
	// if emptyValue is not specified, replaces "null" values by default -- RETURNS new arr.length
	if (typeof emptyValue == "undefined") {
		emptyValue = null;
	}
	var i, l;
	searchForHole:
		for (i = 0, l = arr.length; i < l; i++) {
			if (arr[i] == emptyValue) {
				break searchForHole;
			}
		}
	arr[i] = newValue;
	return i;
};

lib.array.sortBy = function (arr, indexName) {
	// sorts multidimensional array using index "indexName" as a key for sorting
	// elements that don't have this index are put to the end of the array
	var sortFn = function (a, b) {
		var result = 0;
		if (a && b && (typeof a[indexName] != "undefined") && (typeof b[indexName] != "undefined") && (a[indexName] !== null) && (b[indexName] !== null)) {
			if (a[indexName] < b[indexName]) {
				result = -1;
			} else if ( a[indexName] > b[indexName] ) {
				result = 1;
			}
		} else if (b && (typeof b[indexName] != "undefined") && (b[indexName] !== null)) {
			result = -1;
		} else if ((b === null) && (a !== null)) {
			result = -1;
		}
		return result;
	};
	return arr.sort(sortFn);
};






// prevent memory leaks
lib.evt.add(window, "unload", function () { lib.evt.flush(); lib.data.flush(); } );

