Element.implement({

	isDisplayed: function(){
		return this.getStyle('display') != 'none';
	},

	isVisible: function(){
		var w = this.offsetWidth,
			h = this.offsetHeight;
		return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.isDisplayed();
	},

	toggle: function(){
		return this[this.isDisplayed() ? 'hide' : 'show']();
	},

	hide: function(){
		var d;
		try {
			// IE fails here if the element is not in the dom
			if ((d = this.getStyle('display')) == 'none') d = null;
		} catch(e){}
		
		return this.store('originalDisplay', d || 'block').setStyle('display', 'none');
	},

	show: function(display){
		return this.setStyle('display', display || this.retrieve('originalDisplay') || 'block');
	},

	swapClass: function(remove, add){
		return this.removeClass(remove).addClass(add);
	}

});


var Mooverlay = new Class({

	Implements:[Options,Events],

	options:{

		width:550,
		animations:true,
		showOnInitialize:false,
		showCloseIcon:true,
		focusOnShow:true,
		focusOptions:{
			offset:{
				y:-20
			}
		},
		forms:{
			captureEvents:true
		},

		positions:{
			y:100
		},

		overlay:{
			closeOnClick:true
		},

		loading:{
			message:'loading, please wait',
			width:550
		}

	},

	initialize:function(name,options) {
		this.init(name,options);
	},

	init:function(name,options) {
		this.name = name;
		this.setOptions(options);
		this.elements = {};
		this.elements.contents = {};

		//setup the instance
		Mooverlay.create(this);

		//frame
		this.elements.frame = new Element('div',{
			'class':'overlay-box o',
			'styles':{
				'position':'absolute',
				'visibility':'hidden',
				'top':0,
				'left':0,
				'width':this.options.width	
			}
		}).injectInside(document.body);

		//set the inner frame for relative positioning
		this.elements.inner = new Element('div',{
			'class':'overlay-inner o',
			'styles':{
				'position':'relative'
			}
		}).injectInside(this.elements.frame);

		//setup the close button
		if(this.options.showCloseIcon) {
			var id = 'overlay-close-element-'+$time();
			this.elements.close = new Element('div',{
				'class':'overlay-close o',
				'id':id,
				'events':{
					'click':this.hide.bind(this)
				}
			}).injectInside(this.elements.inner);
			if(Browser.Engine.trident4) { //ie6
				DD_belatedPNG.fix('#'+id);
			}
		}

		//grid
		this.elements.grid = new Element('table',{
			'class':'overlay-table o',
			'html':'<tr class="t-top">'+
				   ' <td class="t-tl t-c x y xy"></td>'+
				   ' <td class="t-tm y"></td>'+
				   ' <td class="t-tr t-c x y xy"></td>'+
				   '</tr>'+
				   '<tr class="t-middle">'+
				   ' <td class="t-ml x"></td>'+
				   ' <td class="overlay-container o"><div class="overlay-stage"></div></td>'+
				   ' <td class="t-mr x"></td>'+
				   '</tr>'+
				   '<tr class="t-bottom">'+
				   ' <td class="t-bl x y xy"></td>'+
				   ' <td class="t-bm y"></td>'+
				   ' <td class="t-br x y xy"></td>'+
				   '</tr>'
		}).injectInside(this.elements.inner);

		//content
		this.elements.container = this.elements.grid.getElement('.overlay-stage');
		this.elements.content = new Element('div',{
			'class':'overlay-content o'
		}).injectInside(this.elements.container);

		this.resize();

		//make it visible
		this.hide({},false);
		this.elements.frame.setStyle('visibility','visible');

		//check the initialize
		if(this.options.showOnInitialize) {
			this.show();	
		}
	},

	show:function(options) {
		options = options || {};
		if(options.animate === undefined)
		options.animate = this.options.animations;
		if(!this.isShowing()) {
			Mooverlay.show(this,options);
		}
	},

	hide:function(options,animate) {
		options = options || {};
		if(animate !== undefined)
		options.animate = animate;
		if(options.animate === undefined)
		options.animate = this.options.animations;
		Mooverlay.hide(this,options);
	},

	getName:function() {
		return this.name;
	},

	_show:function(inst,options) {
		options = options || {};
		if(inst.getName()==this.getName()) {
			var H = function() {
				this.elements.frame.setStyles({ 'opacity':1,'visibility':'visible' });

				//check focus
				if(this.options.focusOnShow) {
					new Fx.Scroll(window,this.options.focusOptions || {}).toElement(this.elements.frame);	
				}

				//fix the elements
				if(Browser.Engine.trident4) {
					try {
						DD_belatedPNG.fix('.xy');	
					}
					catch(e) {
						
					}
				}

				this.fireEvent('show');
			}.bind(this);
			var content = this.getFocusContent();
			if(content) {
				options.width = options.width || this.activeContent.retrieve('custom-width') || null;
			}
			if(this.options.animations) {
				this.elements.frame.setOpacity(0);
				this.elements.frame.show();
				this.adjust();
				new Fx.Morph(this.elements.frame).start({ 'opacity':[0,1] }).chain(H);
			}
			else {
				this.elements.frame.show();
				this.adjust();
				H();	
			}
		}
	},

	_hide:function(inst,options) {
		options = options || {};
		if(inst.getName()==this.getName()) {
			var H = function() {
				this.elements.frame.hide();
				this.fireEvent('hide');
			}.bind(this);

			var animate = options.animate !== undefined ? options.animate : this.options.animations;
			if(animate) {
				new Fx.Morph(this.elements.frame).start({ 'opacity':0 }).chain(H);
			}
			else {
				H();	
			}
		}
	},

	destroy:function() {
		Mooverlay.destroy(this);
	},

	close:function() {
		this.hide();
	},

	hasOverlay:function() {
		return !! this.options.overlay;
	},

	getSize:function() {
		return this.elements.frame.getSize();
	},

	setSize:function(width,height) {
		this.options.width = width || this.getWidth();
		this.options.height = height || this.getHeight();
		this.resize(false); //this.options.animations);
	},

	getWidth:function() {
		return this.getSize().x;
	},
	
	getHeight:function() {
		return this.getSize().y;
	},
	
	getContent:function() {
		return this.getFocusContent().get('html');
	},

	lockHeight:function() {
		var height = this.getHeight();
		this.elements.container.setStyle('height',height);
	},

	unlockHeight:function() {
		this.elements.container.setStyle('height','auto');
	},

	setContent:function(content,options) {
		options = options || {};
		var key = options.key || 'c-'+$time();
		this.hideContents();
		this.lockHeight();
		var elm = this.elements.contents[key] || new Element('div',{
			'id':key,
			'class':'content',
			'html':content
		}).injectInside(this.elements.container);
		if(options.width && options.width>0) {
			elm.store('custom-width',options.width);
		}
		this.unlockHeight();
		var width = elm.retrieve('custom-width');
		var height = elm.getSize().y;
		if(width || height) {
			this.setSize(width,height);
		}
		this.activeContent = elm;
		this.elements.contents[key]=elm;
		this.fireEvent('contentChange',[elm,this.getContent()]);
	},

	getFocusContent:function() {
		return this.activeContent;
	},

	focusOnContent:function(key) {
		var elm = this.elements.contents[key];
		if(elm) {
			this.activeContent = elm;
			elm.show();
			var width = this.activeContent.retrieve('custom-width');
			if(width)
			this.setWidth(width);
		}
	},

	hideContents:function() {
		$each(this.elements.contents,function(con) {
			con.hide();
		});
	},

	destroyContent:function(key) {
		var elm = this.elements.contents[key];
		if(elm) {
			var id = this.getFocusContent();
			if(id) id = id.id;
			if(elm.id == id) {
				this.activeContent = null;	
			}
			elm.destroy();
			delete this.elements.contents[key];
		}
	},

	destroyFocusContent:function() {
		this.destroyContent(this.getFocusContent().id);
	},

	getAdjustments:function(options) {
		options = options || {};
		var y = options.y || 'middle';
		var x = options.x || 'middle';
		var w = options.w || this.getWidth();
		var h = options.h || this.getHeight();
		var wx = window.getSize().x;
		var wy = window.getSize().y;
		if(x=='middle') {
			x = Math.round((wx - w) / 2);
		}
		if(y=='middle') {
			y = Math.round((wy - h) / 2);	
		}
		return {
			x:x,
			y:y
		};
	},

	adjust:function(options) {
		options = options || {};
		options = this.getAdjustments(options);
		if(this.options.positions) {
			options.y = this.options.positions.y || options.y;	
		}
		this.elements.frame.setStyles({
			'top':options.y,
			'left':options.x
		});
		this.fireEvent('adjust',[{
			x:options.x,
			y:options.y
		}]);
	},

	getOverlayOptions:function() {
		var options = this.options.overlay || {};
		options.animate = this.options.animations;
		return options;
	},

	setAsLoading:function(message) {
		var width;
		if(this.options.loading) {
			width = this.options.loading.width;
			message = this.options.loading.message;
		}
		this.setContent('<div class="loading">'+message+'</div>');
		if(width) {
			this.setWidth(width);	
		}
		this.fireEvent('loading');
	},

	setWidth:function(width,animate) {
		animate = false;//!! (animate != null ? animate : this.options.animations);
		this.options.width = width;
		this.resize(animate);
	},

	getLeft:function() {
		return this.elements.frame.getCoordinates()['left'];
	},

	isShowing:function() {
		return this.elements.frame.getStyle('display')!='none';
	},

	resize:function(animate) {
		var width = this.options.width;
		var minheight = this.options.height;
		var R = function(width) {
			this.elements.frame.setStyle('width',width);
			this.fireEvent('resize',[this.getSize()]);
			this.adjust();
		}.pass([width],this);
		if(animate) {
			var x = this.getLeft();
			var w = this.getWidth();
			var o = this.getAdjustments({
				'w':width,
				'h':minheight>0?minheight:null
			});
			var options = {
				'width':[w,width],
				'left':[x,o.x],
				'top':o.y
			}
			if(minheight>0)
			options['min-height']=minheight;
			new Fx.Morph(this.elements.frame).start(options).chain(R);	
		}
		else R();
	}
});

Mooverlay.HTML = new Class({

	Extends:Mooverlay,

	options:{
		showOnLoading:false,
		showOnLoad:true
	},

	initialize:function(name,options) {
		this.init(name,options);
	},

  setTempContainer:function(contents) {
   if(!this.tempContainer) {
     this.tempContainer = new Element('div',{
        'class':'temp-container',
        'styles':{
          'position':'absolute',
          'top':-9999,
          'left':-9999
        }
      }).injectInside(document.body);
    }
    this.tempContainer.set('html',contents);
  },

  getTempContainer:function() {
    return this.tempContainer;
  },

  removeTempContainer:function() {
    if(this.tempContainer) {
      this.tempContainer.destroy();
      this.tempContainer = null;
    }
  },

	load:function(options) {
		options = $extend({
			'method':'GET',
			'evalScripts':true
		},options);
		options = $extend(this.options.ajax || {},options);
		options = $extend(options,{

			onRequest:function() {
				this.setAsLoading();
			  if(this.options.showOnLoading) {
					this.show();
				}
			}.bind(this),

			onSuccess:function(tree,elms,html,scripts) {
			  this.setTempContainer(html);
			  var container = this.getTempContainer();
			  var code = container.getElement('code#static');
			  var files = null;
			  if(code) {
			    var contents = code.innerHTML;
			    var files = JSON.decode(contents);
			  }
			  this.setContent(html,{
					width:this.options.width		
				});
			  if(files) {
				files.each(function(file) {
				  new Asset.javascript(file);
				})
			  }
				if(this.options.showOnLoad) {
					this.show();
				}
				this.fireEvent('contentLoaded');
			}.bind(this),
			
			onFailure:function() {
				this.error();
			}.bind(this),

			onComplete:function() {
				this.request = null;
			}.bind(this),

			onCancel:function() {
				this.request = null;
				this.fireEvent('cancel');
			}.bind(this)

		});
		this.request = new Request.HTML(options);
		this.request.send();
	},

	cancel:function() {
		if(this.request) {
			this.request.cancel();
		}
	},

	error:function() {
		var message = this.options.errorMessage || 'there was an error...';
		this.setContent(
			'<div class="message error error-message">'+
			message+
			'</div>'
		);
	}

});

Mooverlay.extend({

	create:function(inst) {
		if(!this.elements) {
			this.elements = {};	
		}
		if(!this.instances) {
			this.instances = {};
		}
		this.instances[inst.getName()]=inst;
	},

	destroyAll:function() {
		var instances = this.getInstances();
		instances.each(function(inst) {
			this.destroy(inst);					
		});
		this.instances = {};
	},

	destroy:function(inst) {
		var name = inst.getName();
		inst = this.instances[name];
		if(inst) {
			inst.elements.frame.destroy();
			delete this.instances[name];
		}
	},

	hideAll:function() {
		var instances = this.getInstances();
		if(instances) {
			instances.each(function(inst) {
				if(inst && inst.getName) {
					this.hide(inst);
				}
			}.bind(this));
		}
	},

	getInstances:function() {
		var elms = [], instances = this.instances;
		if(instances) {
			$each(instances,function(i) {
				if(i && $type(i)=='object') {
					elms.push(i);	
				}
			});
			return elms.length>0 ? elms : null;
		}
	},

	hideOthers:function(inst) {
		var name = inst.getName();
		var instances = this.getInstances();
		if(instances) {
			$each(instances,function(inst) {
				if(inst && inst.getName && inst.getName()!=name) {
					this.hide(inst);
				}
			});
		}
	},

	hide:function(inst,o) {
		var options = inst.getOverlayOptions();
		options.animate = o && o.animate !== undefined ? o.animate : options.animate
		options.animate = false;
		this.hideOverlay(options);
		inst._hide(inst,options);
	},

	getInstance:function(name) {
		return this.instances ? this.instances[name] : null;
	},

	showOverlay:function(opacity,options) {
		if(!this.elements.overlay) {
			this.elements.overlay = new Element('div',{
				'class':'overlay-screen',
				'styles':{
					'position':'fixed',
					'top':0,
					'left':0,
					'right':0,
					'bottom':0
				},
				'events':{
					'click':this.onOverlayClick.bind(this)	
				}
			}).injectInside(document.body);

			//ie6
			if(Browser.Engine.trident4) {
				window.addEvent('resize',function() {
					var sizes = window.getScrollSize();
					var o = this.elements.overlay;
					o.setStyles({
						'position':'absolute',
						'top':0,
						'left':0,
						'height':sizes.y,
						'width':sizes.x
					});
				}.bind(this));
				window.fireEvent('resize');
			}
		}

		this.elements.overlay.setStyle('cursor',options.closeOnClick ? 'pointer' : 'default');	

		opacity = opacity || 0.5;
		var H = (function(overlay,opacity) {
			overlay.setStyles({
				opacity:opacity,
				visibility:'visible',
				display:'block'
			});
		}.pass([this.elements.overlay,opacity]));
		if(options.animate) {
			opacity = opacity || options.opacity;
			this.elements.overlay.setStyles({
				opacity:0,
				visibility:'visible',
				display:'block'
			});
			new Fx.Morph(this.elements.overlay).start({
				'opacity':[0,opacity]
			}).chain(H);
		}
		else {
			H();
		}
	},

	onOverlayClick:function(event) {
		event.stop();
		var inst = this.getActiveInstance();
		if(this.isOverlayClickEvent() && this.isOverlayShowing()) {
			this.hideAll();	
		}
	},

	isOverlayClickEvent:function() {
		var inst = this.getActiveInstance();
		if(inst && inst.hasOverlay()) {
			var options = inst.getOverlayOptions();
			return !! options.closeOnClick;
		}
		return false;
	},

	hideOverlay:function(options) {
	  options = options || {};
		if(this.elements.overlay) {
			var H = (function(overlay) {
				overlay.hide();
			}.pass(this.elements.overlay));
			if(options.animate) {
				this.elements.overlay.setStyles({
					visibility:'visible',
					display:'block'
				});
				new Fx.Morph(this.elements.overlay).start({
					'opacity':0										  
				}).chain(H);
			}
			else {
				H();	
			}
		}
	},

	hasOverlay:function() {
		return !! this.elements.overlay;
	},

	isOverlayShowing:function() {
		return this.hasOverlay() && this.elements.overlay.getStyle('display')!='none';
	},

	show:function(inst,options) {
		this.activeInstance = inst;
		//this.hideOthers(inst);
		if(inst.hasOverlay()) {
			var options = inst.getOverlayOptions();
			options.animate = false;
			this.showOverlay(options.opacity,options);
		}
		if(!inst.isShowing()) {
			inst._show(inst);
		}
	},

	getActiveInstance:function() {
		return this.activeInstance;
	}
});
