(function($){ // secure the $ jQuery alias
/*******************************************************************************************/	
// jQuery.Blackbox.js - rev 37 
// Copyright (c) 2008, Mike Helgeson 
// Liscensed under the MIT License (MIT-LICENSE.txt)
// http://www.opensource.org/licenses/mit-license.php
// Created: 2007-03-22 | Updated: 2008-03-19
/*******************************************************************************************/
// REQUIRES: jquery 1.2.3+, utils.js, jquery.Register.js
/*******************************************************************************************/
var PLUGIN = "Blackbox", // Register the PLUGIN... and public methods...
$reg = $.Register( PLUGIN, "enable,disable,destroy,show,hide,size,position".split(',') );
/*******************************************************************************************/
// the jquery extension
$.fn[ PLUGIN ] = function( arg ){
	if ( isStr( arg ) ){ // call a single public method
		var args = Slice( arguments, 1 );
		return this.each(function(){ $reg.call( this, arg, args ); });
		}
	// inherit default options
	var opts = $.extend( {}, $[ PLUGIN ].defaults, arg||{} );
	// convert some option values from a percentage to a decimal
	$.each(['width','height','opacity'],function(x,v){ 
		if (x=(/^(\d+)%$/i).exec(opts[v])) opts[v] = parseInt(x[1])/100; 
		});
	// initialize the feature
	return this.each(function(){ new $[ PLUGIN ].make( this, opts ); });
	};
/*******************************************************************************************/
// default settings
$[ PLUGIN ].defaults = {
	html: '<h1>Please wait...</h1>', // (string|function|element|jquery) box content to be appended
	onOpen: null, // (null|function) optional fn, return false to prevent, scope: source element, arg: box element
	onClose: null, // (null|function) optional fn, return false to prevent, scope: source element, arg: box element
	opacity: null, // (string|number) overrides the opacity in bgCss
	background: null, // (string|number) overrides the background in bgCss
	wrapClass: '', // (string) optional "wrapper" classname
	wrapCss: {}, // (object) optional "wrapper" css
	bgClass: '', // (string) optional "background" classname
	bgCss: {}, // (object) optional "background" css
	boxClass: '', // (string) optional "display box" classname
	boxCss: {}, // (object) optional "display box" css
	height: 'auto', // (string|number) overrides the height in boxCss
	width: 'auto', // (string|number) overrides the width in boxCss
	top: 'center', // (string|number) overrides the top in boxCss
	left: 'center', // (string|number) overrides the left in boxCss
	isolate: false, // (boolean) true: only this one overlay visible at a time
	keepHtml: true, // (boolean) false: re-create the box content on every "show"
	emptyBox: true, // (boolean) remove all box content before (re)filling
	autoZindex: true, // (boolean) true: automatically manage the z-index
	showEvent: "click", // (string|false) which event on the element triggers "show", false to prevent
	bgClose: "click" // (string|false) which event on the bg div triggers "hide", false to prevent
	};
var base_z_index = 999; // @private var // but customize as needed
/*******************************************************************************************/
// constructor
$[ PLUGIN ].make = function( el, opts ){
	var self = $.extend( $reg.add( el, this ),{el:el,opts:opts}); // register the instance, store args
	self.size( opts.width, opts.height ); // store size
	self.position( opts.left, opts.top ); // store position
	if ( opts.showEvent ) // bind the element show event
		$( el ).bind( opts.showEvent+self.NS, function(){
			if (!self.disabled) return self.show();
			});
	$reg.publish( this, "create", [] ); // "PLUGIN.create"
	}; 
/*******************************************************************************************/
// methods
$[ PLUGIN ].make.prototype = {
	init: function(){ var o = this;
		// create the overlay markup 
		o.$bg = $(dv).addClass(o.opts.bgClass)
			.css( $.extend({position:'absolute',width:'100%',height:'100%',top:0,left:0}, o.opts.bgCss||{}) );
		o.$bx = $(dv).addClass(o.opts.boxClass)
			.css( $.extend({position:'absolute'},o.opts.boxCss||{}) );
		o.$wrap = $(dv).addClass(o.opts.wrapClass)
			.css( $.extend({position:'absolute',top:0,left:0},o.opts.wrapCss||{}) )
			.append(o.$bg).append(o.$bx).hide();
		try { o.$wrap.bgiframe() } catch(err){} // try "bgiframe" 
		// optional opacity and background overrides
		if (!empty(o.opts.opacity)) o.$bg.css('opacity',o.opts.opacity);
		if (!empty(o.opts.background)) o.$bg.css('background',o.opts.background);
		// bind the bg close event
		if ( o.opts.bgClose ) o.$bg.bind( o.opts.bgClose+o.NS , function(){ o.hide(); });
		o.ready = true;
		},
	show: function(){ var o = this;
		if (!o.ready) o.init();
		if (!bbx.isolate && o.allow(o.opts.onOpen) ){	
			if (!o.filled){ // fill box content
				var htm = o.opts.html;
				if (o.opts.emptyBox) o.$bx.empty();
				if (isFunc(htm)) htm = htm.call( o.el, o.$bx[0] );
				if (htm===false) return false;
				else o.$bx.append( htm ); 
				if (o.opts.keepHtml) o.filled = true;
				}
			if (o.opts.isolate) { // isolate this overlay
				$[ PLUGIN ]('hide'); // close everything
				if (bbx.Open.length==0) bbx.isolate = true; // everything is closed 
				else return false; // something else is still open
				}
			var z = bbx.open( o ); 
			if (o.opts.autoZindex){ // manage z-index
				o.$wrap.css('z-index',z);
				o.$bg.css('z-index',z+1);
				o.$bx.css('z-index',z+2);
				}
			else bbx.z = parseInt($wrap.css('zIndex'))||base_z_index; // store this Z
			$reg.publish( this, "show" ); // "PLUGIN.show"
			$(document.body).prepend( o.$wrap );
			o.$wrap.show()
			o.isOpen = true;
			o.size();
			}
		return false;
		},
	hide: function(){ var o = this;
		if (o.isOpen && o.allow(o.opts.onClose)){
			$reg.publish( this, "hide" ); // "PLUGIN.hide"
			bbx.close( o );
			if (o.opts.isolate) bbx.isolate = false;
			o.$wrap.hide()
			bbx.page();
			o.isOpen = false;
			}
		},
	position: function( x, y ){ //console.log('position',x,y)
		this.x = isDef(x) ? x : this.x;
		this.y = isDef(y) ? y : this.y;
		if (this.isOpen){
			var o = this, xx = o.x, yy = o.y, tt, ll, 
			hh = o.$bx.outerHeight({margin:true}), ww = o.$bx.outerWidth({margin:true});
			bbx.page( ww, hh );
			o.$wrap.css({ // cover the page
				height: Math.max( WIN.h, BOD.h ), 
				width: WIN.h<BOD.h ? BOD.w : WIN.w 
				});
			if (xx=='center'){ // horizontal center	
				ll = Math.round( (WIN.w-ww)/2 )+SCR.x; // viewport center
				ll = ww>WIN.w ? ll-(SCR.x-lastSCR.x) : ll; // adjust for scroll dir on overflow
				xx = Math.min(Math.max(ll,0),BOD.w-ww); // the centered position
				}		
			if (yy=='center'){ // vertical center	
				tt = Math.round( (WIN.h-hh)/2 )+SCR.y; // viewport center
				tt = hh>WIN.h ? tt-(SCR.y-lastSCR.y) : tt; // adjust for scroll dir on overflow
				yy = Math.min(Math.max(tt,0),BOD.h-hh); // the centered position
				}	
			o.$bx.css({ top: yy, left: xx }); // pos box
			}
		},
	size: function( w, h ){ //console.log("size",w,h);
		this.w = isDef(w) ? w : this.w;
		this.h = isDef(h) ? h : this.h;
		if (this.isOpen){
			var o = this, ww, hh;
			o.$wrap.css({
				height: Math.max( WIN.h, BOD.h ),
				width: WIN.h<BOD.h ? BOD.w : WIN.w 
				});
			// figure out box width
			ww = w || o.w || o.opts.boxCss.width;
			if (!ww || ww=='auto') ww = o.$bx.width('').width();
			o.$bx.width( ww<1 ? Math.round(WIN.w*ww) : ww );
			// figure out box height
			hh = h || o.h || o.opts.boxCss.height;
			if (!hh || hh=='auto') hh = o.$bx.height('').height();
			o.$bx.height( hh<1 ? Math.round(WIN.h*hh) : hh );
			o.position();
			}
		},
	allow: function(fn){
		if (this.disabled) return false;
		if (isFunc(fn)) return (fn.call( this.el, this.$bx[0] )!==false); 
		return true;
		},
	enable: function(){
		this.disabled = false;
		$reg.publish( this, "enable" ); // "PLUGIN.enable"
		},
	disable: function(){
		this.disabled = true;
		$reg.publish( this, "disable" ); // "PLUGIN.disable"
		},
	destroy: function(){
		$(window).unbind( this.NS ); // window events
		bbx.hide( this.el ); // unreg bbx
		$( this.el ).unbind( this.NS ); // remove "show" event
		o.$wrap.remove().empty(); // remove the markup
		$reg.publish( this, "destroy" ).drop( this ); // "PLUGIN.destroy" & unregister
		}
	};
/*******************************************************************************************/
// store and update window props seperately
var WIN ={}, lastWIN = {}, SCR = {}, lastSCR = {}, BOD = {w:0,h:0}, trueBOD = {}, $bod;
$(function(){ // doc.ready	
	$bod = $(document.body);
	$(window)
		.bind( "resize."+PLUGIN, function(){ //console.log('WINDOW.SIZE',WIN) 
			lastWIN = WIN; // to compare viewport size
			WIN = { w: $(this).width(), h: $(this).height() }; 
			bbx.page(); // recalculate the total page size
			bbx.each('size'); // "size" all open instances
			bbx.each('position'); // "position" all open instances
			}).trigger("resize."+PLUGIN)
		.bind( "scroll."+PLUGIN, function(event){ //console.log('SCROLL.SIZE',SCR)
			lastSCR = SCR; // to compare scroll direction
			SCR = { x: $(this).scrollLeft(), y: $(this).scrollTop() }; 
			bbx.each('position'); // "position" all open instances
			}).trigger("scroll."+PLUGIN);
	}); // doc.ready	
/*******************************************************************************************/
// private helpers - manage open-ness and z-index and such
var bbx = {
	z: base_z_index, Open: [],
	open: function( obj ){
		bbx.close( obj );
		bbx.Open.push( obj );
		return ( bbx.z+=3 );
		},
	close: function( obj ){
		bbx.Open = $.grep( bbx.Open, function(val){ return val.el!==obj.el; });
		if (bbx.Open.length==0) bbx.z = base_z_index;
		}, 
	each: function(meth){
		$.each( bbx.Open, function(){ this[ meth ](); });
		},
	page: function( ww, hh ){ 
		if ( isNum(ww) && isNum(hh) ){
			BOD = { w: Math.max( trueBOD.w, ww ), h: Math.max( trueBOD.h, hh ) };
			if (BOD.w!=trueBOD.w) $bod.width( BOD.w );
			if (BOD.h!=trueBOD.h) $bod.height( BOD.h );
			}
		else trueBOD = { w: $bod.width("").width(), h: $bod.height("").height() }; // to compare page sizes
		},
	isolate: false
	}, 
dv = '<div></div>';
/*******************************************************************************************/
})(jQuery) // secure the $ jQuery alias
