/*
	core javascript library for muze modules
	----------------------------------------

	object namespace(string module)
		This method checks if the namespace 'module' is available, and if not
		creates it and registers it. It returns the object the namespace points
		to, so you can create a shorthand for it.
		examples:
			muze.namespace('muze.test');
			
			var myMod = muze.namespace('muze.temp.my.module.with.a.long.name');

	object require(string module) 
		This method only checks if the given module is available (registered).
		If not, it will alert() an error with the missing module, and return
		false. 
		If it is available, it will return the module object.
		
	object include(string url, string namespace)
		This method checks whether the given namespace is already registered. If
		so it doesn't do anything.
		If the namespace is not registered (or not entered), it dynamically loads
		the url as a javascript object (script tag).
		In both cases the method returns the onload handler object. This object
		has one method 'onload', which allows you to specify a function that should
		be run when the javascript is loaded. This function is also run if the
		javascript was already loaded and include didn't actually do anything.
		examples:
			muze.include('muze.test.js', 'muze.test').onload(function() {
				muze.test.run();
			});

	object load(string url, bool waitforme, bool cached)
		This method allows you to easily do ajax calls. If 'waitforme' is true,
		the ajax call is done synchronously, and load will return the responseText.
		Otherwise the call is done asynchronously, and load will return an onload
		handler object, just like include, only in this case the function you
		specify in onload will be called with one argument, namely the responseText.
		If you set 'cached' to true, the url won't be extended with a timestamp,
		allowing the browser to cache the response.
		examples:
			var response = muze.load('ajax.call.html', true);

			muze.load('ajax.call.html').onload(function(response) {
				myDiv.innerHTML = response;
			});
			
*/

var muze = function() {

	/* private methods */

	function getHTTPObject() {
		var xmlhttp;
		/*@cc_on
		@if (@_jscript_version >= 5)
			try {
				xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
			} catch (e) {
				try {
					xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
				} catch (E) {
					xmlhttp = false;
				}
			}
		@else
		xmlhttp = false;
		@end @*/
		if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
			try {
				xmlhttp = new XMLHttpRequest();
			} catch (e) {
				xmlhttp = false;
			}
		}
		return xmlhttp;
	}

	/* private variables */

	var included={};
	var registered={};

	return {
		namespace : function(module) {
			var rest = new String(module);
			var name = '';
			var temp = window;
			var i = rest.indexOf('.');
			while (i != -1) {
				name = rest.substring(0, i);
				if (!temp[name]) {
					temp[name] = {};
				}
				temp = temp[name];
				rest = rest.substring(i+1);
				i = rest.indexOf('.');
			}
			if (rest) {
				if (!temp[rest]) {
					temp[rest] = {};
				}
				temp = temp[rest];
			}
			registered[module]=true;
			return temp;
		},

		require : function(module) {
			var rest = new String(module);
			var name = '';
			var temp = window;
			var i = rest.indexOf('.');
			while (i != -1) {
				name = rest.substring(0, i);
				if (!temp[name]) {
					alert( 'namespace ' + module + ' not found ' );
					return false;
				}
				temp = temp[name];
				rest = rest.substring(i+1);
				i = rest.indexOf('.');
			}
			if (rest) {
				if (!temp[rest]) {
					alert( 'namespace ' + module + ' not found ' );
					return false;
				}
				temp = temp[rest];
			}
			return temp;
		},

		include : function(url, module) {
			var onload_handler = function() { }
			this.onload = function(continuation) {
				// run the method in continuation when the url is loaded
				onload_handler = continuation;
			}
			this._onload = function() {
				onload_handler();
				onload_handler=null;
			}
			// load a javascript library from the given url
			if (!included[url] && (!module || !registered[module])) {
				var script = document.createElement('SCRIPT');
				script.src = url;
				try {
					script.addEventListener('load', function() {
						onload_handler();
						onload_handler = null;
					}, false);
				} catch(e) {
					script.onreadystatechange = function() { 
						if (script.readyState == 'loaded' || script.readyState == 'complete') {
							onload_handler();
							onload_handler = null;
							script.onreadystatechange = null;
						}
					}
				}
				document.getElementsByTagName('HEAD')[0].appendChild(script);
			} else {
				// setTimeout is not optional here, since we have to return
				// (this) first, before the _onload method is called, otherwise
				// there is no way for a user to change 'onload_handler'.
				setTimeout(this._onload, 1);
			}
			return this;
		},

		load : function(url, waitforme, cached) {
			var onload_handler = function() { }
			this.onload = function(continuation) {
				// run the method in continuation when the url is loaded
				onload_handler = continuation;
			}
			// get content from url
			if (!cached) {
				var timestamp=new Date();
				if (url.match(/\?/)) {
					timestamp='&t='+timestamp.getTime();
				} else {
					timestamp='?t='+timestamp.getTime();
				}
			} else {
				var timestamp='';
			}
			var http=getHTTPObject();
			http.open('GET',url+timestamp,!waitforme);
			if (!waitforme) {
				http.onreadystatechange = function() {
					if (http.readyState == 4) {
						var response = http.responseText;
						onload_handler(response);
					}
				}
			}
			http.send(null);
			if (waitforme) {
				return http.responseText;
			} else {
				return this;
			}
		}
	}
}();
/*
	javascript events library for muze modules
	----------------------------------------

	object get(object evt)
		This method returns the event object cross browser. You only need this if you don't
		use muze.event.attach() to attach your event handler, since it already does this for
		you.

		examples:
			function myEventHandler(evt) {
				evt = muze.event.get(evt);
				....
			}

	bool cancel(object evt)
		This method cancels the event, stops propagation, prevents default, in short it kills
		the event dead. Cross browser. It also returns false, so you may assign it directly
		to events you want killed.

		examples:
			function myEventHandler(evt) {
				...
				if (killEvent == true) {
					return muze.event.cancel(evt);
				}
				...
			}

			document.body.onMouseDown = muze.event.cancel;


	bool pass(object evt)
		This method returns true. So you may use it to make explicit that you don't cancel an event.

	mixed attach(object obj, string event, object handler, bool useCapture)
		This method attaches an event handler to an event on an object. It makes sure the event
		gets cleaned on unload, so you won't get memory leaks. It makes sure that 'this' points
		to the object the event is defined on. Important: Returns the handler required for detaching
		the event. This is not the same handler as passed to the attach function!
		arguments:
			obj		DOM object on which to catch the event
			event		name of the event to catch, e.g. 'load', 'click', etc.
			handler		function that handles the event.
			useCapture	Mozilla's useCapture option to addEventListener
		examples:

			...
			var detachHandler = muze.event.attach(document.body, 'load', function() { alert(this.innerHTML); });
			...

	bool detach(object obj, string event, object, handler, bool useCapture)
		This method detaches an event handler from an event on an object.
		arguments:
			obj		DOM objeect on which the event handler was attached
			event		name of the event to remove, e.g. 'load', 'click', etc.
			handler		handler to detach.
			useCapture	Mozilla's useCapture option to addEventListener
		examples:
		
			...
			var detachHandler = muze.event.attach(document.body, 'click', function() { alert('we have a click'); });
			...
			muze.event.detach(document.body, 'click', detachHandler);
			...


	void clean() 
		This method cleans/removes all attached event handlers. It is automatically run on unload of document

*/


muze.namespace('muze.event');

muze.event = function() {

	/* private methods */

	/* private variables */

	var cache=[];

	var events = {

		get:function(evt) {
			if (!evt) {
				evt=window.event;
			}
			if (!evt.target) {
				evt.target=evt.srcElement;
			}
			return evt;
		},

		cancel:function(evt) {
			evt = muze.event.get(evt);
			if (evt.returnValue) {
				evt.returnValue=false;
			} 
			if (evt.preventDefault) {
				evt.preventDefault();
			}
			evt.cancelBubble=true;
			if (evt.stopPropagation) {
				evt.stopPropagation();
			}
			return false;
		},

		pass:function(evt) {
			return true;
		},

		attach:function(ob, event, fp, useCapture) {
			if (ob) {
				function createHandlerFunction(obj, fn){
					var o = new Object;
					o.myObj = obj;
					o.calledFunc = fn;
					o.myFunc = function(e){ 
						var e = muze.event.get(e);
						return o.calledFunc.call(o.myObj, e);
					}
					return o.myFunc;
				}
				var handler=createHandlerFunction(ob, fp);
				cache[cache.length]={ event:event, object:ob, handler:handler, useCapture:useCapture };
				if (ob.addEventListener){
					ob.addEventListener(event, handler, useCapture);
					return handler;
				} else if (ob.attachEvent){
					ob.attachEvent("on"+event, handler);
					return handler;
				} else {
					//FIXME: don't do alerts like this
					alert("Handler could not be attached");
				}
			} else {
				//FIXME: don't do alerts like this
				alert('Object not found');
			}
		},

		detach:function(ob, event, fp, useCapture) {
			if (ob) {
				var item=null;
				for( var i=cache.length-1; i>=0; i--) {
					item = cache[i];
					if( item && item.object == ob && item.event == event && item.handler == fp && item.useCapture == useCapture) {
						if (item.object.removeEventListener) {
							item.object.removeEventListener(item.event, item.handler, item.useCapture);
						} else if (item.object.detachEvent) {
							item.object.detachEvent("on" + item.event, item.handler);
						}
						cache[i]=null;
						return;	
					}
				}
			}
		},

		clean:function() {
			var item=null;
			for (var i=cache.length-1; i>=0; i--) {
				item=cache[i];
				if (item) {
					item.object['on'+item.event]=null;
					if (item.object.removeEventListener) {
						item.object.removeEventListener(item.event, item.handler, item.useCapture);
					} else if (item.object.detachEvent) {
						item.object.detachEvent("on" + item.event, item.handler);
					}
					cache[i]=null;
				}
			}
			item=null;
		}

	}

	events.attach(window, 'unload', events.clean);

	return events;
}();