/* jBox 4.3
 * Copyright 2009 Craig Pierce | http://jbox.unnecessarilylongurl.com
 * Governed by the BSD, MIT and GPL licenses */
 
 /* Browsers Tested: Windows:   IE6, IE7, IE8, FF2, FF3, FF3.5, Safari 3, Safari 4, Chrome 2
                     Mac:       Safari 3, FF3 */

/*	Notes:	1)	Pages will typically need a DOCTYPE in order for IE to work properly with the 'fixed position' CSS attribute */
 
 /* TODOs:  1)	iFrames not working perfectly in IE
			2)	Somehow, switching a jBox remembers the previous animation, and hides the
					new jBox using that animation instead of the new one - but only in some cases
			3)	Just realized why we should let users overwrite the position CSS attribute:
					very tall jBoxes will go off screen, but you can't scroll to contents */
 
(function($){
    /* plugin definition */
	/* main function attached to jQuery prototype */
	$.fn.jBox = function(action, options){
	    if (!isNothing(this[0])){
	        /* chickity check the params */

	        action = action || "show";
		    if (isType(action, "string")){ action = action.toLowerCase(); }else{ options = action; action = "show"; };
		    if (!isType(options, "object")){ options = null; };
		    
		    /* find the id (forget the ego) */
	        var id = $(this[0]).attr("id") || contentElementId;
	        $(this[0]).attr("id", id);
	        
		    /* don't be a hate'a, call the real play'a */
		    $.jBox(action, $.extend(options, { inlineId : id }));
		}else{
		    /* what were you thinkin kid? */
		    return false;
		}
	};
	
	/* main function attached to jQuery object */
	$.jBox = function(action, options){
	    /* check the params */

	    action = action || "show";
		if (isType(action, "string")){ action = action.toLowerCase(); }else{ options = action; action = "show"; };
		if (!isType(options, "object")){ options = null; };
		
		/* do action */
		if (action == "show"){
		    var cet = $("#" + contentElementId);
			if ($.jBox.isOpen() && !isNothing($(cet)[0])){
			    /* there is already a jBox open - close it (but not the overlay) before continuing */
			    removeContentElement(function(){
			        if ($(cet).hasClass(statClassName)){
						/* we're switching from a static jBox, so just move it back down where it was */
						$(cet).css({zIndex: existingZindex}).removeClass(statClassName);
			            /* now give it a few milliseconds; otherwise, the eyes get confused by all the goings-on */
			            setTimeout(function(){ continueShowAction(false, options); }, 150);
			        }else{
						/* we're switching from an animated jBox, which already animated out, so just animate the next one in */
			            continueShowAction(false, options);
			        };
			    });
			}else{
			    /* show new jbox */
			    continueShowAction(true, options);
			};
		}else if (action == "hide"){
			$.jBox.hide(options);
		}else{
		    /* what the heck did you set 'action' to: 42? */
			return false;
		};
	};
	
	/* secondary functions attached to jQuery object */
	$.jBox.hide = function(options){
		/* quick, Kevin Smith: get behind this rock! */
		if ($.jBox.isOpen()){
		    if (!isType(options, "object")){ options = null; };
		    setOptions(options, false);
		    
		    /* the begining of the end */
			fireFunc(opts.earlyCallback);
			
			/* remove teh loader... */
			removeLoader();
			
			/* ...and teh content element... */
			removeContentElement();
			
			/* ...and teh overlay (all happens at teh same time) */
			$("#" + overlayId).fadeOut(halfSpeed, function(){
			    /* remove overlay */
				$("#" + overlayId).remove();
				
				/* be ninja - leave no trace */
				if (isPreIE7){
				    $("html, body").css({overflow: ""});
				    $("select").each(function(){
					    if ($(this).css("visibility") == "hidden" && !arrayContains(elementsMeantToBeHidden, this)){
						    $(this).css({visibility: "visible"});
					    };
				    });
                    elementsMeantToBeHidden.length = 0;
				    window.onresize = existingWindowResize;
				    existingWindowResize = null;
				};
				
				if ($("#" + contentElementId).hasClass(statClassName)){
				    /* technically we should 'remove' the static jBox in removeContentElement - however,
				        the content and the overlay are removed at the same time, and if we didn't wait till the
				        overlay was gone then the static jBox would 'jump' down under it while it was fading out
				        
				        this also needs to be done when switching from a static jBox to another jBox in the 'show' action above */
				    $("#" + contentElementId).css({zIndex: existingZindex}).removeClass(statClassName);
				    existingZindex = 1;
				};
				
				contentElementId = "jbox-content-element";
                halfSpeed = 400;
                needContentElement = false;
				ajaxLoading = false;
				
				/* holla back y'all */
				fireFunc(opts.callback);
			});
        };
	};
    
    $.jBox.isOpen = function(){ return !isNothing($("#" + overlayId)[0]); };
    
    /* jBox's document.ready function */
    $(document).ready(function(){
		fixFlash($("embed, object"));
    });
    
    /* plugin properties */
    /* public overridable */
    var defaults = {
        opacity:            8,
        speed:              800,
		ajaxTimeout:		8000,
        zIndex:             100000,
        inlineId:           "",
        url:                "",
        htmlNode:           "div",
        animation:          "fade",
        overlayColor:       "#000",
        iframeScroll:       false,
        jBoxCSS:            {},
        earlyCallback:      null,
        callback:           null
    };
    
    /* private */
    var opts,
    existingWindowResize,
    loaderTimer,
    halfSpeed =                 400,
    existingZindex =            1,
    overlayId =                 "jbox-background-overlay",
    contentElementId =          "jbox-content-element",
    loaderId =                  "jbox-loading-div",
    statClassName =             "stationary-jbox",
    elementsMeantToBeHidden =   [],
	ajaxLoading =				false,
	needContentElement =        false,
    isPreIE7 =                  ($.browser.msie && parseInt($.browser.version) < 7);

    /* private functions */
    /* setOptions: extends the incoming options with either the defaults (if it's a new jBox) or the previously chosen options (if a jBox is already open)
        then attempts to error check those options the best way it knows how (though that may be a bit more than the law will allow) */
    function setOptions(options, isNew){
		var exo;
        if (isNew){
            exo = defaults;
        }else{
            /* if callbacks were set previously, we don't want to re-fire them - this makes doubly sure they are nulled out before continuing on */
            opts.earlyCallback = opts.callback = null;
            exo = opts;
        };
		
		opts = null;
		opts = $.extend({}, exo, options);
		
        if (!isNothing(options)){
            opts.opacity = parseInt(opts.opacity) || 8;
            if (parseInt(opts.speed) === 0){ opts.speed = 1; };
            opts.speed = parseInt(opts.speed) || 800;
            halfSpeed = Math.round(opts.speed/2);
            opts.zIndex = parseInt(opts.zIndex) || 100000;
            opts.ajaxTimeout = parseInt(opts.ajaxTimeout) || 8000;
            
            opts.htmlNode = (isType(opts.htmlNode, "string")) ? opts.htmlNode.toLowerCase() : "div";
            opts.animation = (isType(opts.animation, "string")) ? opts.animation.toLowerCase() : "fade";
            if (opts.animation != "fade" && opts.animation != "expand" && opts.animation != "rolldown" && opts.animation != "pulldown" && opts.animation != "pullup" && opts.animation != "dropin" && opts.animation != "dropinbounce"){ opts.animation = "fade"; };
            if (opts.inlineId == "" || isNothing($("#" + opts.inlineId)[0])){ contentElementId = "jbox-content-element"; needContentElement = true; }else{ contentElementId = opts.inlineId; };
			
			opts.iframeScroll = ((isType(opts.iframeScroll, "boolean") && opts.iframeScroll) || (isType(opts.iframeScroll, "string") && opts.iframeScroll.toLowerCase() == "true")) ? true : false;
			
            if (!isType(opts.jBoxCSS, "object")){ opts.jBoxCSS = {}; };
            if (!isType(opts.earlyCallback, "function")){ opts.earlyCallback = null; };
            if (!isType(opts.callback, "function")){ opts.callback = null; };
        };
    };
    
    /* continueShowAction: the 'show' action is a bit conditional when it comes to what to show and when to show it, but it all ends up here eventually */
    function continueShowAction(isNewJbox, options){
		setOptions(options, true);
        var cet = $("#" + contentElementId);
	    
	    /* create content element, if inline element not provided */
        if (needContentElement){
	        if (opts.url == ""){ return false; /* no inline content given, no ajax content given - exactly what were you expecting to happen!? */ };
			
	        var ifs = (opts.iframeScroll) ? "auto" : "no", iframeTxt = (opts.htmlNode != "iframe") ? "" : " src='" + opts.url + "' frameborder='0' scrolling='" + ifs + "' marginwidth='0' marginheight='0'";
	        $("body").append("<" + opts.htmlNode + " id='" + contentElementId + "'" + iframeTxt + " style='display: none;'></" + opts.htmlNode + ">");
	        
	        cet = $("#" + contentElementId);
        }else if ($(cet).is(":visible")){
            /* or if there is a content element, but it's static... */
            var cePos = $(cet).css("position");
		    existingZindex = parseInt($(cet).css("zIndex")) || 1;
	        $(cet).css({zIndex: (opts.zIndex + 2), position: (cePos != "" && cePos != "static") ? cePos : "relative"}).addClass(statClassName);
		};
		
        /* go ahead and start loading the ajax content - if any */
        if (opts.url != "" && opts.htmlNode != "iframe"){
	        ajaxLoading = true;
			
		    loaderTimer = setTimeout(function(){
		        $("body").append("<div id='" + loaderId + "'><div style='position: absolute; right: 1px; top: 0px; color: #fff; z-index: 2;'>Loading...</div><div style='position: absolute; right: 0px; top: 1px; color: #000; z-index: 1;'>Loading...</div></div>");
                $("#" + loaderId).css({
                    position:           (isPreIE7) ? "absolute" : "fixed",
                    zIndex:             (opts.zIndex + 1),
                    border:             "0px",
                    top:                "80%",
                    left:               "0px",
                    width:              "80%",
                    display:            "none"
                }).fadeIn(opts.speed);
            }, (opts.speed + 800));
			
	        $.ajax({
                url:            opts.url,
                timeout:        opts.ajaxTimeout,
                success:        function(data){
                                    $(cet).html(data);
									fixFlash($("embed, object", cet));
                                    showContentElement();
                                },
                error:          function(){
                                    changeLoaderText("An error was encountered - Closing popup screen...");
                                    setTimeout($.jBox.hide, 2000);
                                }
            });
        };
        
        /* hide 'select' elements */
        if (isPreIE7){
            $("select").each(function(){
                var st = $(this);
                
                if ($(cet).html().indexOf($(st).parent().html()) == -1){
                    /* it's not in the content to be displayed, meaning it needs to be under the overlay */
                    if ($(st).is(":hidden") || $(st).css("visibility") == "hidden"){
                        /* element is already hidden - don't display it when jBox closes */
                        elementsMeantToBeHidden.push(this);
                    }else{
                        /* hide it so it won't show over the overlay */
                        $(st).css({visibility: "hidden"});
                    };
                };
            });
        };
		
		if (isNewJbox){
            /* create the overlay */
            /* fix ie6 (i.e. do Macrosux job for them) */
            if (isPreIE7){
                /* resize overlay correctly - setup an event that will help correctly size the overlay on window resize */
                existingWindowResize = window.onresize;
                window.onresize = function(){
                    setTimeout(function(){
                        $("#" + overlayId).css({height: $(window).height() + "px", width: $(window).width() + "px"});
		                fireFunc(existingWindowResize);
                    }, 1);
                };
                
                /* fix scrolling */
                $("html, body").css({overflow: "hidden"});
            };

//opts.zIndex = 10;
//opts.overlayColor = "#000000";
//alert(opts.zIndex);
//alert(isPreIE7);

            /* build overlay */
            $("#blockMainInner").append("<div id='" + overlayId + "'></div>");
            $("#" + overlayId).css({
                position:           (isPreIE7) ? "absolute" : "fixed",
                zIndex:             (opts.zIndex),
                backgroundColor:    opts.overlayColor,
                border:             "0px",
                top:                (isPreIE7) ? $(document).scrollTop() + "px" : "0px",
                left:               "0px",
                opacity:            "0." + opts.opacity,
                height:             (isPreIE7) ? $(window).height() + "px" : "100%",
                width:              (isPreIE7) ? $(window).width() + "px" : "100%",
				cursor:				"pointer",
                display:            "none"
            }).click($.jBox.hide).fadeIn(opts.speed, showContentElement);
        }else{
            /* change any css values on the overlay that need to be changed to new values */
            if (!isNothing($.fx.step["backgroundColor"])){
                /* animating the background color requires jQuery color plugin... */
                $("#" + overlayId).css({zIndex: opts.zIndex}).animate({backgroundColor: opts.overlayColor, opacity: "0." + opts.opacity}, opts.speed);
            }else{
                /* ...otherwise, just switch right to the new color */
                $("#" + overlayId).css({zIndex: opts.zIndex, backgroundColor: opts.overlayColor}).animate({opacity: "0." + opts.opacity}, opts.speed);
            };
            
            showContentElement();
        };
    };
	
	/* showContentElement: animates the content into view
	    gets called twice when loading ajax content - if the load is done first, the content won't show till the overlay is done showing
	    if the overlay is done first, content won't show till it's done loading */
	function showContentElement(){
		if (!ajaxLoading){
		    /* cancel/remove the loader */
		    removeLoader();
		    
			/* get ready... */
			var cet = $("#" + contentElementId), easeOB = (!isNothing($.easing.easeOutBounce) && opts.animation == "dropinbounce") ? "easeOutBounce" : "";
			fireFunc(opts.earlyCallback);
			
			/* ...get set... */
			if ($(cet).hasClass(statClassName)){
			    /* no animation to perform - the static jBox was brought forward before the overlay was shown */
				fireFunc(opts.callback);
			}else{
			    /* setup the jBox with some css witchdiggery */
				setJboxCSS(); var h = $(cet).height();
				
				/* ...go! */
			    /* It's time for Animaniacs | And we're zany to the max | So just sit back and relax | You'll laugh 'til you collapse | We're Animaniacs! */
				switch(opts.animation){
					case "rolldown":
						$(cet).css({
							height:         "0px",
							marginTop:      Math.round(h/2) + "px"
						}).animate({
							height:         h + "px",
							marginTop:      "0px"
						}, halfSpeed, opts.callback);
						break;
					case "pulldown":
						$(cet).css({
							height:         "0px"
						}).animate({
							height:         h + "px"
						}, halfSpeed, opts.callback);
						break;
					case "pullup":
						$(cet).css({
							height:         "0px",
							marginTop:      h + "px"
						}).animate({
							height:         h + "px",
							marginTop:      "0px"
						}, halfSpeed, opts.callback);
						break;
					case "dropinbounce": /* requires jQuery easing plugin, otherwise works like 'dropin' */
					case "dropin":
						$(cet).css({
							marginTop:      -(h*2) + "px",
							opacity:        "0"
						}).animate({
							marginTop:      "0px",
							opacity:        "1"
						}, (easeOB != "") ? opts.speed : halfSpeed, easeOB, opts.callback);
						break;
					case "expand":
						$(cet).css({display: "none"}).show(halfSpeed, opts.callback);
						break;
					default: /* fade */
						$(cet).css({display: "none"}).fadeIn(halfSpeed, opts.callback);
				};
			};
		};
		ajaxLoading = false;
    };
	
	/* removeContentElement: animates the content out of view */
	function removeContentElement(callback){
		var cet = $("#" + contentElementId);
		
		if ($(cet).hasClass(statClassName)){
		    /* it was a static jBox - go straight to callback - do not pass Go, do not collect $200 */
		    fireFunc(callback);
		}else{
		    /* make with the animating */
		    var h = $(cet).height(), tCallback = function(){
		        if (contentElementId == "jbox-content-element"){ $(cet).remove(); };
		        fireFunc(callback);
	        };
			
			switch(opts.animation){
				case "rolldown":
					$(cet).animate({
						height:     "0px",
						marginTop:  Math.round(h/2) + "px",
						opacity:    "0"
					}, halfSpeed, function(){
						$(cet).css({display: "none", opacity: "1", height: h, marginTop: "0px"});
						tCallback();
					});
					break;
				case "pulldown":
					$(cet).animate({
						height:     "0px",
						opacity:    "0"
					}, halfSpeed, function(){
						$(cet).css({display: "none", opacity: "1", height: h});
						tCallback();
					});
					break;
				case "pullup":
					$(cet).animate({
						height:     "0px",
						marginTop:  h + "px",
						opacity:    "0"
					}, halfSpeed, function(){
						$(cet).css({display: "none", opacity: "1", height: h, marginTop: "0px"});
						tCallback();
					});
					break;
				case "dropinbounce":
				case "dropin":
					$(cet).animate({
						marginTop:     Math.round(h*2) + "px",
						opacity:    "0"
					}, halfSpeed, function(){
						$(cet).css({display: "none", opacity: "1", marginTop: "0px"});
						tCallback();
					});
					break;
				case "expand":
					$(cet).hide(halfSpeed, tCallback);
					break;
				default: /* fade */
					$(cet).fadeOut(halfSpeed, tCallback);
			};
		};
	};
	
	/* setJboxCSS: sets all the various pieces of the jBox's CSS
		won't change certain CSS values if they are already set, such as the height and width of the jBox */
	function setJboxCSS(){
		var h = opts.jBoxCSS["height"], w = opts.jBoxCSS["width"], finalCSS = {}, cet = $("#" + contentElementId);
		
		$(cet).css({display: "block", position: (isPreIE7) ? "absolute" : "fixed", zIndex: (opts.zIndex + 2), left: "0px", top: "0px", marginTop: "0px", marginLeft: "0px"});
		
		if (isType(h, "undefined")){
			if ($(cet)[0].toString().toLowerCase() == "[object htmliframeelement]"){
				try{
                    h = $(cet)[0].contentWindow.document.body.scrollHeight;
                }catch(ex){
					h = $(cet).height();
				};
			}else{
				h = $(cet).height();
			};
		};
		if (isType(w, "undefined")){
			if ($(cet)[0].toString().toLowerCase() == "[object htmliframeelement]"){
				try{
                    w = $(cet)[0].contentWindow.document.body.scrollWidth;
                }catch(ex){
					w = $(cet).width();
				};
			}else{
				w = $(cet).width();
			};
		};
		if (isType(opts.jBoxCSS["top"], "undefined")){
			/* what percent is the content height of the window height? */
			var percent = Math.round(100 * (h / $(window).height()));
			/* ...and what's left over from that? */
			var remain = 100 - percent;
			/* ...and what's a quarter of the leftovers? */
			var remainQuarter = Math.round(remain / 4);
			/* done */
			var finalTop = (remainQuarter > 0) ? remainQuarter : 2;
			
			finalCSS["top"] = finalTop  + "%";
		};
		if (isType(opts.jBoxCSS["left"], "undefined")){
			finalCSS["left"] = "50%";
			finalCSS["marginLeft"] = -Math.round(w/2) + "px";
		};
		
		finalCSS["height"] = h + "px";
		finalCSS["width"] = w + "px";
		
		$(cet).css($.extend({}, opts.jBoxCSS, finalCSS));
		
		if (isPreIE7){ $(cet).css({top: ($(cet).offset().top + $(document).scrollTop()) + "px"}); };
	};
	
	/* fixFlash:  fix flash elements so they won't appear on top of the overlay, and so they'll animate properly (if they're a part of some content to be shown)
            interesting note for posterity:  flash elements won't fade in/out unless their wmode is set.  you can't even fade their parents in/out
            correctly - if you try to fade their parent (or a parent's parent, or so on) then they'll just 'jump' out at you or instantly disappear
            while their parent fades around them */
	function fixFlash(objs){
	
	
	
	
	
	
		return false;
		
		
		
		
		
		
		
		
		
        $(objs).each(function(){
            var obt = $(this), obHTML = $(obt).html().toLowerCase(), nn = this.nodeName.toLowerCase(), newHTML = obHTML;
            
            if (!isNothing($(obt).parent())){
				if (!isNothing($(obt).parent().parent())){
					/* for any static jBoxes containing flash elements, when the position of the jBox gets set it makes the flash 'blink'
					    I'd rather do this at load time than when the jBox is shown */
					var obParPos = $(obt).parent().parent().css("position");
					$(obt).parent().parent().css({position: (obParPos != "" && obParPos != "static") ? obParPos : "relative"});
				};
				
                var obParHTML = $(obt).parent().html().toLowerCase();
                
                /* an object element, possibly with an embed inside */
                if (nn == "object"){
                    /* wmode param on the object tag */
                    if (newHTML.indexOf("name=\"wmode") > -1){
                        if (newHTML.indexOf("value=\"window") > -1){ newHTML = newHTML.replace("value=\"window", "value=\"opaque"); };
                    }else{
                        newHTML = newHTML + "<param name=\"wmode\" value=\"opaque\" />";
                    };
                    
                    /* wmode attribute on the embed tag */
                    if (newHTML.indexOf("<embed") > -1){
                        if (newHTML.indexOf("wmode=") > -1){
                            if (newHTML.indexOf("wmode=\"window") > -1){ newHTML = newHTML.replace("wmode=\"window", "wmode=\"opaque"); };
                        }else{
                            newHTML = newHTML.replace("<embed", "<embed wmode=\"opaque\"");
                        };
                    };
                    
                    if (newHTML != obHTML){ $(obt).parent().html(obParHTML.replace(obHTML, newHTML)); };
                };
                
                /* an embed tag without an object parent */
                if (nn == "embed" && obParHTML.indexOf("<object") == -1 && obParHTML.indexOf("<param") == -1){
                    newHTML = obParHTML;
                    
                    /* wmode attribute on the lone embed tag */
                    if (newHTML.indexOf("wmode=") > -1){
                        if (newHTML.indexOf("wmode=\"window") > -1){ newHTML = newHTML.replace("wmode=\"window", "wmode=\"opaque"); };
                    }else{
                        newHTML = newHTML.replace("<embed", "<embed wmode=\"opaque\"");
                    };
                
                    if (newHTML != obParHTML){ $(obt).parent().html(newHTML); };
                };
            };
        });
	};
	
	/* removeLoader: cancels the timeout that will show the loader, and/or fades the loader out */
	function removeLoader(){
		clearTimeout(loaderTimer);
		loaderTimer = null;
	    $("#" + loaderId).fadeOut(halfSpeed, function(){
	        $("#" + loaderId).remove();
	    });
	};
	
	/* changeLoaderText: changes the text inside the loader (the text and its 'shadow') */
	function changeLoaderText(newTxt){ $("#" + loaderId).children("div").html(newTxt); };
    
    /* private helper functions */
    function arrayContains(a, i){ for(var x=0;x<a.length;x++){ if (a[x] === i){ return true; }; }; return false; };
	function fireFunc(f){ if (isType(f, "function")){ f(); }; };
	function isNothing(x){ return (x == null || x == undefined); };
	function isType(x, y){ return typeof(x) == y; };
})(jQuery);

