/**
 * jQuery Lightbox Plugin (balupton edition) - Lightboxes for jQuery
 * Copyright (C) 2008 Benjamin Arthur Lupton
 * http://jquery.com/plugins/project/jquerylightbox_bal
 *
 * This file is part of jQuery Lightbox (balupton edition).
 * 
 * jQuery Lightbox (balupton edition) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * jQuery Lightbox (balupton edition) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with jQuery Lightbox (balupton edition).  If not, see <http://www.gnu.org/licenses/>.
 *
 * @name jquery_lightbox: jquery.lightbox.js
 * @package jQuery Lightbox Plugin (balupton edition)
 * @version 1.3.7-final
 * @date April 25, 2009
 * @category jQuery plugin
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2008 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license GNU Affero General Public License - {@link http://www.gnu.org/licenses/agpl.html}
 * @example Visit {@link http://jquery.com/plugins/project/jquerylightbox_bal} for more information.
 */

// Start of our jQuery Plugin
(function ($) {	// Create our Plugin function, with $ as the argument (we pass the jQuery object over later)
    // More info: http://docs.jquery.com/Plugins/Authoring#Custom_Alias

    // Debug
    if (typeof $.log === 'undefined') {
        if (!$.browser.safari && typeof window.console !== 'undefined' && typeof window.console.log === 'function') {	// Use window.console
            $.log = function () {
                var args = [];
                for (var i = 0; i < arguments.length; i++) {
                    args.push(arguments[i]);
                }
                window.console.log.apply(window.console, args);
            }
            $.console = {
                log: $.log,
                debug: window.console.debug || $.log,
                warn: window.console.warn || $.log,
                error: window.console.error || $.log,
                trace: window.console.trace || $.log
            }
        }
        else {	// Don't use anything
            $.log = function () { };
            $.console = {
                log: $.log,
                debug: $.log,
                warn: $.log,
                error: alert,
                trace: $.log
            };
        }
    }

    // Pre-Req
    $.params_to_json = $.params_to_json || function (params) {	// Turns a params string or url into an array of params
        // Adjust
        params = String(params);
        // Remove url if need be
        params = params.substring(params.indexOf('?') + 1);
        // params = params.substring(params.indexOf('#')+1);
        // Change + to %20, the %20 is fixed up later with the decode
        params = params.replace(/\+/g, '%20');
        // Do we have JSON string
        if (params.substring(0, 1) === '{' && params.substring(params.length - 1) === '}') {	// We have a JSON string
            return eval(decodeURIComponent(params));
        }
        // We have a params string
        params = params.split(/\&|\&amp\;/);
        var json = {};
        // We have params
        for (var i = 0, n = params.length; i < n; ++i) {
            // Adjust
            var param = params[i] || null;
            if (param === null) { continue; }
            param = param.split('=');
            if (param === null) { continue; }
            // ^ We now have "var=blah" into ["var","blah"]

            // Get
            var key = param[0] || null;
            if (key === null) { continue; }
            if (typeof param[1] === 'undefined') { continue; }
            var value = param[1];
            // ^ We now have the parts

            // Fix
            key = decodeURIComponent(key);
            value = decodeURIComponent(value);
            try {
                // value can be converted
                value = eval(value);
            } catch (e) {
                // value is a normal string
            }

            // Set
            // console.log({'key':key,'value':value}, split);
            var keys = key.split('.');
            if (keys.length === 1) {	// Simple
                json[key] = value;
            }
            else {	// Advanced
                var path = '';
                for (ii in keys) {	//
                    key = keys[ii];
                    path += '.' + key;
                    eval('json' + path + ' = json' + path + ' || {}');
                }
                eval('json' + path + ' = value');
            }
            // ^ We now have the parts added to your JSON object
        }
        return json;
    };

    // Declare our class
    $.LightboxClass = function () {	// This is the handler for our constructor
        this.construct();
    };

    // Extend jQuery elements for Lightbox
    $.fn.lightbox = function (options) {	// Init a el for Lightbox
        // Eg. $('#gallery a').lightbox();

        // If need be: Instantiate $.LightboxClass to $.Lightbox
        $.Lightbox = $.Lightbox || new $.LightboxClass();

        // Handle IE6 appropriatly
        if ($.Lightbox.ie6 && !$.Lightbox.ie6_support) {	// We are IE6 and we want to ignore
            return this; // chain
        }

        // Establish options
        options = $.extend({ start: false, events: true} /* default options */, options);

        // Get group
        var group = $(this);

        // Events?
        if (options.events) {	// Add events
            $(group).unbind('click').click(function () {
                // Get obj
                var obj = $(this);
                // Get rel
                // var rel = $(obj).attr('rel');
                // Init group
                if (!$.Lightbox.init($(obj)[0], group))
                { return false; }
                // Display lightbox
                if (!$.Lightbox.start())
                { return false; }
                // Cancel href
                return false;
            });
            // Add style
            $(group).addClass('lightbox-enabled');
        }

        // Start?
        if (options.start) {	// Start
            // Get obj
            var obj = $(this);
            // Get rel
            // var rel = $(obj).attr('rel');
            // Init group
            if (!$.Lightbox.init($(obj)[0], group))
            { return this; }
            // Display lightbox
            if (!$.Lightbox.start())
            { return this; }
        }

        // And chain
        return this;
    };

    // Define our class
    $.extend($.LightboxClass.prototype,
	{	// Our LightboxClass definition

	    // -----------------
	    // Everyting to do with images

	    images: {

	        // -----------------
	        // Variables

	        // Our array of images
	        list: [], /* [ {
				src: 'url to image',
				link: 'a link to a page',
				title: 'title of the image',
				name: 'name of the image',
				description: 'description of the image'
			} ], */

	        // The current active image
	        image: false,

	        // -----------------
	        // Functions

	        prev: function (image) {	// Get previous image

	            // Get previous from current?
	            if (typeof image === 'undefined') {
	                image = this.active();
	                if (!image) { return image; }
	            }

	            // Is there a previous?
	            if (this.first(image))
	            { return false; }

	            // Get the previous
	            return this.get(image.index - 1);
	        },

	        next: function (image) {	// Get next image

	            // Get next from current?
	            if (typeof image === 'undefined') {
	                image = this.active();
	                if (!image) { return image; }
	            }

	            // Is there a next?
	            if (this.last(image))
	            { return false; }

	            // Get the next
	            return this.get(image.index + 1);
	        },

	        first: function (image) {	//
	            // Get the first image?
	            if (typeof image === 'undefined')
	            { return this.get(0); }

	            // Are we the first?
	            return image.index === 0;
	        },

	        last: function (image) {	//
	            // Get the last image?
	            if (typeof image === 'undefined')
	            { return this.get(this.size() - 1); }

	            // Are we the last?
	            return image.index === this.size() - 1;
	        },

	        single: function () {	// Are we only one
	            return this.size() === 1;
	        },

	        size: function () {	// How many images do we have
	            return this.list.length;
	        },

	        empty: function () {	// Are we empty
	            return this.size() === 0;
	        },

	        clear: function () {	// Clear image arrray
	            this.list = [];
	            this.image = false;
	        },

	        active: function (image) {	// Set or get the active image
	            // Use false to reset

	            // Get the active image?
	            if (typeof image === 'undefined')
	            { return this.image; }

	            // Set the ative image
	            if (image !== false) {	// Make sure image exists
	                image = this.get(image);
	                if (!image) {	// Error
	                    return image;
	                }
	            }

	            // Set the active image
	            this.image = image;
	            return true;
	        },

	        add: function (obj) {
	            // Do we need to recurse?
	            if (obj[0]) {	// We have a lot of images
	                for (var i = 0; i < obj.length; i++)
	                { this.add(obj[i]); }
	                return true;
	            }

	            // Default image

	            // Try and create a image
	            var image = this.create(obj);
	            if (!image) { return image; }

	            // Set image index
	            image.index = this.size();

	            // Push image
	            this.list.push(image);

	            // Success
	            return true;
	        },

	        create: function (obj) {	// Create image

	            // Define
	            var image = { // default
	                src: '',
	                title: 'Untitled',
	                description: '',
	                name: '',
	                index: -1,
	                color: null,
	                width: null,
	                height: null,
	                image: true
	            };

	            // Create
	            if (obj.image) {	// Already a image, so copy over values
	                image.src = obj.src || image.src;
	                image.title = obj.title || image.title;
	                image.description = obj.description || image.description;
	                image.name = obj.name || image.name;
	                image.color = obj.color || image.color;
	                image.width = obj.width || image.width;
	                image.height = obj.height || image.height;
	                image.index = obj.index || image.index;
	            }
	            else if (obj.tagName) {	// We are an element
	                obj = $(obj);
	                if (obj.attr('src') || obj.attr('href')) {
	                    image.src = obj.attr('src') || obj.attr('href');
	                    image.title = obj.attr('title') || obj.attr('alt') || image.title;
	                    image.name = obj.attr('name') || '';
	                    image.color = obj.css('backgroundColor');
	                    // Extract description from title
	                    var s = image.title.indexOf(': ');
	                    if (s > 0) {	// Description exists
	                        image.description = image.title.substring(s + 2) || image.description;
	                        image.title = image.title.substring(0, s) || image.title;
	                    }
	                }
	                else {	// Unsupported element
	                    image = false;
	                }
	            }
	            else {	// Unknown
	                image = false;
	            }

	            if (!image) {	// Error
	                $.console.error('We dont know what we have:', obj);
	                return false;
	            }

	            // Success
	            return image;
	        },

	        get: function (image) {	// Get the active, or specified image

	            // Establish image
	            if (typeof image === 'undefined' || image === null) {	// Get the active image
	                return this.active();
	            }
	            else
	                if (typeof image === 'number') {	// We have a index

	                    // Get image
	                    image = this.list[image] || false;
	                }
	                else {	// Create
	                    image = this.create(image);
	                    if (!image) { return false; }

	                    // Find
	                    var f = false;
	                    for (var i = 0; i < this.size(); i++) {
	                        var c = this.list[i];
	                        if (c.src === image.src && c.title === image.title && c.description === image.description)
	                        { f = c; }
	                    }

	                    // Found?
	                    image = f;
	                }

	            // Determine image
	            if (!image) {	// Image doesn't exist
	                $.console.error('The desired image does not exist: ', image, this.list);
	                return false;
	            }

	            // Return image
	            return image;
	        },

	        debug: function () {
	            return $.Lightbox.debug(arguments);
	        }

	    },

	    // -----------------
	    // Variables

	    constructed: false,
	    compressed: null,

	    // -----------------
	    // Options

	    src: null, 	// the source location of our js file
	    baseurl: null,

	    files: {
	        compressed: {
	            js: {
	                lightbox: 'jquery-lightbox/jquery.lightbox.min.js',
	                colorBlend: 'jquery-lightbox/jquery.color.min.js'
	            },
	            css: {
	                lightbox: 'jquery-lightbox/jquery.lightbox.css'
	            }
	        },
	        uncompressed: {
	            js: {
	                lightbox: 'jquery-lightbox/jquery.lightbox.js',
	                colorBlend: 'jquery-lightbox/jquery.color.js'
	            },
	            css: {
	                lightbox: 'jquery-lightbox/jquery.lightbox.css'
	            }
	        },
	        images: {
	            prev: 'jquery-lightbox/prev.gif',
	            next: 'jquery-lightbox/next.gif',
	            blank: 'jquery-lightbox/blank.gif',
	            loading: 'jquery-lightbox/loading.gif'
	        }
	    },

	    text: {
	        // For translating
	        image: 'Image',
	        of: 'of',
	        close: 'Close X',
	        closeInfo: 'You can also click anywhere outside the image to close.',
	        download: 'Download.',
	        help: {
	            close: 'Click to close',
	            interact: 'Hover to interact'
	        },
	        about: {
	            text: '',
	            title: '',
	            link: 'http://jquery.com/plugins/project/jquerylightbox_bal'
	        }
	    },

	    keys: {
	        close: 'c',
	        prev: 'p',
	        next: 'n'
	    },

	    handlers: {
	        // For custom actions
	        show: null
	    },

	    opacity: 0.9,
	    padding: null, 	// if null - autodetect

	    speed: 400, 	// Duration of effect, milliseconds

	    rel: 'lightbox', // What to look for in the rels

	    auto_relify: true, 	// should we automaticly do the rels?

	    auto_scroll: 'follow', // should the lightbox scroll with the page? follow, disabled, ignore
	    auto_resize: true, 	// true or false

	    ie6: null, 	// are we ie6?
	    ie6_support: true, 	// have ie6 support
	    ie6_upgrade: true, 	// show ie6 upgrade message

	    colorBlend: null, 	// null - auto-detect, true - force, false - no

	    download_link: false, // Display the download link

	    show_helper_text: false, // Display the helper text up the top right
	    show_linkback: true, // true, false
	    show_info: 'true', // auto - automaticly handle, true - force
	    show_extended_info: 'true', // auto - automaticly handle, true - force	

	    // names of the options that can be modified
	    options: ['show_helper_text', 'auto_scroll', 'auto_resize', 'download_link', 'show_info', 'show_extended_info', 'ie6_support', 'ie6_upgrade', 'colorBlend', 'baseurl', 'files', 'text', 'show_linkback', 'keys', 'opacity', 'padding', 'speed', 'rel', 'auto_relify'],

	    // -----------------
	    // Functions

	    construct: function (options) {	// Construct our Lightbox

	        // -------------------
	        // Prepare

	        // Initial construct
	        var initial = typeof this.constructed === 'undefined' || this.constructed === false;
	        this.constructed = true;

	        // Perform domReady
	        var domReady = initial;

	        // Prepare options
	        options = options || {};

	        // -------------------
	        // Handle files

	        // Prepend function to use later
	        var prepend = function (item, value) {
	            if (typeof item === 'object') {
	                for (var i in item) {
	                    item[i] = prepend(item[i], value);
	                }
	            } else if (typeof value === 'array') {
	                for (var i = 0, n = item.length; i < n; ++i) {
	                    item[i] = prepend(item[i], value);
	                }
	            } else {
	                item = value + item;
	            }
	            return item;
	        }

	        // Add baseurl
	        if (initial && (typeof options.files === 'undefined')) {	// Load the files like default

	            // Reset compressed
	            this.compressed = null;

	            // Get the src of the first script tag that includes our js file (with or without an appendix)
	            var $script = $('script[src*=' + this.files.compressed.js.lightbox + ']:first');
	            if ($script.length !== 0) {
	                // Compressed
	                $.extend(true, this.files, this.files.compressed);
	                this.compressed = true;
	            } else {
	                // Uncompressed
	                $script = $('script[src*=' + this.files.uncompressed.js.lightbox + ']:first');
	                if ($script.length !== 0) {
	                    // Uncompressed
	                    $.extend(true, this.files, this.files.uncompressed);
	                    this.compressed = false;
	                } else {
	                    // Nothing
	                }
	            }

	            // Make sure we found ourselves
	            if (this.compressed === null) {	// We didn't
	                $.console.error('Lightbox was not able to find it\'s javascript script tag necessary for auto-inclusion.');
	                // We don't work with files anymore, so don't care for domReady
	                domReady = false;
	            }
	            else {	// We found ourself

	                // Grab the script src
	                this.src = $script.attr('src');

	                // The baseurl is the src up until the start of our js file
	                this.baseurl = this.src.substring(0, this.src.indexOf(this.files.js.lightbox));

	                // Prepend baseurl to files
	                this.files = prepend(this.files, this.baseurl);

	                // Now as we have source, we may have more params
	                options = $.extend(options, $.params_to_json(this.src));
	            }

	        }
	        else
	            if (typeof options.files === 'object') {	// We have custom files
	                // Prepend baseurl to files
	                options.files = prepend(options.files, this.baseurl);
	            }
	            else {	// Don't have any files, so no need to perform domReady
	                domReady = false;
	            }

	        // -------------------
	        // Apply options

	        for (var i in this.options) {	// Cycle through the options
	            var name = this.options[i];
	            if ((typeof options[name] === 'object') && (typeof this[name] === 'object')) {	// We have a group like text or files
	                this[name] = $.extend(true, this[name], options[name]);
	            }
	            else if (typeof options[name] !== 'undefined') {	// We have that option, so apply it
	                this[name] = options[name];
	            }
	        } delete i;

	        // -------------------
	        // Figure out what to do

	        // Handle IE6
	        if (initial && navigator.userAgent.indexOf('MSIE 6') >= 0) {	// Is IE6
	            this.ie6 = true;
	        }
	        else {	// We are not IE6
	            this.ie6 = false;
	        }

	        // -------------------
	        // Handle our DOM

	        if (domReady || typeof options.download_link !== 'undefined' || typeof options.colorBlend !== 'undefined' || typeof options.files === 'object' || typeof options.text === 'object' || typeof options.show_linkback !== 'undefined' || typeof options.scroll_with !== 'undefined') {	// We have reason to handle the dom
	            $(function () {
	                // DOM is ready, so fire our DOM handler
	                $.Lightbox.domReady();
	            });
	        }

	        // -------------------
	        // Finish Up

	        // All good
	        return true;
	    },

	    domReady: function () {
	        // -------------------
	        // Include resources

	        // Grab resources
	        var bodyEl = document.getElementsByTagName($.browser.safari ? 'head' : 'body')[0];
	        var stylesheets = this.files.css;
	        var scripts = this.files.js;

	        // Handle IE6 appropriatly
	        if (this.ie6 && this.ie6_upgrade) {	// Add the upgrade message
	            scripts.ie6 = 'http://www.savethedevelopers.org/say.no.to.ie.6.js';
	        }

	        // colorBlend
	        if (this.colorBlend === true && typeof $.colorBlend === 'undefined') {	// Force colorBlend
	            this.colorBlend = true;
	            // Leave file in place to be loaded
	        }
	        else {	// We either have colorBlend or we don't
	            this.colorBlend = typeof $.colorBlend !== 'undefined';
	            // Remove colorBlend file
	            delete scripts.colorBlend;
	        }

	        // Include stylesheets
	        for (stylesheet in stylesheets) {
	            var linkEl = document.createElement('link');
	            linkEl.type = 'text/css';
	            linkEl.rel = 'stylesheet';
	            linkEl.media = 'screen';
	            linkEl.href = stylesheets[stylesheet];
	            linkEl.id = 'lightbox-stylesheet-' + stylesheet.replace(/[^a-zA-Z0-9]/g, '');
	            $('#' + linkEl.id).remove();
	            bodyEl.appendChild(linkEl);
	        }

	        // Include javascripts
	        for (script in scripts) {
	            var scriptEl = document.createElement('script');
	            scriptEl.type = 'text/javascript';
	            scriptEl.src = scripts[script];
	            scriptEl.id = 'lightbox-script-' + script.replace(/[^a-zA-Z0-9]/g, '');
	            $('#' + scriptEl.id).remove();
	            bodyEl.appendChild(scriptEl);
	        }

	        // Cleanup
	        delete scripts;
	        delete stylesheets;
	        delete bodyEl;

	        // -------------------
	        // Append display

	        // Append markup
	        $('#lightbox,#lightbox-overlay').remove();
	        $('body').append('<div id="lightbox-overlay"><div id="lightbox-overlay-text">' + (this.show_linkback ? '<p><span id="lightbox-overlay-text-about"><a href="#" title="' + this.text.about.title + '">' + this.text.about.text + '</a></span></p><p>&nbsp;</p>' : '') + (this.show_helper_text ? '<p><span id="lightbox-overlay-text-close">' + this.text.help.close + '</span><br/>&nbsp;<span id="lightbox-overlay-text-interact">' + this.text.help.interact + '</span></p>' : '') + '</div></div><div id="lightbox"><div id="lightbox-imageBox"><div id="lightbox-imageContainer"><img id="lightbox-image" /><div id="lightbox-nav"><a href="#" id="lightbox-nav-btnPrev"></a><a href="#" id="lightbox-nav-btnNext"></a></div><div id="lightbox-loading"><a href="#" id="lightbox-loading-link"><img src="' + this.files.images.loading + '" /></a></div></div></div><div id="lightbox-infoBox"><div id="lightbox-infoContainer"><div id="lightbox-infoHeader"><span id="lightbox-caption">' + (this.download_link ? '<a href="#" title="' + this.text.download + '" id="lightbox-caption-title"></a>' : '<span id="lightbox-caption-title"></span>') + '<span id="lightbox-caption-seperator"></span><span id="lightbox-caption-description"></span></span></div><div id="lightbox-infoFooter"><span id="lightbox-currentNumber"></span><span id="lightbox-close"><a href="#" id="lightbox-close-button" title="' + this.text.closeInfo + '">' + this.text.close + '</a></span></div><div id="lightbox-infoContainer-clear"></div></div></div></div>');

	        // Update Boxes - for some crazy reason this has to be before the hide in safari and konqueror
	        this.resizeBoxes();
	        this.repositionBoxes();

	        // Hide
	        $('#lightbox,#lightbox-overlay,#lightbox-overlay-text-interact').hide();

	        // -------------------
	        // Browser specifics

	        // Handle IE6
	        if (this.ie6 && this.ie6_support) {	// Support IE6
	            // IE6 does not support fixed positioning so absolute it
	            // ^ This is okay as we disable scrolling
	            $('#lightbox-overlay').css({
	                position: 'absolute',
	                top: '0px',
	                left: '0px'
	            });
	        }

	        // -------------------
	        // Preload Images

	        // Cycle and preload
	        $.each(this.files.images, function () {	// Proload the image
	            var preloader = new Image();
	            preloader.onload = function () {
	                preloader.onload = null;
	                preloader = null;
	            }; preloader.src = this;
	        });

	        // -------------------
	        // Apply events

	        // If the window resizes, act appropriatly
	        $(window).unbind('resize').resize(function () {	// The window has been resized
	            $.Lightbox.resizeBoxes('resized');
	        });

	        // If the window scrolls, act appropriatly
	        if (this.scroll === 'follow') {	// We want to
	            $(window).scroll(function () {	// The window has scrolled
	                $.Lightbox.repositionBoxes();
	            });
	        }

	        // Prev
	        $('#lightbox-nav-btnPrev').unbind().hover(function () { // over
	            $(this).css({ 'background': 'url(' + $.Lightbox.files.images.prev + ') left 45% no-repeat' });
	        }, function () { // out
	            $(this).css({ 'background': 'transparent url(' + $.Lightbox.files.images.blank + ') no-repeat' });
	        }).click(function () {
	            $.Lightbox.showImage($.Lightbox.images.prev());
	            return false;
	        });

	        // Next
	        $('#lightbox-nav-btnNext').unbind().hover(function () { // over
	            $(this).css({ 'background': 'url(' + $.Lightbox.files.images.next + ') right 45% no-repeat' });
	        }, function () { // out
	            $(this).css({ 'background': 'transparent url(' + $.Lightbox.files.images.blank + ') no-repeat' });
	        }).click(function () {
	            $.Lightbox.showImage($.Lightbox.images.next());
	            return false;
	        });

	        // Help
	        if (this.show_linkback) {	// Linkback exists so add handler
	            $('#lightbox-overlay-text-about a').click(function () { window.open($.Lightbox.text.about.link); return false; });
	        }
	        $('#lightbox-overlay-text-close').unbind().hover(
				function () {
				    $('#lightbox-overlay-text-interact').fadeIn();
				},
				function () {
				    $('#lightbox-overlay-text-interact').fadeOut();
				}
			);

	        // Image link
	        $('#lightbox-caption-title').click(function () { window.open($(this).attr('href')); return false; });

	        // Assign close clicks
	        $('#lightbox-overlay, #lightbox, #lightbox-loading-link, #lightbox-btnClose').unbind().click(function () {
	            $.Lightbox.finish();
	            return false;
	        });

	        // -------------------
	        // Finish Up

	        // Relify
	        if (this.auto_relify) {	// We want to relify, no the user
	            this.relify();
	        }

	        // All good
	        return true;
	    },

	    relify: function () {	// Create event

	        //
	        var groups = {};
	        var groups_n = 0;
	        var orig_rel = this.rel;
	        // Create the groups
	        $.each($('[rel*=' + orig_rel + ']'), function (index, obj) {
	            // Get the group
	            var rel = $(obj).attr('rel');
	            // Are we really a group
	            if (rel === orig_rel) {	// We aren't
	                rel = groups_n; // we are individual
	            }
	            // Does the group exist
	            if (typeof groups[rel] === 'undefined') {	// Make the group
	                groups[rel] = [];
	                groups_n++;
	            }
	            // Append the image
	            groups[rel].push(obj);
	        });
	        // Lightbox groups
	        $.each(groups, function (index, group) {
	            $(group).lightbox();
	        });
	        // Done
	        return true;
	    },

	    init: function (image, images) {	// Init a batch of lightboxes

	        // Establish images
	        if (typeof images === 'undefined') {
	            images = image;
	            image = 0;
	        }

	        // Clear
	        this.images.clear();

	        // Add images
	        if (!this.images.add(images))
	        { return false; }

	        // Do we need to bother
	        if (this.images.empty()) {	// No images
	            $.console.warn('WARNING', 'Lightbox started, but no images: ', image, images);
	            return false;
	        }

	        // Set active
	        if (!this.images.active(image))
	        { return false; }

	        // Done
	        return true;
	    },

	    start: function () {	// Display the lightbox

	        // We are alive
	        this.visible = true;

	        // Adjust scrolling
	        if (this.scroll === 'disable') {	// 
	            $(document.body).css('overflow', 'hidden');
	        }

	        // Fix attention seekers
	        $('embed, object, select').css('visibility', 'hidden'); //.hide(); - don't use this, give it a go, find out why!

	        // Resize the boxes appropriatly
	        this.resizeBoxes('general');

	        // Reposition the Boxes
	        this.repositionBoxes({ 'speed': 0 });

	        // Hide things
	        //$('#lightbox-infoFooter').hide(); // we hide this here because it makes the display smoother
	        $('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-infoBox').hide();

	        // Display the boxes
	        $('#lightbox-overlay').css('opacity', this.opacity).fadeIn(400, function () {
	            // Show the lightbox
	            $('#lightbox').fadeIn(300);

	            // Display first image
	            if (!$.Lightbox.showImage($.Lightbox.images.active()))
	            { $.Lightbox.finish(); return false; }
	        });

	        // All done
	        return true;
	    },

	    finish: function () {	// Get rid of lightbox

	        // Hide lightbox
	        $('#lightbox').hide();
	        $('#lightbox-overlay').fadeOut(function () { $('#lightbox-overlay').hide(); });

	        // Fix attention seekers
	        $('embed, object, select').css({ 'visibility': 'visible' }); //.show();

	        // Kill active image
	        this.images.active(false);

	        // Adjust scrolling
	        if (this.scroll === 'disable') {	// 
	            $(document.body).css('overflow', 'visible');
	        }

	        // We are dead
	        this.visible = false;

	    },

	    resizeBoxes: function (type) {	// Resize the boxes
	        // Used on transition or window resize

	        // Resize Overlay
	        if (type !== 'transition') {	// We don't care for transition
	            var $body = $(this.ie6 ? document.body : document);
	            $('#lightbox-overlay').css({
	                width: $body.width(),
	                height: $body.height()
	            });
	            delete $body;
	        }

	        // Handle cases
	        switch (type) {
	            case 'general': // general resize (start of lightbox)
	                return true;
	                break;
	            case 'resized': // window was resized
	                if (this.auto_resize === false) {	// Stop
	                    // Reposition
	                    this.repositionBoxes({ 'nHeight': nHeight, 'speed': this.speed });
	                    return true;
	                }
	            case 'transition': // transition between images
	            default: // unknown
	                break;
	        }

	        // Get image
	        var image = this.images.active();
	        if (!image || !image.width || !this.visible) {	// No image or no visible lightbox, so we don't care
	            //$.console.warn('A resize occured while no image or no lightbox...');
	            return false;
	        }

	        // Resize image box
	        // i:image, w:window, b:box, c:current, n:new, d:difference

	        // Get image dimensions
	        var iWidth = image.width;
	        var iHeight = image.height;

	        // Get window dimensions
	        var wWidth = $(window).width();
	        var wHeight = $(window).height();

	        // Check if we are in size
	        // Lightbox can take up 4/5 of size
	        if (this.auto_resize !== false) {	// We want to auto resize
	            var maxWidth = Math.floor(wWidth * (4 / 5));
	            var maxHeight = Math.floor(wHeight * (4 / 5));
	            var resizeRatio;
	            while (iWidth > maxWidth || iHeight > maxHeight) {	// We need to resize
	                if (iWidth > maxWidth) {	// Resize width, then height proportionally
	                    resizeRatio = maxWidth / iWidth;
	                    iWidth = maxWidth;
	                    iHeight = Math.floor(iHeight * resizeRatio);
	                }
	                if (iHeight > maxHeight) {	// Resize height, then width proportionally
	                    resizeRatio = maxHeight / iHeight;
	                    iHeight = maxHeight;
	                    iWidth = Math.floor(iWidth * resizeRatio);
	                }
	            }
	        }

	        // Get current width and height
	        var cWidth = $('#lightbox-imageBox').width();
	        var cHeight = $('#lightbox-imageBox').height();

	        // Get the width and height of the selected image plus the padding
	        // padding*2 for both sides (left+right || top+bottom)
	        var nWidth = (iWidth + (this.padding * 2));
	        var nHeight = (iHeight + (this.padding * 2));

	        // Diferences
	        var dWidth = cWidth - nWidth;
	        var dHeight = cHeight - nHeight;

	        // Set the overlay buttons height and the infobox width
	        // Other dimensions specified by CSS
	        $('#lightbox-nav-btnPrev,#lightbox-nav-btnNext').css('height', nHeight);
	        $('#lightbox-infoBox').css('width', nWidth);

	        // Handle final action
	        if (type === 'transition') {	// We are transition
	            // Do we need to wait? (just a nice effect to counter the other
	            if (dWidth === 0 && dHeight === 0) {	// We are the same size
	                this.pause(this.speed / 3);
	                this.showImage(null, 3);
	            }
	            else {	// We are not the same size
	                // Animate
	                $('#lightbox-image').width(iWidth).height(iHeight);
	                $('#lightbox-imageBox').animate({ width: nWidth, height: nHeight }, this.speed, function () { $.Lightbox.showImage(null, 3); });
	            }
	        }
	        else {	// We are a resize
	            // Animate Lightbox
	            $('#lightbox-image').animate({ width: iWidth, height: iHeight }, this.speed);
	            $('#lightbox-imageBox').animate({ width: nWidth, height: nHeight }, this.speed);
	        }

	        // Reposition
	        this.repositionBoxes({ 'nHeight': nHeight, 'speed': this.speed });

	        // Done
	        return true;
	    },

	    repositioning: false, // are we currently repositioning
	    reposition_failsafe: false, // failsafe
	    repositionBoxes: function (options) {
	        // Prepare
	        if (this.repositioning) {	// Already here
	            this.reposition_failsafe = true;
	            return null;
	        }
	        this.repositioning = true;

	        // Options
	        options = $.extend({}, options);
	        options.callback = options.callback || null;
	        options.speed = options.speed || 'slow';

	        // Get page scroll
	        var pageScroll = this.getPageScroll();

	        // Figure it out
	        // alert($(window).height()+"\n"+$(document.body).height()+"\n"+$(document).height());
	        // var nHeight = options.nHeight || parseInt($('#lightbox').height(),10) || $(document).height()/3;
	        var nHeight = options.nHeight || parseInt($('#lightbox').height(), 10);

	        // Display lightbox in center
	        // var nTop = pageScroll.yScroll + ($(document.body).height() /*frame height*/ - nHeight) / 2.5;
	        var nTop = pageScroll.yScroll + ($(window).height() /*frame height*/ - nHeight) / 2.5;
	        var nLeft = pageScroll.xScroll;

	        // Animate
	        var css = {
	            left: nLeft,
	            top: nTop
	        };
	        if (options.speed) {
	            $('#lightbox').animate(css, 'slow', function () {
	                if ($.Lightbox.reposition_failsafe) {	// Fire again
	                    $.Lightbox.repositioning = $.Lightbox.reposition_failsafe = false;
	                    $.Lightbox.repositionBoxes(options);
	                }
	                else {	// Done
	                    $.Lightbox.repositioning = false;
	                    if (options.callback) {	// Call the user callback
	                        options.callback();
	                    }
	                }
	            });
	        }
	        else {
	            $('#lightbox').css(css);
	            if (this.reposition_failsafe) {	// Fire again
	                this.repositioning = this.reposition_failsafe = false;
	                this.repositionBoxes(options);
	            }
	            else {	// Done
	                this.repositioning = false;
	            }
	        }

	        // Done
	        return true;
	    },

	    visible: false,
	    showImage: function (image, step) {
	        // Establish image
	        image = this.images.get(image);
	        if (!image) { return image; }

	        // Default step
	        step = step || 1;

	        // Split up below for jsLint compliance
	        var skipped_step_1 = step > 1 && this.images.active().src !== image.src;
	        var skipped_step_2 = step > 2 && $('#lightbox-image').attr('src') !== image.src;
	        if (skipped_step_1 || skipped_step_2) {	// Force step 1
	            $.console.info('We wanted to skip a few steps: ', image, step, skipped_step_1, skipped_step_2);
	            step = 1;
	        }

	        // What do we need to do
	        switch (step) {
	            // --------------------------------- 
	            // We need to preload 
	            case 1:

	                // Disable keyboard nav
	                this.KeyboardNav_Disable();

	                // Show the loading image
	                $('#lightbox-loading').show();

	                // Hide things
	                $('#lightbox-image,#lightbox-nav,#lightbox-nav-btnPrev,#lightbox-nav-btnNext,#lightbox-infoBox').hide();

	                // Remove show info events
	                $('#lightbox-imageBox').unbind();
	                // ^ Why? Because otherwise when the image is changing, the info pops out, not good!

	                // Make the image the active image
	                if (!this.images.active(image)) { return false; }

	                // Check if we need to preload
	                if (image.width && image.height) {	// We don't
	                    // Continue to next step
	                    this.showImage(null, 2);
	                }
	                else {	// We do
	                    // Create preloader
	                    var preloader = new Image();
	                    // Set callback
	                    preloader.onload = function () {	// We have preloaded the image
	                        // Update image with our new info
	                        image.width = preloader.width;
	                        image.height = preloader.height;
	                        // Continue to next step
	                        $.Lightbox.showImage(null, 2);
	                        // Kill preloader
	                        preloader.onload = null;
	                        preloader = null;
	                    };
	                    // Start preload
	                    preloader.src = image.src;
	                }

	                // Done
	                break;


	            // --------------------------------- 
	            // Resize the container 
	            case 2:

	                // Apply image changes
	                $('#lightbox-image').attr('src', image.src);

	                // Set container border (Moved here for Konqueror fix - Credits to Blueyed)
	                if (typeof this.padding === 'undefined' || this.padding === null || isNaN(this.padding)) {	// Autodetect
	                    this.padding = parseInt($('#lightbox-imageContainer').css('padding-left'), 10) || parseInt($('#lightbox-imageContainer').css('padding'), 10) || 0;
	                }

	                // Use colorBlend?
	                if (this.colorBlend) {	// We have colorBlend
	                    // Background
	                    $('#lightbox-overlay').animate({ 'backgroundColor': image.color }, this.speed * 2);
	                    // Border
	                    $('#lightbox-imageBox').css('borderColor', image.color);
	                }

	                // Resize boxes
	                this.resizeBoxes('transition');
	                // ^ contains callback to next step

	                // Done
	                break;


	            // --------------------------------- 
	            // Display the image 
	            case 3:

	                // Hide loading
	                $('#lightbox-loading').hide();

	                // Animate image
	                $('#lightbox-image').fadeIn(this.speed * 1.5, function () { $.Lightbox.showImage(null, 4); });

	                // Start the proloading of other images
	                this.preloadNeighbours();

	                // Fire custom handler show
	                if (this.handlers.show !== null) {	// Fire it
	                    this.handlers.show(image);
	                }

	                // Done
	                break;


	            // --------------------------------- 
	            // Set image info / Set navigation 
	            case 4:

	                // ---------------------------------
	                // Set image info

	                // Hide and set image info
	                var $title = $('#lightbox-caption-title').html(image.title || 'Untitled');
	                if (this.download_link)
	                { $title.attr('href', this.download_link ? image.src : ''); }
	                delete $title;
	                $('#lightbox-caption-seperator').html(image.description ? ': ' : '');
	                $('#lightbox-caption-description').html(image.description || '&nbsp;');

	                // If we have a set, display image position
	                if (this.images.size() > 1) {	// Display
	                    $('#lightbox-currentNumber').html(this.text.image + '&nbsp;' + (image.index + 1) + '&nbsp;' + this.text.of + '&nbsp;' + this.images.size());
	                } else {	// Empty
	                    $('#lightbox-currentNumber').html('&nbsp;');
	                }

	                // ---------------------------------
	                // Info events

	                // Apply event
	                $('#lightbox-imageBox').unbind('mouseover').mouseover(function () {
	                    $('#lightbox-infoBox:not(:visible)').stop().slideDown('fast');
	                });

	                // Apply event
	                $('#lightbox-infoBox').unbind('mouseover').mouseover(function () {
	                    $('#lightbox-infoFooter:not(:visible)').stop().slideDown('fast');
	                });

	                // Forced show?
	                if (this.show_extended_info === true) {	// Force show
	                    $('#lightbox-imageBox').trigger('mouseover');
	                    $('#lightbox-infoBox').trigger('mouseover');
	                }
	                else if (this.show_info === true) {	// Force show
	                    $('#lightbox-imageBox').trigger('mouseover');
	                }

	                // ---------------------------------
	                // Set navigation

	                // Instead to define this configuration in CSS file, we define here. And it's need to IE. Just.
	                $('#lightbox-nav-btnPrev, #lightbox-nav-btnNext').css({ 'background': 'transparent url(' + this.files.images.blank + ') no-repeat' });

	                // If not first, show previous button
	                if (!this.images.first(image)) {
	                    // Not first, show button
	                    $('#lightbox-nav-btnPrev').show();
	                }

	                // If not last, show next button
	                if (!this.images.last(image)) {
	                    // Not first, show button
	                    $('#lightbox-nav-btnNext').show();
	                }

	                // Make navigation active / show it
	                $('#lightbox-nav').show();

	                // Enable keyboard navigation
	                this.KeyboardNav_Enable();

	                // Done
	                break;


	            // --------------------------------- 
	            // Error handling 
	            default:
	                $.console.error('Don\'t know what to do: ', image, step);
	                return this.showImage(image, 1);
	                // break;

	        }

	        // All done
	        return true;
	    },

	    preloadNeighbours: function () {	// Preload all neighbour images

	        // Do we need to do this?
	        if (this.images.single() || this.images.empty())
	        { return true; }

	        // Get active image
	        var image = this.images.active();
	        if (!image) { return image; }

	        // Load previous
	        var prev = this.images.prev(image);
	        var objNext;
	        if (prev) {
	            objNext = new Image();
	            objNext.src = prev.src;
	        }

	        // Load next
	        var next = this.images.next(image);
	        if (next) {
	            objNext = new Image();
	            objNext.src = next.src;
	        }
	    },

	    // --------------------------------------------------
	    // Things we don't really care about

	    KeyboardNav_Enable: function () {
	        $(document).keydown(function (objEvent) {
	            $.Lightbox.KeyboardNav_Action(objEvent);
	        });
	    },

	    KeyboardNav_Disable: function () {
	        $(document).unbind('keydown');
	    },

	    KeyboardNav_Action: function (objEvent) {
	        // Prepare
	        objEvent = objEvent || window.event;

	        // Get the keycode
	        var keycode = objEvent.keyCode;
	        var escapeKey = objEvent.DOM_VK_ESCAPE /* moz */ || 27;

	        // Get key
	        var key = String.fromCharCode(keycode).toLowerCase();

	        // Close?
	        if (key === this.keys.close || keycode === escapeKey)
	        { return $.Lightbox.finish(); }

	        // Prev?
	        if (key === this.keys.prev || keycode === 37) {	// We want previous
	            return $.Lightbox.showImage($.Lightbox.images.prev());
	        }

	        // Next?
	        if (key === this.keys.next || keycode === 39) {	// We want next
	            return $.Lightbox.showImage($.Lightbox.images.next());
	        }

	        // Unknown
	        return true;
	    },

	    getPageScroll: function () {
	        var xScroll, yScroll;
	        if (self.pageYOffset) {	// Some browser
	            yScroll = self.pageYOffset;
	            xScroll = self.pageXOffset;
	        } else if (document.documentElement && document.documentElement.scrollTop) {	// Explorer 6 Strict
	            yScroll = document.documentElement.scrollTop;
	            xScroll = document.documentElement.scrollLeft;
	        } else if (document.body) {	// All other browsers
	            yScroll = document.body.scrollTop;
	            xScroll = document.body.scrollLeft;
	        }
	        var arrayPageScroll = { 'xScroll': xScroll, 'yScroll': yScroll };
	        return arrayPageScroll;
	    },

	    pause: function (ms) {
	        var date = new Date();
	        var curDate = null;
	        do { curDate = new Date(); }
	        while (curDate - date < ms);
	    }

	}); // We have finished extending/defining our LightboxClass


    // --------------------------------------------------
    // Finish up

    // Instantiate
    if (typeof $.Lightbox === 'undefined') {	// 
        $.Lightbox = new $.LightboxClass();
    }

    // Finished definition

})(jQuery);  // We are done with our plugin, so lets call it with jQuery as the argument

