/**
 * tools.expose 1.0.4 - Make HTML elements stand out
 * 
 * Copyright (c) 2009 Tero Piirainen
 * http://flowplayer.org/tools/expose.html
 *
 * Dual licensed under MIT and GPL 2+ licenses
 * http://www.opensource.org/licenses
 *
 * Launch  : June 2008
 * Date: ${date}
 * Revision: ${revision} 
 */
(function($) { 	

	// static constructs
	$.tools = $.tools || {};
	
	$.tools.expose = {
		version: '1.0.4',  
		conf: {	

			// mask settings
			maskId: null,
			loadSpeed: 'slow',
			closeSpeed: 'fast',
			closeOnClick: false, // Default is true
			closeOnEsc: false,
			
			// css settings
			zIndex: 9998,
			opacity: 0.8,
			color: '#456',
			api: false
		}
	};

	/* one of the greatest headaches in the tool. finally made it */
	function viewport() {
				
		// the horror case
		if ($.browser.msie) {
			
			// if there are no scrollbars then use window.height
			var d = $(document).height(), w = $(window).height();
			
			return [
				window.innerWidth || 								// ie7+
				document.documentElement.clientWidth || 	// ie6  
				document.body.clientWidth, 						// ie6 quirks mode
				d - w < 20 ? w : d
			];
		} 
		
		// other well behaving browsers
		return [$(window).width(), $(document).height()];
		
	} 
	
	function Expose(els, opts) { 
		
		// private variables
		var self = this, mask = null, loaded = false, origIndex = 0;		
		
		// 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); }
		});	
		

		// adjust mask size when window is resized (or firebug is toggled)
		$(window).resize(function() {
			self.fit();
		}); 
		
		
		// public methods
		$.extend(this, {
		
			getMask: function() {
				return mask;	
			},
			
			getExposed: function() {
				return els;	
			},
			
			getConf: function() {
				return opts;	
			},		
			
			isLoaded: function() {
				return loaded;	
			},
			
			load: function() { 
				
				// already loaded ?
				if (loaded) { return self;	}
	
				origIndex = els.eq(0).css("zIndex"); 				
				
				// find existing mask
				if (opts.maskId) { mask = $("#" + opts.maskId);	}
					
				if (!mask || !mask.length) {
					
					var size = viewport();
					
					mask = $('<div/>').css({				
						position:'absolute', 
						top:0, 
						left:0,
						width: size[0],
						height: size[1],
						display:'none',
						opacity: 0,					 		
						zIndex:opts.zIndex	
					});						
					
					// id
					if (opts.maskId) { mask.attr("id", opts.maskId); }					
					
					$("body").append(mask);	
					
					
					// background color 
					var bg = mask.css("backgroundColor");
					
					if (!bg || bg == 'transparent' || bg == 'rgba(0, 0, 0, 0)') {
						mask.css("backgroundColor", opts.color);	
					}   
					
					// esc button
					if (opts.closeOnEsc) {						
						$(document).bind("keydown.unexpose", function(evt) {							
							if (evt.keyCode == 27) {
								self.close();	
							}		
						});			
					}
					
					// mask click closes
					if (opts.closeOnClick) {
						mask.bind("click.unexpose", function()  {
							self.close();		
						});					
					}					
				}				
				
				// possibility to cancel click action
				var p = {proceed: true};
				$(self).trigger("onBeforeLoad", p);				
				if (!p.proceed) { return self; }
				
				
				// make sure element is positioned absolutely or relatively
				$.each(els, function() {
					var el = $(this);
					if (!/relative|absolute|fixed/i.test(el.css("position"))) {
						el.css("position", "relative");		
					}					
				});
			 
				// make elements sit on top of the mask				
				els.css({zIndex:Math.max(opts.zIndex + 1, origIndex == 'auto' ? 0 : origIndex)});				

				
				// reveal mask
				var h = mask.height();
				
				if (!this.isLoaded()) {					
					mask.css({opacity: 0, display: 'block'}).fadeTo(opts.loadSpeed, opts.opacity, function() {

						// sometimes IE6 misses the height property on fadeTo method
						if (mask.height() != h) { mask.css("height", h); }						
						$(self).trigger("onLoad");	
						 
					});					
				}
				
				loaded = true;	
				return self;
			}, 
			
			
			close: function() {
				
				if (!loaded) { return self; }   

				var p = {proceed: true};
				$(self).trigger("onBeforeClose", p);					
				if (p.proceed === false) { return self; }
				
				mask.fadeOut(opts.closeSpeed, function() {
					$(self).trigger("onClose");
					els.css({zIndex: $.browser.msie ? origIndex : null});
				});        										
				
				loaded = false;
				return self; 
			},
			
			
			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); 		
			},
			
			fit: function() {
				if (mask) {
					var size = viewport();				
					mask.css({ width: size[0], height: size[1]});
				}	
			}
			
		});

	}
	
	
	// jQuery plugin implementation
	$.fn.expose = function(conf) {
		
		var el = this.eq(typeof conf == 'number' ? conf : 0).data("expose");
		if (el) { return el; }
		
		if (typeof conf == 'string') {
			conf = {color: conf};
		}
		
		var opts = $.extend({}, $.tools.expose.conf);
		$.extend(opts, conf);		

		// construct exposes
		this.each(function() {
			el = new Expose($(this), opts);
			$(this).data("expose", el);	 
		});		
		
		return opts.api ? el: this;		
	};		


})(jQuery);
