/*************************

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.
    Should be run as a method of the element you want to showcase, e.g. $('#myimage').lightbox();
    Lightbox is built and prepared quietly when page loads

    If run on #lightbox, i.e. $('#lightbox').lightbox(), or $.prototype.lightbox.doHide() called, the lightbox hides. Lightbox also closes on click on lightbox
    itself (excluding central element), or on click of any elements in centralElement that is a button or has .close. You can prevent buttons from closing the
    LB if you need to by giving them 'noLBClose' as a class or rel.

    $.prototype.lightbox.doHide() can receive 2 args - noFade (bool) and callback (func). noFade, if true, hides lightbox simply, no fade.

    Lightbox is styled here - no dependency on CSS rules. Obviously the central element is not styled here - should be done in CSS.


    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

    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


*************************/

//prep
var ie6 = navigator.appVersion.match(/MSIE 6\.0/);

//build lightbox onload
$(document).ready(function() {

    //build
    var lightboxDiv = document.createElement('div');
    lightboxDiv.id = 'lightbox';
    $(lightboxDiv).click(function() { $(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: 1, opacity: .6, filter: 'alpha(opacity=60)', width: '100%', height: !ie6 ? '100%' : '1000px', background: '#444', position: 'absolute', left: 0, top: 0, zIndex: 100, display: 'none'});

	//on scroll, silently reposition LB, even if hidden, so its position takes into account page scroll pos
	$(window).scroll($.prototype.lightbox.snapToScrollPos)

});


//func for handling it
$.prototype.lightbox = function(noFade) {

    //prep
    var ac = arguments.callee;
    var centralElement = this.get(0);
    try { if (ie6) $('select').css('visibility', 'hidden'); } catch(e) {}


    //as well as closing the lightbox when clicking outside of the central element, also close if any <button> or element with .close inside central element is clicked
    $(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');
                        try { if (ie6) $('select').css('visibility', 'visible'); } catch(e) {}
                    });
                });
            } else
	    	$(ac.centralElement).slideUp('fast', function() { $('#lightbox').fadeOut('fast', callback ? callback() : null); reinsertCentralElement(); });
        } else {
            $(ac.centralElement).hide();
            $('#lightbox').hide();
            if (callback) callback();
            reinsertCentralElement();
        }

	}


    //scroll pos
    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) scrollX = 0;

    ac.snapToScrollPos = function() { //makes sure LB always fills current viewport, taking into account scroll
		$('#lightbox').css('top', scrollY);
        $('#lightbox').css('left', scrollX);
	}

    //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 101 if not set
        if ($(centralElement).css('position') != 'absolute') $(centralElement).css('position', 'absolute');
        if ($(centralElement).css('zIndex') != 101) $(centralElement).css('zIndex', 101);

        //remember the central element so we can kill it on hide request
        ac.centralElement = centralElement

        //centre it.
        $(centralElement).centreElement();

    }


	//toggle show/hide lightbox
    if ($('#lightbox').is(":hidden")) { //hidden - so show (repositioning lightbox according to page scroll pos as necessary)

		ac.snapToScrollPos();
		var callback = function() { $(centralElement).slideToggle('fast', function() { $(centralElement).children().fadeIn(); }); };
		if (!noFade) $('#lightbox').fadeIn('fast', callback); else { $('#lightbox').show(); callback(); }

    } else //showing - so hide
        ac.doHide();

}



//supporting func - centre central element in lightbox

$.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;
    }

}



// supporting func - on lightbox close, reinsert central element to where it was originally in the DOM

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) {}
}



// supporting func - alert/confirm replacement for javascript default.

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 and append dialog box if doesn't already exist
    var box = document.createElement('div');
    box.id = 'lightboxdialog';
    $(box).css({background: '#fff', position: 'absolute', padding: 15, width: 320, height: 140, display: 'none', textAlign: 'left'});
    if (typeof params.css == 'object') $(box).css(params.css);
    document.body.insertBefore(box, document.body.childNodes[0]);

    //add content
    var header;
    if (params.error) header = ['ERROR', 'b00'];
	else if (params.success) ['SUCCESS', '00b'];
	header = header == undefined ? '' : "<h5 style='margin: 0 0 10px 0; color: #"+header[1]+"'>"+header[0]+"</h4>";
    $(box).html(header+params.content+"<div style='clear: both'></div><br />");

    //add buttons. 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
    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);
        }
    }

    //add any additional CSS
    if (params.css) $(box).css(params.css);

    //finally, centre and show
    $(box).lightbox(true);

}


//supporting func - easier access to $.prototype.lightbox.doHide()
hidelightbox = function(noFade, callback) { $.prototype.lightbox.doHide(noFade, callback); }