/*************************

LIGHTBOX
author: Andy Croxall (mitya@mitya.co.uk)


OVERVIEW:
CSS-indepedent, jQuery-powered lightbox script and supporting functions to centre the element being showcased and also a function which replaces JS's default alert()/confirm() functions with one which uses
the lightbox script.


DESCRIPTION & USAGE:

    LIGHTBOX

    Builds and handles lightbox.
    Run by calling lightbox() on your jQuery selector, e.g. $('#myEleemnt').lightbox();
    Lightbox is built and prepared quietly when page loads. It is styled here, no dependency on CSS.


	CLOSING THE LIGHTBOX
	The lightbox can be closed in a number of ways:
		- clicking outside the central element
		- clicking any element in the central element that is a button (unless it has .noLBClose) or has .close
		- pressing escape
		- calling the lightbox on itself (i.e. $('#lightbox').lightbox())
		- calling lightboxhide()


    CENTREELEMENT FUNC

    Used by the lightbox to centre the element being showcased, but can be used independently of lightbox. Defined on the jQuery prototype, so just run it on
    the element(s) you want to centre, e.g. $('.myClass').centreElement().
    Expects 3 args, all optional:
        - horiz: centre on X axis (default: true)
        - vert: centre on Y axis (default: true)
        - justReturnValues: if true, returns the potential left/top coords of the element(s) were they centred, but doesn't actually centre them. Returns as an
        array [left, top], without 'px'. (Default: false)

    Note: for purposes of assured positioning, func lifts the element(s) out of the DOM and replaces them as first children of the body.
    Centres on both axies by default, though you can stipulate just one by passing horizontally and or vertically depending on args (both true by default)


    LBDIALOG FUNC

    Lightbox-using replacement for JS's in-built alert()/confirm() functions. Expects 3 args, only the first of which is required:
        - content: the html to display in the dialog box
        - OKButton: a right button will be put out whether you pass this or not, but this allows you to tailor it. An object containing sub options including
                - text: the text to display on the button (default: 'OK')
                - callback: a function to be called onclick
                - noLBClose: if passed, lightbox won't close when button is clicked (useful if callback send user to another page, therefore no point killing
                lightbox)
        - cancelButton: as with rightButton, but this acts as your negative button in the event you want the dialog to act as a confirm(), not alert()
        - css: an optional of CSS pairings to further style your alert
        - error: if true, a red heading saying 'error' will appear above your message, saving you the trouble of coding it in @content
        - success: as with error, but a green 'success' header instead
        - autoCloseAfter (int): the number of seconds after which the dialog should automatically disappear (in which case no close button is shown)
        - noLightbox: if true, dialog appears as normal but no lightbox behind it, i.e. screen doesn't fade

    Note:	if wanting confirm() not alert(), be sure to pass cancelButton, otherwise only the OK button will be put out, and the dialog will be an alert.
    Note:	onclick, buttons will effect any callbacks passed but also close the lightbox, unless 'noLBClose' is passed in their params object
    Note:	evaluates integrity of passed params at start and won't run if invalid args passed
    Note:	as with in-built browser dialogs, it can be closed by pressing escape (and by other methods - see below) 
    Note:	Like in-built browser dialogs, dialogs generated by lbdialog() cannot by default be closed by clicking the lightbox itself. To allow this, see
    		the config vars. This is only for dialogs (lbdiaog({}), not $(selector).lightbox() calls.


*************************/

/* -------------------
 | CONFIG
 ------------------- */

// IE no likey bacgkground images over https

var lb_bg = ("https:" == document.location.protocol && $.browser.msie)
			? 
			'no-repeat right bottom #fff'
			:
			'url(/assets/images/spike.jpg) no-repeat right bottom #fff';

lightboxConfig = {
	lbOpacity:	.6,
	lbBackground: '444',
	lbdialog_defaultHeight: 100, //default height of lightbox replcement dialog box (i.e. alert/confirm)
	lbdialog_allowCloseByClickingLightbox: false, //see notes at top
	lbdialog_css: {
		border: 'solid 3px #000', fontSize: 16, width: 500, fontWeight: 'bold', background: lb_bg
	}
}


/* -------------------
 | PREP. Log whether IE6 and declare func used by lbdialog to allow closure via escsape key (need to declare as non-anonymouse
 | func so it can be unbound when lbdialog closes
 ------------------- */

var ie6 = navigator.appVersion.match(/MSIE 6\.0/);
var closeLBDialogOnEscapePress = function(e) { if (e.keyCode == 27) hidelightbox(); }


$(function() {
	
	/* -------------------
	| MAIN LIGHTBOX FUNC
	------------------- */
	
	$.prototype.lightbox = function(noFade, autoDisappear, noLightbox, noClose) {
	
		
	    //prep
		
	    var ac = arguments.callee;
	    var centralElement = this.get(0);
	    if (noLightbox) $('#lightbox').css({opacity: 0, filter: 'alpha(opacity=0)'})
		if (noClose) $('#lightbox').addClass('noClose');
	
	
	    //assuming noClose not passed, close lighbox if any <button> or element with .close inside central element is clicked
	    
	    if (!noClose) $(centralElement).find('button, .close').not('.noLBClose, [rel=noLBClose]').click(function() { $('#lightbox').lightbox(); });
	
	
	    //utility functions
	    
	    ac.doHide = function(noFade, callback) { //called on hide request
	        if (!noFade) {
	            var numChildren;
		        if ((numChildren = $(ac.centralElement).children().length) > 0) {
	                var childrenFaded = 0;
		    	    $(ac.centralElement).children().fadeOut('', function() {
	                    childrenFaded++;
	                    if (childrenFaded == numChildren) $(ac.centralElement).slideUp('fast', function() { reinsertCentralElement(); $('#lightbox').fadeOut('fast'); });
	                });
	            } else
		    	$(ac.centralElement).slideUp('fast', function() { $('#lightbox').fadeOut('fast', callback ? callback() : null); reinsertCentralElement(); });
	        } else {
	            $(ac.centralElement).hide();
	            $('#lightbox').fadeOut();
	            if (callback) callback();
	            reinsertCentralElement();
	        }
	
		}
	
	    
	    //request to show
	    
	    if (centralElement.id != 'lightbox') {
	
	    	
	        //if central element's parent is not body, make it so (i.e. lift it out of the DOM and re-insert it). This ensures centering relative to body. After, put back where it was.
	    	
	        if (centralElement.parentNode.tagName.toLowerCase() != "body") {
	            var node_holder = centralElement;
	            var markerNodeForReinsertion = document.createElement('div');
	            markerNodeForReinsertion.id = 'markerNodeForReinsertion';
	            node_holder.parentNode.insertBefore(markerNodeForReinsertion, node_holder);
	            centralElement.parentNode.removeChild(centralElement);
	            centralElement = document.body.insertBefore(node_holder, document.body.childNodes[0]);
	        }
	
	        
	        //force position absolute and z-index 10001 if not set
	        
	        if ($(centralElement).css('position') != 'absolute') $(centralElement).css('position', 'absolute');
	        if ($(centralElement).css('zIndex') != 10001) $(centralElement).css('zIndex', 10001);
	
	        
	        //remember the central element so we can kill it on hide request
	        ac.centralElement = centralElement
	
	        
	        //centre it and ensure that, if user scrolls while LB open, central element moves with it
	        $(centralElement).centreElement();
			$(window).scroll(function() { $(centralElement).centreElement(); });
	
	    }
	
	
		//toggle show/hide lightbox
	    
	    if ($('#lightbox').is(":hidden")) { //hidden - so show pos as necessary)
	
			var callback = function() { $(centralElement).slideToggle('fast', function() { $(centralElement).children().fadeIn(); }); };
			if (!noFade) $('#lightbox').fadeIn('fast', callback); else { $('#lightbox').show(); callback(); }
			if (autoDisappear) setTimeout(hidelightbox, autoDisappear * 1000)
	
	    } else //showing - so hide
	        ac.doHide();
	
	}



	/* -------------------
	| UTILITY: centre central element
	------------------- */
	
	$.prototype.centreElement = function(horiz, vert, justReturnValues) {
	
	    //prep
	    if (horiz == undefined) horiz = true;
	    if (vert == undefined) vert = true;
	    var scrollX = (document.documentElement.scrollLeft || document.body.scrollLeft || 0) || window.pageXOffset;
	    var scrollY = (document.documentElement.scrollTop || document.body.scrollTop || 0) || window.pageYOffset;
	    if (scrollX == undefined) scrollX = 0;
	    if (scrollY == undefined) scrollY = 0;
	    var el = this.get(0);
	
	    //calculate
	    var temp_elWidth = parseInt(el.currentStyle ? el.currentStyle.width : getComputedStyle(el, null).width);
	    if (isNaN(temp_elWidth)) { $(el).css('width', '300px'); temp_elWidth = 300; } //force default width if none set
	    var left = (self.innerWidth || (document.documentElement.clientWidth || document.body.clientWidth)) / 2 - (temp_elWidth / 2) + scrollX;
	
	    var temp_elHeight = parseInt(el.currentStyle ? el.currentStyle.height : getComputedStyle(el, null).height);
	    if (isNaN(temp_elHeight)) { $(el).css('height', '300px'); temp_elHeight = 300; } // " " "
	    var top = (self.innerHeight || (document.documentElement.clientHeight || document.body.clientHeight)) / 2 - (temp_elHeight / 2) + scrollY;
	
	    //account for padding
	    top -= parseInt(el.currentStyle ? el.currentStyle.paddingTop : getComputedStyle(el, null).paddingTop);
	    left -= parseInt(el.currentStyle ? el.currentStyle.paddingLeft : getComputedStyle(el, null).paddingLeft);
	
	    //return/effect
	    if (!justReturnValues) {
	        if (horiz) el.style.left = left+"px";
	        if (vert) el.style.top = top+"px";
	    } else {
	        if ((!horiz || horiz) && (!vert || vert))
	            return [left, top];
	        else if (!horiz || horiz)
	            return left;
	        else if (!vert || vert)
	            return vert;
	    }
	
	}


	/* -------------------
	| BUILD LIGHTBOX ONLOAD
	------------------- */

    //build
    var lightboxDiv = document.createElement('div');
    lightboxDiv.id = 'lightbox';
    $(lightboxDiv).click(function() {
    	if (
			!$(this).hasClass('noClose')
			&&
			(
				$.prototype.lightbox.centralElement.id != 'lightboxdialog'
				||
				lightboxConfig.lbdialog_allowCloseByClickingLightbox
			)
    	)
    		$(this).lightbox();
    });
    document.body.insertBefore(lightboxDiv, document.body.childNodes[0]);

    //style it (doing it here means the script is portable, don't need to tell users to add rules to their CSS sheets)
    $('#lightbox').css({
    	opacity: lightboxConfig.lbOpacity,
    	filter: 'alpha(opacity='+(lightboxConfig.lbOpacity * 100)+')',
    	width: $(document.body).width(),
    	height: $(document.body).height(),
    	background: '#'+lightboxConfig.lbBackground,
    	position: 'fixed',
    	left: 0,
    	top: 0,
    	zIndex: 10000,
    	display: 'none'
    });

});



/* -------------------
| UTILITY FUNC: supporting func - on lightbox close, reinsert central element into DOM at its original position
------------------- */

reinsertCentralElement = function() {
    var ce = $.prototype.lightbox.centralElement;
    try {
        markerNodeForReinsertion = document.getElementById('markerNodeForReinsertion');
        if (markerNodeForReinsertion.parentNode.tagName.toLowerCase() != "body") {
            var ceHolder = ce;
            ce.parentNode.removeChild(ce);
            markerNodeForReinsertion.parentNode.insertBefore(ceHolder, markerNodeForReinsertion);
            markerNodeForReinsertion.parentNode.removeChild(markerNodeForReinsertion);
        }
    } catch(e) {}
}



/* -------------------
| UTILITY FUNC: lightbox-utilising replacement for in-built alert/confirm methods. See usage notes at top of page.
------------------- */

lbdialog = function(params) {

	
    //checktype passed args before continuing
	
    if (
        typeof params.content != 'string'
        ||
            (params.okButton &&
                (typeof params.okButton.callback != 'function' && params.okButton.callback)
            )
        ||
            (params.cancelButton &&
                (typeof params.cancelButton.callback != 'function' && params.cancelButton.callback)
            )
        )
        return false;

    
    //clean up from any previous alert
    
    if ($('#lightboxdialog').length != 0) $('#lightboxdialog').remove();

    
    //create, style (with necessary CSS, params-passed CSS and config CSS at top of file) and append dialog box
    
    var box = document.createElement('div');
    with ($(box)) {
    	attr('id', 'lightboxdialog');
    	css({background: '#fff', position: 'absolute', padding: 15, width: 320, height: lightboxConfig.lbdialog_defaultHeight, display: 'none', textAlign: 'left'});
    	if (typeof params.css == 'object') $(box).css(params.css);
    	if (lightboxConfig.lbdialog_css) css(lightboxConfig.lbdialog_css);
    }
    document.body.insertBefore(box, document.body.childNodes[0]);

    
    //add content
    
    var fs = lightboxConfig.lbdialog_css.fontSize+'';
    if (!fs.match(/^\d$/)) fs += 'px'; 
    var header;
    if (params.error) header = ['ERROR', 'b00'];
	else if (params.success) ['SUCCESS', '00b'];
	var header = header == undefined ? '' : "<h5 style='margin: 0 0 10px 0; color: #"+header[1]+"'>"+header[0]+"</h4>";
    $(box).html(header+'<p'+(fs ? " style='font-size: "+fs+"'" : '')+'>'+params.content+"</p<div style='clear: both'></div><br />");

    
    //add buttons, unless params stipulate the dialog should close itself after X seconds. right button will always be put out,
    //whereas left button is only put out if params.cancelButton object is passed, i.e. is confirm, not alert. onclick, along
    //with effecting any callbacks passed, they will also close the lightbox unless  you pass 'noLBClose' in their object
    
    if (!params.autoDisappear) {
	    var buttons = ['OK', 'cancel'];
	    for(var e in buttons) {
	        if (buttons[e] == 'OK' || params.cancelButton) {
	            var but = document.createElement('button');
	            $(but).css({'float': buttons[e] == 'OK' ? 'right' : 'left'});
	            if (params[buttons[e]+'Button']) {
	                if (params[buttons[e]+'Button'].noLBClose) $(but).addClass('noLBClose');
	                var butText = params[buttons[e]+'Button'].text ? params[buttons[e]+'Button'].text : buttons[e];
	                if (params[buttons[e]+'Button'].callback) $(but).click(params[buttons[e]+'Button'].callback);
	            } else
	                var butText = buttons[e];
	            $(but).text(butText);
	            box.appendChild(but);
	        }
	    }
    }

    
    //centre and show
    
    $(box).lightbox(true, params.autoDisappear ? params.autoDisappear : null, params.noLightbox ? true : null);
    
    
    //if set to auto disappear after X seconds, set that up
    
    //if (params.autoCloserAfter) setTimeout(hidelightbox, params.autoCloserAfter * 1000)
    
    
    //lastly, close on keypress to <escape>
    
    $(document).bind('keypress', closeLBDialogOnEscapePress);

}


/* -------------------
| UTILITY FUNC: close lightbox and remove keypress bind to escape key
------------------- */

hidelightbox = function(noFade, callback) {
	$.prototype.lightbox.doHide(noFade, callback);
	$(document).unbind('keypress', closeLBDialogOnEscapePress);
}