/**
 * tools.overlay 1.1.0 - Overlay HTML with eye candy.
 * 
 * Copyright (c) 2009 Tero Piirainen
 * http://flowplayer.org/tools/overlay.html
 *
 * Dual licensed under MIT and GPL 2+ licenses
 * http://www.opensource.org/licenses
 *
 * Launch  : March 2008
 * Date: ${date}
 * Revision: ${revision} 
 */
(function($) { 

	// static constructs
	$.tools = $.tools || {};
	
	$.tools.overlay = {
		
		version: '1.1.0',
		
		addEffect: function(name, loadFn, closeFn) {
			effects[name] = [loadFn, closeFn];	
		},
	
		conf: {  
			top: '10%', 
			left: 'center',
			absolute: false,
			
			speed: 'normal',
			closeSpeed: 'fast',
			effect: 'default',
			
			close: null,	
			oneInstance: true,
			closeOnClick: true,
			closeOnEsc: true, 
			api: false,
			expose: null,
			
			// target element to be overlayed. by default taken from [rel]
			target: null 
		}
	};

	
	var effects = {};
		
	// the default effect. nice and easy!
	$.tools.overlay.addEffect('default', 
		
		/* 
			onLoad/onClose functions must be called otherwise none of the 
			user supplied callback methods won't be called
		*/
		function(onLoad) { 
			this.getOverlay().fadeIn(this.getConf().speed, onLoad); 
			
		}, function(onClose) {
			this.getOverlay().fadeOut(this.getConf().closeSpeed, onClose); 			
		}		
	);
	
		
	var instances = [];		

	
	function Overlay(trigger, opts) {		
		
		// private variables
		var self = this, 
			 w = $(window), 
			 closers,
			 overlay,
			 opened,
			 expose = opts.expose && $.tools.expose.version;
		
		// get overlay and triggerr
		var jq = opts.target || trigger.attr("rel");
		overlay = jq ? $(jq) : null || trigger;	
		
		
		// if trigger is given - assign it's click event
		if (trigger) {
			trigger.click(function(e) {				
				self.load();
				return e.preventDefault();
			});
		}   			
		
		// generic binding function
		function bind(name, fn) {
			$(self).bind(name, function(e, args)  {
				if (fn && fn.call(this) === false && args) {
					args.proceed = false;	
				}	
			});	 
			return self;
		}
		
		// bind all callbacks from configuration
		$.each(opts, function(name, fn) {
			if ($.isFunction(fn)) { bind(name, fn); }
		});   
		
		
		// API methods  
		$.extend(self, {

			load: function() {
				
				// can be opened only once
				if (self.isOpened()) { return self; } 

				// close other instances?
				if (opts.oneInstance) {
					$.each(instances, function() {
						this.close();
					});
				}
				
				// onBeforeLoad
				var p = {proceed: true};
				$(self).trigger("onBeforeLoad", p);				
				if (!p.proceed) { return self; }						

				// possible expose effect
				if (expose) { overlay.expose().load(); }				
				
				// calculate end position 
				var top = opts.top;
				if (typeof top == 'string')  {
					top = parseInt(top, 10) / 100 * w.height();			
				}
					
				var left = opts.left;

				// get overlay dimensions
				var oWidth = overlay.outerWidth({margin:true});
				var oHeight = overlay.outerHeight({margin:true}); 
				
				
				if (top == 'center') { top = Math.max((w.height() - oHeight) / 2, 0); }
				if (left == 'center') { left = Math.max((w.width() - oWidth) / 2, 0); }
				
				if (!opts.absolute)  {
					top += w.scrollTop();
					left += w.scrollLeft();
				} 
				
				// position overlay
				overlay.css({top: top, left: left, position: 'absolute'}); 
				
		 		// load effect  
				effects[opts.effect][0].call(self, function() {
					$(self).trigger("onLoad");		
					opened = true;
				}); 
				
		
				// when window is clicked outside overlay, we close
				if (opts.closeOnClick) {					
					$(document).bind("click.overlay", function(evt) { 
						if (!self.isOpened()) { return; }
						var et = $(evt.target); 
						if (et.parents(overlay).length > 1) { return; }
						$.each(instances, function() {
							this.close();
						}); 
					});						
				}						
				
				// keyboard::escape
				if (opts.closeOnEsc) {
					
					// one callback is enough if multiple instances are loaded simultaneously
					$(document).unbind("keydown.overlay").bind("keydown.overlay", function(evt) {
						if (evt.keyCode == 27) {				
							$.each(instances, function() {
								this.close();
							});	 
						}
					});			
				}

				return self; 
			}, 
			
			close: function() {
				
				if (!self.isOpened()) { return self; }
				
				var p = {proceed: true};
				$(self).trigger("onBeforeClose", p);				
				if (!p.proceed) { return self; } 
				
				// close effect
				effects[opts.effect][1].call(self, function() {
					opened = false;
					$(self).trigger("onClose"); 
				});
				
				// if all instances are closed then we unbind the keyboard / clicking actions
				var allClosed = true;
				$.each(instances, function() {
					if (this.isOpened()) { allClosed = false; }
				});				
				
				if (allClosed) {
					$(document).unbind("click.overlay").unbind("keydown.overlay");		
				}
				
							
				return self;
			}, 
			
			// @deprecated
			getContent: function() {
				return overlay;	
			}, 
			
			getOverlay: function() {
				return overlay;	
			},
			
			getTrigger: function() {
				return trigger;	
			},
			
			getClosers: function() {
				return closers;	
			},			

			isOpened: function()  {
				return opened;
			},
			
			// manipulate start, finish and speeds
			getConf: function() {
				return opts;	
			},

			// callback functions
			onBeforeLoad: function(fn) {
				return bind("onBeforeLoad", fn); 		
			},
			
			onLoad: function(fn) {
				return bind("onLoad", fn); 		
			},
			
			onBeforeClose: function(fn) {
				return bind("onBeforeClose", fn); 		
			},
			 
			onClose: function(fn) {
				return bind("onClose", fn); 		
			} 			
			
		});  
			

		// exposing effect
		if (expose) {
			
			// expose configuration
			if (typeof opts.expose == 'string') { opts.expose = {color: opts.expose}; }
						
			$.extend(opts.expose, {
				api: true,
				closeOnClick: opts.closeOnClick,
				
				// only overlay control's the esc button
				closeOnEsc: false
			});
			
			// initialize expose api
			var e = overlay.expose(opts.expose);
			
			e.onBeforeClose(function() {
				self.close();		
			});
			
			self.onClose(function() {
				e.close();		
			});
		}		
		
		// close button
		closers = overlay.find(opts.close || ".close");		
		
		if (!closers.length && !opts.close) {
			closers = $('<div class="close"></div>');
			overlay.prepend(closers);	
		}		
		
		closers.click(function() { 
			self.close();  
		});					
	}
	
	// jQuery plugin initialization
	$.fn.overlay = function(conf) {   
		
		// already constructed --> return API
		var el = this.eq(typeof conf == 'number' ? conf : 0).data("overlay");
		if (el) { return el; }	  		 
		
		if ($.isFunction(conf)) {
			conf = {onBeforeLoad: conf};	
		}
		
		var opts = $.extend({}, $.tools.overlay.conf); 
		$.extend(true, opts, conf);
		
		this.each(function() {		
			el = new Overlay($(this), opts);
			instances.push(el);
			$(this).data("overlay", el);	
		});
		
		return opts.api ? el: this;		
	}; 
	
})(jQuery);


