function Lightbox(config) {
    var config = config || {};
    config.width = config.hasOwnProperty('width') ? config.width : 814;
    config.height = 0;
    config.element = config.hasOwnProperty('element') ? config.element : $('<div id="lightbox"><div id="lightbox-close"></div><div id="lightbox-wrap"><iframe id="lightbox-iframe" name="lightbox-iframe" noresize="noresize" frameborder="0" border="0" cellspacing="0" scrolling="no"></iframe></div><div class="shadow shadow1"></div><div class="shadow shadow2"></div><div class="shadow shadow3"></div><div class="shadow shadow4"></div></div>');
    config.overlay = config.hasOwnProperty('overlay') ? config.overlay : $('<div id="overlay"></div>');
    config.closeSelector = config.hasOwnProperty('closeSelector') ? config.closeSelector : '#lightbox-close';
    config.transition = config.hasOwnProperty('transition') ? config.transition : 'grow';
    config.delay = config.hasOwnProperty('delay') ? config.delay : 1;
    config.offsetY = config.hasOwnProperty('offsetY') ? config.offsetY : 0;
    config.offsetX = config.hasOwnProperty('offsetX') ? config.offsetX : 0;

    var visible = false;
    var created = false;
    var iframe = false;
    var link = false;
    var timeout = false;
    var delayTimeout = false;
    var frameTimeout = false;
    var currentHref = false;
    var that = this;
    var frameChanged = false;

    // Runs callback if frame is fully loaded
    var frameReadyLoad = function(callback) {
        var document = (iframe[0].contentWindow.document || iframe[0].contentDocument);
        if (document.getElementById('ready')) {
            var ready = document.getElementById('ready');
            ready.parentNode.removeChild(ready);
            clearInterval(frameTimeout);
            callback();
        } else {
            return false;
        }
    }

    // Run the open function with a user configurable delay
    this.open = function(clickedLink) {
        link = clickedLink;
        delayTimeout = setTimeout(function() {
            doOpen(link);
        }, config.delay);
    }

    var doOpen = function(link) {
        // Create the lightbox elements if they don't already exist on the page
        if (!created) {

            // Add a close links to selected elements in the lightbox
            if (config.closeSelector) {
                config.element.find(config.closeSelector).click(function() {
                    that.close();
                });
            }

            iframe = config.element.find('iframe');
            iframe.load(frameChange);
            $('body').prepend(config.element);

            // Create overlay
            if (config.overlay) {
                config.overlay.hide();
                config.overlay.css({
                    opacity: 0,
                    width: '100%',
                    height: $(document).height()
                });
                config.overlay.click(that.close);
                $('body').prepend(config.overlay);
            }
            created = true;
        }

        if (config.overlay) {
            config.overlay.show();
            config.overlay.animate({
                opacity: 0.8
            }, 400);
        }

        // Hide the lightbox by setting a position far outside the window.
        // We can't hide it it with display: none because then it's height 
        // won't be available
        config.element.css({
            left: '-1000px',
            width: config.width
        });

        // Link hasn't changed, just show the same content again
        if (link.attr('href') == currentHref && !frameChanged) {
            that.show();
        }

        // Load src of the clicked link in the iframe and show the lightbox once the content is loaded
        else {
            iframe.attr('src', link.attr('href'));

            // Postpone showing the lightbox until the frame content is fully loaded
            frameTimeout = setInterval(function() {
                frameReadyLoad(that.show);
            }, 20);
        }
    }

    // runs every time a new document is loaded in the iframe
    var frameChange = function() {
        iframe.contents().find('.cancel').click(function() {
            that.close();
            return false;
        });

        iframe.contents().find('a').click(function() {
            frameChanged = true;
        });
    }

    // Shows the lightbox once it has content
    this.show = function() {
        currentHref = link.attr('href');

        // Hide selects in IE6 when the lightbox is displayed
        $('body').addClass('hideSelects');

        // Get the height of the lightbox. We will grow the lightbox to this height
        config.height = iframe.contents().find('#lightbox-content, #popup-content').height();

        // Grows the lightbox out from the clicked link
        if (config.transition == 'grow') {
            // Set the starting position of the lightbox to the position of the link
            // that opened the lightbox, it will grow outwards from there
            config.element.css({
                top: link.offset().top,
                left: link.offset().left,
                width: '1px',
                height: '1px'
            });

            // Make the lightbox grow starting from the position of the clicked link 
            // ending up on the center of the screen
            config.element.animate({
                width: config.width + 'px',
                height: config.height + 'px',
                left: ($(document).width() / 2) - (config.width / 2) + 'px',
                top: 130 + $(document).scrollTop() + 'px'
            }, 300, function() {
                // Once the animation is finished reset height to auto to allow the 
                // height to grow or shrink with new content added to the lightbox later
                // (for instance after submitting a form)
                iframe.height(config.height);
                config.element.css({
                    height: 'auto'
                });
            });
        }

        // Show the lightbox centered to the right side of the link
        else if (config.transition == 'linkRight') {
            iframe.height(config.height);
            config.element.css({
                width: config.width + 'px',
                height: 'auto',
                left: link.offset().left + link.outerWidth() + config.offsetX + 'px',
                top: link.offset().top + (link.outerHeight() / 2) - (config.element.outerHeight() / 2) + config.offsetY + 'px'
            });
        }

        // Show the lightbox centered over the link
        else if (config.transition == 'linkTop') {
            iframe.height(config.height);
            config.element.css({
                width: config.width + 'px',
                height: 'auto',
                left: link.offset().left - (config.element.outerWidth() / 2) + (link.outerWidth() / 2) + config.offsetX + 'px',
                top: link.offset().top - config.element.height() - link.height() + config.offsetY + 'px'
            });
        }

        // Just show a centered lightbox without animation
        else {
            iframe.height(config.height);
            config.element.css({
                width: config.width + 'px',
                height: 'auto',
                left: link.offset().left + 'px',
                top: link.offset().top - config.element.height() - 50 + 'px'
            });
        }
        visible = true;
    }

    // Close the lightbox
    this.close = function() {
        if (config.overlay) {
            config.overlay.css({ 'opacity': 0 });
            config.overlay.hide();
        }

        if (timeout) {
            clearTimeout(timeout);
        }

        if (frameTimeout) {
            clearTimeout(frameTimeout);
        }

        if (delayTimeout) {
            clearTimeout(delayTimeout);
        }

        config.element.css({
            left: '-1000px'
        });

        // Show selects in IE6 when the lightbox closes
        $('body').removeClass('hideSelects');
        visible = false;
    }
}
