/*
	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;
}();muze.namespace('muze.util');

muze.util = function() {

	return {
		isString : function(str) {
			if (typeof str == 'string') {
				return true;
			}
			if (typeof str == 'object') {
				var criterion = str.constructor.toString().match(/string/i); 
				return (criterion != null);  
			}
			return false;
		},
		isArray : function(arr) {
			if (typeof arr == 'array') {
				return true;
			}
			if (typeof arr == 'object') {
				var criterion = arr.constructor.toString().match(/array/i); 
				return (criterion != null);  
			}
			return false;
		},
		setOpacity : function(object, opacity) {
			if (opacity >= 1) {
				opacity = 1;
			}
			if (opacity < 0) {
				opacity = 0;
			}

			object.style.filter = "alpha(opacity=" + (opacity*100) + ")";
			object.style.MozOpacity = opacity;
			object.style.KHTMLOpacity = opacity;
			object.style.opacity = opacity;
		},
		getSize : function(object) {
			return { 
				height : parseInt(object.offsetHeight),
				width : parseInt(object.offsetWidth)
			}
		},
		getScrollSize : function() {
			var xScroll, yScroll;
	
			if (window.innerHeight && window.scrollMaxY) {	
				xScroll = document.body.scrollWidth;
				yScroll = window.innerHeight + window.scrollMaxY;
			} else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
				xScroll = document.body.scrollWidth;
				yScroll = document.body.scrollHeight;
			} else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
				xScroll = document.body.offsetWidth;
				yScroll = document.body.offsetHeight;
			}
			return {
				width : xScroll,
				height : yScroll
			}
		},
		getScrollOffset : function(object) {
			if (!object) {
				var iebody = (document.compatMode && document.compatMode!='BackCompat') ? document.documentElement : document.body;
				return {
					x : document.all? iebody.scrollLeft : window.pageXOffset,
					y : document.all? iebody.scrollTop: window.pageYOffset
				}
			} else {
				return {
					x : object.scrollLeft,
					y : object.scrollTop
				}
			}
		},
		getPageSize : function() {
			var scrollSize = muze.util.getScrollSize();
			var windowSize = muze.util.getWindowSize();
			
			// for small pages with total height less then height of the viewport
			if(scrollSize.height < windowSize.height){
				pageHeight = windowSize.height;
			} else { 
				pageHeight = scrollSize.height;
			}

			// for small pages with total width less then width of the viewport
			if(scrollSize.width < windowSize.width){	
				pageWidth = windowSize.width;
			} else {
				pageWidth = scrollSize.width;
			}

			return {
				width : pageWidth,
				height : pageHeight
			}
		},
		getWindowSize : function() {
			var windowWidth, windowHeight;
			if (self.innerHeight) {	// all except Explorer
				windowWidth = self.innerWidth;
				windowHeight = self.innerHeight;
			} else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
				windowWidth = document.documentElement.clientWidth;
				windowHeight = document.documentElement.clientHeight;
			} else if (document.body) { // other Explorers
				windowWidth = document.body.clientWidth;
				windowHeight = document.body.clientHeight;
			}
			return {
				width : windowWidth,
				height : windowHeight
			}
		}
	}
}();
/**********************************************************
 Adapted from the sortable lists example by Tim Taylor
 http://tool-man.org/examples/sorting.html
 
 **********************************************************/

muze.namespace('muze.util');

muze.util.coordinates = function() {
	var Coordinate = function(x, y) {
		this.x = x;
		this.y = y;
	}

	Coordinate.prototype.toString = function() {
		return "(" + this.x + "," + this.y + ")";
	}

	Coordinate.prototype.plus = function(that) {
		return new Coordinate(this.x + that.x, this.y + that.y);
	}

	Coordinate.prototype.minus = function(that) {
		return new Coordinate(this.x - that.x, this.y - that.y);
	}

	Coordinate.prototype.distance = function(that) {
		var deltaX = this.x - that.x;
		var deltaY = this.y - that.y;	
		return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
	}

	Coordinate.prototype.max = function(that) {
		var x = Math.max(this.x, that.x);
		var y = Math.max(this.y, that.y);
		return new Coordinate(x, y);
	}

	Coordinate.prototype.constrain = function(min, max) {
		if (min.x > max.x || min.y > max.y) return this;

		var x = this.x;
		var y = this.y;

		if (min.x != null) x = Math.max(x, min.x);
		if (max.x != null) x = Math.min(x, max.x);
		if (min.y != null) y = Math.max(y, min.y);
		if (max.y != null) y = Math.min(y, max.y);

		return new Coordinate(x, y);
	}

	Coordinate.prototype.reposition = function(element) {
		element.style["top"] = this.y + "px";
		element.style["left"] = this.x + "px";
	}

	Coordinate.prototype.equals = function(that) {
		if (this == that) return true;
		if (!that || that == null) return false;

		return this.x == that.x && this.y == that.y;
	}

	// returns true of this point is inside specified box
	Coordinate.prototype.inside = function(northwest, southeast, scrolloffset) {
		if ((this.x >= northwest.x - scrolloffset.x) && (this.x <= southeast.x - scrolloffset.x) &&
			(this.y >= northwest.y - scrolloffset.y) && (this.y <= southeast.y - scrolloffset.y)) {
			return true;
		}
		return false;
	}

	Coordinate.prototype.originside = function(northwest, southeast) {
		if ((this.x >= northwest.x) && (this.x <= southeast.x) &&
			(this.y >= northwest.y) && (this.y <= southeast.y)) {
			return true;
		}
		return false;
	}

	return {
		ORIGIN : new Coordinate(0, 0),

		northwestPosition : function(element) {
			var x = parseInt(element.style.left);
			var y = parseInt(element.style.top);
			return new Coordinate(isNaN(x) ? 0 : x, isNaN(y) ? 0 : y);
		},

		southeastPosition : function(element) {
			return muze.util.coordinates.northwestPosition(element).plus(
					new Coordinate(element.offsetWidth, element.offsetHeight));
		},

		northwestOffset : function(element, isRecursive) {
			var offset = new Coordinate(element.offsetLeft, element.offsetTop);
			if (!isRecursive) {
				return offset;
			}
			var parent = element.offsetParent;
			while (parent) {
				offset = offset.plus(
					new Coordinate(parent.offsetLeft, parent.offsetTop));
				parent = parent.offsetParent;
			}
			return offset;
		},

		southeastOffset : function(element, isRecursive) {
			return muze.util.coordinates.northwestOffset(element, isRecursive).plus(
				new Coordinate(element.offsetWidth, element.offsetHeight));
		},

	        scrollOffset : function(element, skipBody) {
	                var offset = new Coordinate(element.parentNode.scrollLeft, element.parentNode.scrollTop);
	                var parent = element.parentNode;
	                while (parent && parent.parentNode && typeof(parent.parentNode.scrollLeft) == 'number' && (!skipBody || parent.parentNode.tagName!='BODY')) {
        	                offset = offset.plus(
        	                        new Coordinate(parent.parentNode.scrollLeft, parent.parentNode.scrollTop)
        	                );
        	                parent = parent.parentNode;
		         }
	                return offset;
	        },

		getOffsets : function(element, isRecursive) {
			// FIXME: this is not cross browser, the nwOffset is borken, the northwestOffset() method works. probably a bug with offsetParent / parentNode
			var nwOffset = new Coordinate(element.offsetLeft, element.offsetTop);
			var scrOffset = new Coordinate(0,0);
			if (element.parentNode && typeof(element.parentNode.scrollLeft) == 'number') {
				scrOffset = new Coordinate(element.parentNode.scrollLeft, element.parentNode.scrollTop);
			}
			if (!isRecursive) {
				var seOffset = nwOffset.plus(new Coordinate(element.offsetWidth, element.offsetHeight));
				return Array(nwOffset, seOffset, scrOffset);
			}
			var parent = element.parentNode;
			while (parent&& parent.parentNode && typeof(parent.parentNode.scrollLeft) == 'number') {
				nwOffset = nwOffset.plus(new Coordinate(parent.offsetLeft, parent.offsetTop));
				if (parent.parentNode && typeof(parent.parentNode.scrollLeft) == 'number') {
					scrOffset = scrOffset.plus(new Coordinate(parent.parentNode.scrollLeft, parent.parentNode.scrollTop));
				}
				parent = parent.parentNode;
			}
			var seOffset = nwOffset.plus(new Coordinate(element.offsetWidth, element.offsetHeight));
			return Array(nwOffset, seOffset, scrOffset);
		},

		visibleOffsets : function(el) {
			function getOverflow(el) {
				function getStyle(el, prop) {
					if (document.defaultView && document.defaultView.getComputedStyle) {
						return document.defaultView.getComputedStyle(el, null)[prop];
					} else if (el.currentStyle) {
						return el.currentStyle[prop];
					} else {
						return el.style[prop];
					}
				}
				return getStyle(el, 'overflow');
			}
			
			// return the topleft / bottomright coordinates which describe the visible part of the element
			var nwOffset = muze.util.coordinates.northwestOffset(el, true);
			var scrOffset = muze.util.coordinates.scrollOffset(el, true);
			nwOffset.x -= scrOffset.x;
			nwOffset.y -= scrOffset.y;
			seOffset = { x : nwOffset.x + el.offsetWidth, y : nwOffset.y + el.offsetHeight };
			
	                var parent = el.parentNode;
	                while (parent && parent.parentNode && typeof(parent.parentNode.scrollLeft) == 'number') {
				if ((parent.tagName == 'BODY') || (getOverflow(parent)!='visible')) {
					var nwPos = muze.util.coordinates.northwestOffset(parent, true);
					var scrPos = muze.util.coordinates.scrollOffset(parent, true);
					nwPos.x -= scrPos.x;
					nwPos.y -= scrPos.y;
					var sePos = { x : nwPos.x + parent.offsetWidth, y : nwPos.y + parent.offsetHeight }
					if (sePos.x<seOffset.x) {
						seOffset.x = sePos.x;
					}
					if (sePos.y<seOffset.y) {
						seOffset.y = sePos.y;
					}
					if (nwPos.x>nwOffset.x) {
						nwOffset.x = nwPos.x;
					}
					if (nwPos.y>nwOffset.y) {
						nwOffset.y = nwPos.y;
					}
				}
				parent = parent.parentNode;
			}
			if ((nwOffset.x>seOffset.x) || (nwOffset.y>seOffset.y)) {
				return false;
			} else {
				return { nw : nwOffset, se : seOffset };
			}
		},

		
		fixEvent : function(event) {
			event.windowCoordinate = new Coordinate(event.clientX, event.clientY);
		},

		Coordinate : function(x, y) {
			return new Coordinate(x, y);
		}
	}
}();muze.namespace('muze.tools');

muze.require('muze.util.coordinates');

muze.tools.hiderdivs = function() {
	// private vars and methods
	var Coordinates	= muze.util.coordinates;
	var hiderDivs	= null;
	var hidden	= false;
	var selectedDiv	= null;
	var blankPage	= '';	
	var bodyOverflowStyle = '';
	var zIndex	= 1000;
	
	function makeHiderDiv() {
		hiderDiv			= document.createElement("div");
		hiderDiv.style.position		= "absolute";
		hiderDiv.style.backgroundColor	= "#888888";
		
		if (blankPage) {
			var iframe		= document.createElement("IFRAME");
			iframe.src		= blankPage;
			iframe.frameBorder	= "0";
			iframe.style.border	= "none";
			iframe.style.width	= "100%";
			iframe.style.height	= "100%";
			muze.util.setOpacity(iframe, 0);
			hiderDiv.appendChild(iframe);
		}
		
		hiderDiv.style.position	= "absolute";
		hiderDiv.style.zIndex	= 100;
		muze.util.setOpacity(hiderDiv, 0.5);
		hiderDiv.onClick	= function() {
			Event.cancelBubble	= true;
			return false;
	      	}
		hiderDiv.style.overflow	= "hidden";
		return hiderDiv;
	}
	
	function positionHiderDiv(div, top, left, width, height, z) {
		div.style.top		= top + 'px';
		div.style.left		= left + 'px';
		div.style.width		= width + 'px';
		div.style.height	= height + 'px';
		div.style.zIndex	= z;
	}

	function showDiv(div) {
		var size = muze.util.getSize(div);
		if (size.width==0 || size.height==0) {
			div.style.display = 'none';
		} else {
			div.style.display = 'block';
		}
	}

	function hideDiv(div) {
		div.style.display = 'none';
	}
	
	return { //public api
		insert : function(options) {
			if (options) {
				if (options.blankPage) {
					blankPage = options.blankPage;
				}
				if (options.zIndex) {
					zIndex = options.zIndex;
				}
			}
			hiderDivs = {'top' : null, 'left' : null, 'right' : null, 'bottom' : null}
			for (var i in hiderDivs) {
				hiderDivs[i] = makeHiderDiv();
				document.body.appendChild(hiderDivs[i]);
			}
		},
		remove : function() {
			if (hiderDivs) {
				for (div in hiderDivs) {
					document.body.removeChild(hiderDivs[div]);
				}
			}
			hiderDivs	= null;
			hidden		= false;
			selectedDiv	= null;
			document.getElementsByTagName("BODY")[0].style.overflow = bodyOverflowStyle;
		},
		show : function() {
			if (hiderDivs) {
				for (var i in hiderDivs) {
					showDiv(hiderDivs[i]);
				}
			}
			hidden = true;
		},
		hide : function()  {
			if (hiderDivs) {
				for (var i in hiderDivs) {
					hideDiv(hiderDivs[i]);
				}
			}
			hidden = false;
		},
		toggle : function() {
			if (!hidden) {
				this.show();
			} else {
				this.hide();
			}
		},
		make : function(object) { //deprecated, use select
			return this.select(object);
		},
		select : function(object) {
			if (hidden) {
				this.remove();
			}

			hidden		= true;
			selectedDiv	= object;

			if (!bodyOverflowStyle) {
				bodyOverflowStyle = document.body.style.overflow;
			}
			document.getElementsByTagName("BODY")[0].style.overflow = "hidden";

			var screenSize	= muze.util.getPageSize();

			if (!hiderDivs) {
				this.insert();
			}

			if (object) {
				var selected	= muze.util.getSize(object);
				var offset	= muze.util.coordinates.northwestOffset(object, true);
				selected.left	= offset.x;
				selected.top	= offset.y;
				selected.right	= selected.left + selected.width;
				selected.bottom	= selected.top + selected.height;

				var rightWidth	= screenSize.width - selected.right;
				if (rightWidth < 0) {
					rightWidth = 0;
				}
				var bottomHeight= screenSize.height - (selected.bottom);
				if (bottomHeight < 0) {
					bottomHeight = 0;
				}
			
				positionHiderDiv(hiderDivs.top, 0, 0, screenSize.width, selected.top, zIndex);
				positionHiderDiv(hiderDivs.left, selected.top, 0, selected.left, selected.height, zIndex);
				positionHiderDiv(hiderDivs.right, selected.top, selected.right, rightWidth, selected.height, zIndex);
				positionHiderDiv(hiderDivs.bottom, selected.bottom, 0, screenSize.width, bottomHeight, zIndex);
			} else { // hide everything with one hiderDiv
				positionHiderDiv(hiderDivs.top, 0, 0, screenSize.width, screenSize.height, zIndex);
				positionHiderDiv(hiderDivs.left, 0, 0, 0, 0, 0);
				positionHiderDiv(hiderDivs.right, 0, 0, 0, 0, 0);
				positionHiderDiv(hiderDivs.bottom, 0, 0, 0, 0, 0);
			}
			this.show();
		},
		addCallback : function(action, callback) {
		},
		removeCallback : function(action, callback) {
		},
		addEventListener : function(event, handler) {
		},
		removeEventListener : function(event, handler) {
		}
	}
}();muze.namespace('muze.widgets');

muze.require('muze.util');

muze.widgets.lightbox = function() {
	var lightBox = null;
	var hiderDiv = false;
	var resizeHandler = null;
	
	function makeDiv(id, style, children) {
		var div = document.createElement('DIV');
		div.id = id;
		if (style) {
			for (var i in style) {
				div.style[i] = style[i];
			}
		}
		if (children) {
			for (var i in children) {
				div.appendChild(children[i]);
			}
		}
		return div;		
	}

	function clearDiv(div) {
		while (div.firstChild) {
			div.removeChild(div.firstChild);
		}
	}
	
	function insertBox() {
		var i =0;
		lightBox = makeDiv('lightBox', { 'display' : 'none', 'position' : 'absolute' },
		[	
			makeDiv('lightBoxHeader', '', [
				makeDiv('lightBoxClose', { 'position' : 'absolute', 'top' : '0px', 'right' : '0px' }),
				makeDiv('lightBoxTitle')
			]),
			makeDiv('lightBoxContent')
		]);
		// attach event handlers
		document.body.appendChild(lightBox);
	}

	function setTitle(title) {
		var size = muze.util.getSize(lightBox);
		var titleDiv = document.getElementById('lightBoxTitle');
		clearDiv(titleDiv);
		if (muze.util.isString(title)) {
			titleDiv.appendChild(document.createTextNode(title));
			titleDiv.style.display = 'block';
		} else if (title) {
			titleDiv.appendChild(title);
			titleDiv.style.display = 'block';
		} else {
			titleDiv.style.display = 'none';		
		}
		setHeight();
	}
	
	function setWidth(width) {
		width = parseInt(width);
		if (!width) {
			width = 640;
		}
		lightBox.style.width = width + "px";
		centerBox();
	}
	
	function setHeight(height) {
		function getStyleValue(el, style) {
			if (document.defaultView && document.defaultView.getComputedStyle) {
				return document.defaultView.getComputedStyle(el, null)[style];
			} else if (el.currentStyle[style]) {
				return el.currentStyle[style];
			} else {
				return el.style[style];
			}
		}
		function calcPixel(element, value) {
			if (!value) {
				return 0;
			}
			var PIXEL = /^\d+(px)?$/i;
			if (!PIXEL.test(value) && element.runtimeStyle) {
				var style = element.style.left;
				var runtimeStyle = element.runtimeStyle.left;
				element.runtimeStyle.left = element.currentStyle.left;
				element.style.left = value || 0;
				value = element.style.pixelLeft;
				element.style.left = style;
				element.runtimeStyle.left = runtimeStyle;
			}
			return parseInt(value);
		}
		function getMarginHeight(el) {
			return calcPixel(el, getStyleValue(el, 'marginTop')) + calcPixel(el, getStyleValue(el, 'marginBottom'));
		}
		function getBorderHeight(el) {
			return calcPixel(el, getStyleValue(el, 'borderTopSize')) + calcPixel(el, getStyleValue(el, 'borderBottomSize'));
		}
		function getPaddingHeight(el) {
			return calcPixel(el, getStyleValue(el, 'paddingTop')) + calcPixel(el, getStyleValue(el, 'paddingBottom'));
		}

		height = parseInt(height);
		if (!height) {
			var size = muze.util.getSize(lightBox);
			height = size.height;
		} else {
			lightBox.style.height = height + 'px';
		}
		var headerHeight = muze.util.getSize(document.getElementById('lightBoxHeader')).height;
		var contentHeight = height - headerHeight;
		// substract content.margin, content.border, lightbox.padding
		var content = document.getElementById('lightBoxContent');
		var margin = getMarginHeight(content);
		var border = getBorderHeight(content);
		var border2 = getBorderHeight(lightBox);
		var padding = getPaddingHeight(lightBox);
		contentHeight -= (margin+border+border2+padding);
		document.getElementById('lightBoxContent').style.height = contentHeight + 'px';
		centerBox();
	}
	
	function showBox(options) {
		lightBox.style.display = 'block';
		centerBox();
		// show hiderdiv if set
		if (options.hiderDiv) {
			hiderDiv = true;
			if (!options.zIndex) {
				options.zIndex=1000;
			}
			muze.tools.hiderdivs.insert(options);
			muze.tools.hiderdivs.select();
			lightBox.style.zIndex = 1001;
		}
		resizeHandler = muze.event.attach(window, 'resize', function() {
			centerBox();
			if (options.hiderDiv) {
				muze.tools.hiderdivs.select();
			}
		});
		if (!options.hideCloseButton) {
			document.getElementById('lightBoxClose').style.display='block';
			muze.event.attach(document.getElementById('lightBoxClose'), 'click', function() {
				muze.widgets.lightbox.close();
			});
		} else {
			document.getElementById('lightBoxClose').style.display='none';
		}
	}
	
	function hideBox() {
		clearDiv(document.getElementById('lightBoxContent'));
		lightBox.style.display = 'none';
		// hide hiderdiv if set
		if (hiderDiv) {
			hiderDiv = false;
			muze.tools.hiderdivs.remove();
		}
		muze.event.detach(window, 'resize', resizeHandler);
	}
	
	function centerBox() {
		var screenSize = muze.util.getWindowSize();
		var mySize = muze.util.getSize(lightBox);
		var scrollOffset = muze.util.getScrollOffset();
		var center = { 
			x : ( screenSize.width / 2 ) + scrollOffset.x,
			y : ( screenSize.height / 2 ) + scrollOffset.y
		}
		lightBox.style.top = (center.y - Math.ceil(mySize.height / 2)) + 'px';
		lightBox.style.left = (center.x - Math.ceil(mySize.width / 2)) + 'px';
	}
	
	function setIFrame(url) {
		var iframe = document.createElement('IFRAME');
		iframe.id = 'lightBoxIFrame';
		iframe.src = url;
		iframe.setAttribute('frameborder', 'no');
		iframe.style.border = '0px';
		iframe.style.width = '100%';
		iframe.style.height = '100%';
		var content = document.getElementById('lightBoxContent');
		var contentSize = muze.util.getSize(content);
		iframe.style.width = contentSize.width+'px';
		iframe.style.height = contentSize.height+'px';
		clearDiv(content);
		content.appendChild(iframe);
	}
	
	function setImage(url) {
		var img = document.createElement('IMG');
		img.id = 'lightBoxImage';
		img.src = url;
		img.style.border = '0px';
		var content = document.getElementById('lightBoxContent');
		clearDiv(content);
		content.appendChild(img);
	}
	
	function setClassName(className) {
		lightBox.className = className;
	}

	function setToImageSize() {
		var size = muze.util.getSize(document.getElementById('lightBoxImage'));
		setHeight(size.height);
		setWidth(size.width);
	}
	
	function walkList(list, method, arguments) {
		if (muze.util.isArray(list)) {
			for (var i in list) {
				walkList(list[i], method, arguments);
			}
		} else if (muze.util.isString(list)) {
			if (list.match(/^./)) {
				// class
				var className = list.substr(1);
				var links = document.getElementsByTagName('A');
				var re = new RegExp('\\b'+className+'\\b');
				for (var i=0; i<links.length; i++) {
					if (links.item(i).className && links.item(i).className.match(re)) {
						method(links[i], arguments);
					}
				}
			} else if (list.match(/^#/)) {
				// id
				var id = list.substr(1);
				var link = document.getElementById(id);
				if (link) {
					method(link, arguments);
				}
			}
		} else {
			method(list, arguments);
		}
	}
	
	return {
		attach : function(list, options) {
			if (!options) {
				options = {};
			}
			walkList(list, function(item) {
				var handler = muze.event.attach(item, 'click', function(evt) {
					if (options.title!==false && item.title && !options.title) {
						options.title = item.title;
					}
					if (options.image!==false) {
						var img = item.getElementsByTagName('IMG').item(0);
						if (img) {
							options.image = true;
						}
					}
					if (item.className) {
						options.className += ' '+item.className;
					}
					muze.widgets.lightbox.open(item.href, options);
					return muze.event.cancel(evt);
				});
				item.lightBoxHandler = handler;
			});
		},
		detach : function(list) {
			walkList(list, function(item) {
				if (item.lightBoxHandler) {
					muze.event.detach(item, 'click', item.lightBoxHandler);
					item.lightBoxHandler = null;
				}
			});
		},
		get : function() {
			return lightBox;
		},
		open : function(url, options) {
			if (!options) {
				options = {};
			}
			if (!lightBox) {
				insertBox();
			}
			showBox(options);
			this.set(options);
			if (options.image) {
				setImage(url);
				if (!options.height && !options.width) {
					setToImageSize();
					// FIXME: add optional padding
				}
			} else {
				setIFrame(url);
			}
		},
		close : function() {
			hideBox();
		},
		set : function(options) {
			if (!options) {
				options = {};
			}
			if (options.width) {
				setWidth(options.width);
			}
			setHeight(options.height);
			setTitle(options.title);
			if (options.className) {
				setClassName(options.className);
			}
		},
		init : function(options) {
			if (!options) {
				options = {
					'hiderDiv' : true
				}
			}
			this.attach('.lightbox', options)
		}
	}
}();