/**
 * This file is part of jQuery Lightbox
 * Copyright (C) 2007-2010 Benjamin Arthur Lupton
 * http://www.balupton.com/projects/jquery-lightbox
 *
 * jQuery Lightbox is free software; You can redistribute it and/or modify it under the terms of
 * the GNU Affero General Public License version 3 as published by the Free Software Foundation.
 * You don't have to do anything special to accept the license and you don’t have to notify
 * anyone which that you have made that decision.
 * 
 * jQuery Lightbox 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 your chosen license for more details.
 * 
 * You should have received along with jQuery Lightbox:
 * - A copy of the license used.
 *   If not, see <http://www.gnu.org/licenses/agpl-3.0.html>.
 * - A copy of our interpretation of the license used.
 *   If not, see <http://github.com/balupton/jquery-lightbox/blob/master/COPYING.txt>.
 * 
 * @version 1.4.6-final
 * @date July 28, 2010
 * @since v0.1.0-dev, December 3, 2007
 * @category jquery-plugin
 * @package jquery-lightbox {@link http://www.balupton/projects/jquery-lightbox}
 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
 * @copyright (c) 2007-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
 * @example Visit {@link http://www.balupton.com/projects/jquery-lightbox} 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
	
	/**
	 * Console Emulator
	 * We have to convert arguments into arrays, and do this explicitly as webkit (chrome) hates function references, and arguments cannot be passed as is
	 * @version 1.0.2
	 * @date August 21, 2010
	 * @since 0.1.0-dev, December 01, 2009
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	if ( typeof window.console !== 'object' || typeof window.console.emulated === 'undefined' ) {
		// Check to see if console exists
		if ( typeof window.console !== 'object' || !(typeof window.console.log === 'function' || typeof window.console.log === 'object') ) {
			// Console does not exist
			window.console = {};
			window.console.log = window.console.debug = window.console.warn = window.console.trace = function(){};
			window.console.error = function(){
				var msg = "An error has occured. More information will be available in the console log.";
				for ( var i = 0; i < arguments.length; ++i ) {
					if ( typeof arguments[i] !== 'string' ) { break; }
					msg += "\n"+arguments[i];
				}
				if ( typeof Error !== 'undefined' ) {
					throw new Error(msg);
				}
				else {
					throw(msg);
				}
			};
		}
		else {
			// Console is object, and log does exist
			// Check Debug
			if ( typeof window.console.debug === 'undefined' ) {
				window.console.debug = function(){
					var arr = ['console.debug:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
				    window.console.log.apply(window.console, arr);
				};
			}
			// Check Warn
			if ( typeof window.console.warn === 'undefined' ) {
				window.console.warn = function(){
					var arr = ['console.warn:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
				    window.console.log.apply(window.console, arr);
				};
			} 
			// Check Error
			if ( typeof window.console.error === 'undefined' ) {
				window.console.error = function(){
					var arr = ['console.error']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
				    window.console.log.apply(window.console, arr);
				};
			}
			// Check Trace
			if ( typeof window.console.trace === 'undefined' ) {
				window.console.trace = function(){
				    window.console.error.apply(window.console, ['console.trace does not exist']);
				};
			}
		}
		// We have been emulated
		window.console.emulated = true;
	}
	
	/**
	 * Return a new JSON object of the old string.
	 * Turns:
	 * 		file.js?a=1&amp;b.c=3.0&b.d=four&a_false_value=false&a_null_value=null
	 * Into:
	 * 		{"a":1,"b":{"c":3,"d":"four"},"a_false_value":false,"a_null_value":null}
	 * @version 1.1.0
	 * @date July 16, 2010
	 * @since 1.0.0, June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	String.prototype.queryStringToJSON = String.prototype.queryStringToJSON || function ( )
	{	// Turns a params string or url into an array of params
		// Prepare
		var params = String(this);
		// 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
			// window.console.log({'key':key,'value':value}, split);
			var keys = key.split('.');
			if ( keys.length === 1 )
			{	// Simple
				json[key] = value;
			}
			else
			{	// Advanced (Recreating an object)
				var path = '',
					cmd = '';
				// Ensure Path Exists
				$.each(keys,function(ii,key){
					path += '["'+key.replace(/"/g,'\\"')+'"]';
					jsonCLOSUREGLOBAL = json; // we have made this a global as closure compiler struggles with evals
					cmd = 'if ( typeof jsonCLOSUREGLOBAL'+path+' === "undefined" ) jsonCLOSUREGLOBAL'+path+' = {}';
					eval(cmd);
					json = jsonCLOSUREGLOBAL;
					delete jsonCLOSUREGLOBAL;
				});
				// Apply Value
				jsonCLOSUREGLOBAL = json; // we have made this a global as closure compiler struggles with evals
				valueCLOSUREGLOBAL = value; // we have made this a global as closure compiler struggles with evals
				cmd = 'jsonCLOSUREGLOBAL'+path+' = valueCLOSUREGLOBAL';
				eval(cmd);
				json = jsonCLOSUREGLOBAL;
				delete jsonCLOSUREGLOBAL;
				delete valueCLOSUREGLOBAL;
			}
			// ^ We now have the parts added to your JSON object
		}
		return json;
	};

	/**
	 * Remove a element, or a set of elements from an array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @copyright John Resig
	 * @license MIT License - {@link http://opensource.org/licenses/mit-license.php}
	 */
	Array.prototype.remove = function(from, to) {
		var rest = this.slice((to || from) + 1 || this.length);
		this.length = from < 0 ? this.length + from : from;
		return this.push.apply(this, rest);
	};

	/**
	 * Get a element from an array at [index]
	 * if [current] is set, then set this index as the current index (we don't care if it doesn't exist)
	 * @version 1.0.1
	 * @date July 09, 2010
	 * @since 1.0.0 June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.get = function(index, current) {
		// Determine
		if ( index === 'first' ) {
			index = 0;
		} else if ( index === 'last' ) {
			index = this.length-1;
		} else if ( index === 'prev' ) {
			index = this.index-1;
		} else if ( index === 'next' ) {
			index = this.index+1;
		} else if ( !index && index !== 0 ) {
			index = this.index;
		}
	
		// Set current?
		if ( current||false !== false ) {
			this.setIndex(index);
		}
	
		// Return
		return this.exists(index) ? this[index] : undefined;
	};

	/**
	 * Apply the function [fn] to each element in the array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.each = function(fn){
		for (var i = 0; i < this.length; ++i) {
			if (fn(i, this[i], this) === false) 
				break;
		}
		return this;
	}

	/**
	 * Checks whether the index is a valid index
	 * @version 1.0.0
	 * @date July 09, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.validIndex = function(index){
		return index >= 0 && index < this.length;
	};

	/**
	 * Set the current index of the array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.setIndex = function(index){
		if ( this.validIndex(index) ) {
			this.index = index;
		} else {
			this.index = null;
		}
		return this;
	};

	/**
	 * Get the current index of the array
	 * If [index] is passed then set that as the current, and return it's value
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.current = function(index){
		return this.get(index, true);
	};

	/**
	 * Get whether or not the array is empty
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.isEmpty = function(){
		return this.length === 0;
	};

	/**
	 * Get whether or not the array has only one item
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.isSingle = function(){
		return this.length === 1;
	};

	/**
	 * Get whether or not the array is not empty
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.isNotEmpty = function(){
		return this.length !== 0;
	};

	/**
	 * Get whether or not the array has more than one item
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.isNotEmpty = function(){
		return this.length > 1;
	};

	/**
	 * Get whether or not the current index is the last one
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.isLast = function(index){
		index = typeof index === 'undefined' ? this.index : index;
		return !this.isEmpty() && index === this.length-1;
	}

	/**
	 * Get whether or not the current index is the first one
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.isFirst = function(index){
		index = typeof index === 'undefined' ? this.index : index;
		return !this.isEmpty() && index === 0;
	}

	/**
	 * Clear the array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.clear = function(){
		this.length = 0;
	};

	/**
	 * Set the index as the next one, and get the item
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.next = function(update){
		return this.get(this.index+1, update);
	};

	/**
	 * Set the index as the previous one, and get the item
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.prev = function(update){
		return this.get(this.index-1, update);
	};

	/**
	 * Reset the index
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.reset = function(){
		this.index = null;
		return this;
	};

	/**
	 * Set the [index] to the [item]
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.set = function(index, item){
		// We want to set the item
		if ( index < this.length && index >= 0 ) {
			this[index] = item;
		} else {
			throw new Error('Array.prototype.set: [index] above this.length');
			// return false;
		}
		return this;
	};

	/**
	 * Set the index as the next item, and return it.
	 * If we reach the end, then start back at the beginning.
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.loop = function(){
		if ( !this.index && this.index !== 0 ) {
			// index is not a valid value
			return this.current(0);
		}
		return this.next();
	};

	/**
	 * Add the [arguments] to the array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.add = function(){
		this.push.apply(this,arguments);
		return this;
	};

	/**
	 * Insert the [item] at the [index] or at the end of the array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.insert = function(index, item){
		if ( typeof index !== 'number' ) {
			index = this.length;
		}
		index = index<=this.length ? index : this.length;
		var rest = this.slice(index);
		this.length = index;
		this.push(item);
		this.push.apply(this, rest);
		return this;
	};

	/**
	 * Get whether or not the index exists in the array
	 * @version 1.0.0
	 * @date July 09, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.exists = Array.prototype.exists || function(index){
		return typeof this[index] !== 'undefined';
	};

	/**
	 * Get whether or not the value exists in the array
	 * @version 1.0.0
	 * @date June 30, 2010
	 * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	Array.prototype.has = Array.prototype.has || function(value){
		var has = false;
		for ( var i=0, n=this.length; i<n; ++i ) {
			if ( value == this[i] ) {
				has = true;
				break;
			}
		}
		return has;
	};

	/**
	 * Bind a event only once
	 * @version 1.0.0
	 * @date June 30, 2010
     * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	$.fn.once = $.fn.once || function(event, data, callback){
		// Only apply a event handler once
		var $this = $(this);
		// Handle
		if ( (callback||false) ) {
			$this.unbind(event, callback);
			$this.bind(event, data, callback);
		} else {
			callback = data;
			$this.unbind(event, callback);
			$this.bind(event, callback);
		}
		// Chain
		return $this;
	};
	
	/**
	 * Bind a event, with or without data
	 * Benefit over $.bind, is that $.binder(event, callback, false|{}|''|false) works.
	 * @version 1.0.0
	 * @date June 30, 2010
     * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	$.fn.binder = $.fn.binder || function(event, data, callback){
		// Help us bind events properly
		var $this = $(this);
		// Handle
		if ( (callback||false) ) {
			$this.bind(event, data, callback);
		} else {
			callback = data;
			$this.bind(event, callback);
		}
		// Chain
		return $this;
	};
	
	/**
	 * Event for the last click for a series of one or more clicks
	 * @version 1.0.0
	 * @date July 16, 2010
     * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	$.fn.lastclick = $.fn.lastclick || function(data,callback){
		return $(this).binder('lastclick',data,callback);
	};
	$.event.special.lastclick = $.event.special.lastclick || {
		setup: function( data, namespaces ) {
			$(this).bind('click', $.event.special.lastclick.handler);
		},
		teardown: function( namespaces ) {
			$(this).unbind('click', $.event.special.lastclick.handler);
		},
		handler: function( event ) {
			// Setup
			var clear = function(){
				// Fetch
				var Me = this;
				var $el = $(Me);
				// Fetch Timeout
				var timeout = $el.data('lastclick-timeout')||false;
				// Clear Timeout
				if ( timeout ) {
					clearTimeout(timeout);
				}
				timeout = false;
				// Store Timeout
				$el.data('lastclick-timeout',timeout);
			};
			var check = function(event){
				// Fetch
				var Me = this;
				clear.call(Me);
				var $el = $(Me);
				// Store the amount of times we have been clicked
				$el.data('lastclick-clicks', ($el.data('lastclick-clicks')||0)+1);
				// Handle Timeout for when All Clicks are Completed
				var timeout = setTimeout(function(){
					// Fetch Clicks Count
					var clicks = $el.data('lastclick-clicks');
					// Clear Timeout
					clear.apply(Me,[event]);
					// Reset Click Count
					$el.data('lastclick-clicks',0);
					// Fire Event
					event.type = 'lastclick';
					$.event.handle.apply(Me, [event,clicks])
				},300);
				// Store Timeout
				$el.data('lastclick-timeout',timeout);
			};
			// Fire
			check.apply(this,[event]);
		}
	};
	
	/**
	 * Prevent the default action when a click is performed
	 * @version 1.0.0
	 * @date August 19, 2010
	 * @since 1.0.0, August 19, 2010
     * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
	 */
	$.fn.preventDefault = $.fn.preventDefault || function(){
		return $(this).click(function(event){
			event.preventDefault();
			return false;
		});
	};
	

	// 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
		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.preventDefault().once('lastclick',function(event){
				// Get obj
				var $obj = $(this);
				// Get position
				var index = $group.index($obj);
				// Init group
				if ( !$.Lightbox.init(index, $group) )
				{	return false;	}
				// Display lightbox
				if ( !$.Lightbox.start() )
				{	return false;	}
				// Cancel href
				event.preventDefault();
				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(0, $group) )
			{	return this;	}
			// Display lightbox
			if ( !$.Lightbox.start() )
			{	return this;	}
		}
		
		// And chain
		return this;
	};
	
	// Define our class
	$.extend($.LightboxClass.prototype,
	{	// Our LightboxClass definition
		
		// -----------------
		// Everything to do with images
		
		images: [],
		
		// -----------------
		// Variables
		
		constructed:		false,
		compressed:			null,
		
		// -----------------
		// Options
		
		src:				null,		// the source location of our js file
		baseurl:			null,
		
		files: {
			compressed: {
				scripts: {
					lightbox:	'scripts/jquery.lightbox.min.js',
					colorBlend:	'scripts/jquery.color.min.js'
				},
				styles: {
					lightbox:	'styles/jquery.lightbox.min.css'
				}
			},
			uncompressed: {
				scripts: {
					lightbox:	'scripts/jquery.lightbox.js',
					colorBlend:	'scripts/jquery.color.js'
				},
				styles: {
					lightbox:	'styles/jquery.lightbox.css'
				}
			},
			images: {
				prev:		'images/prev.gif',
				next:		'images/next.gif',
				blank:		'images/blank.gif',
				loading:	'images/loading.gif'
			}
		},
		
		text: {
			// For translating
			image:		'Image',
			of:			'of',
			close:		'',
			closeInfo:	'close',
			download:	'Download.',
			help: {
				close:		'',
				interact:	'Hover to interact'
			},
			about: {
				text: 	'',
				title:	'',
				link:	''
			}
		},
		
		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
		
		colorBlend:		null,		// null - auto-detect, true - force, false - no
		
		download_link:		true,	// Display the download link
		
		show_helper_text:	true,	// Display the helper text up the top right
		show_linkback:		true,	// true, false
		show_info:			'auto',	// auto - automaticly handle, true - force
		show_extended_info:	'auto',	// 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', '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.scripts.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.scripts.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
					window.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.scripts.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, this.src.queryStringToJSON());
				}
				
				// Create
				this.images.image = {
					src:	'',
					title:	'',
					description:	'',
					name:	'',
					color:	null,
					width:	null,
					height:	null,
					id: 	null,
					image:	true
				};
				this.images.prepare = function(obj){
					var image = $.extend({}, this.image);
					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 {
							image = null;
						}
					} else if ( obj.src ) {
						// We are a image object
						image = $.extend(this.image,obj);
					} else {
						image = null;
					}
					if ( image ) {
						image.id = image.id || image.src+image.title+image.description;
					}
					return image;
				}
				this.images.create = function(obj){
					var images = this;
					if ( obj.each ) {
						obj.each(function(index,item){
							images.create(item);
						});
						return;
					}
					
					var image = images.prepare(obj);
					
					if ( !image ) {
						window.console.error('We dont know what we have:', obj, image);
					} else {
						images.push(image);
					}
					
					return images;
				};
			}
			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.styles;
			var scripts = this.files.scripts;
			
			// 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
			delete scripts.lightbox; // prevent us from including ourself
			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().preventDefault().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' });
			}).lastclick(function() {
				$.Lightbox.showImage('prev');
				return false;
			});
					
			// Next
			$('#lightbox-nav-btnNext').unbind().preventDefault().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' });
			}).lastclick(function() {
				$.Lightbox.showImage('next');
				return false;
			});
			
			// Help
			if ( this.show_linkback )
			{	// Linkback exists so add handler
				$('#lightbox-overlay-text-about a').preventDefault().lastclick(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
			if ( this.download_link ) {
				$('#lightbox-caption-title').preventDefault().lastclick(function(){window.open($(this).attr('href')); return false;});
			}
			
			// Assign close clicks
			$('#lightbox-overlay, #lightbox, #lightbox-loading-link, #lightbox-btnClose').unbind().preventDefault().lastclick(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
			this.images.create(images);
			
			// Do we need to bother
			if ( this.images.isEmpty() ) {
				// No images
				window.console.warn('WARNING', 'Lightbox started, but no images: ', image, images);
				return false;
			}
			
			// Set current
			if ( !this.images.current(image) ) {
				// No images
				window.console.warn('WARNING', 'Could not find current image: ', image, this.images);
				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.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 current image
			this.images.reset();
			
			// 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.current();
			if ( !image || !image.width || !this.visible )
			{	// No image or no visible lightbox, so we don't care
				//window.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 ){
			// Default step
			step = step || 1;
			
			// Make the image the current image, or get the current
			image = this.images.current(image) || this.images.get('first',true);
			if ( !image ) {
				return;
			}
			
			// 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!
					
					// 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 || '');
					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.length > 1 )
					{	// Display
						$('#lightbox-currentNumber').html(this.text.image + '&nbsp;' + ( this.images.index + 1 ) + '&nbsp;' + this.text.of + '&nbsp;' + this.images.length);
					} 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.isFirst() ) {
						// Not first, show button
						$('#lightbox-nav-btnPrev').show();
					}
					
					// If not last, show next button
					if ( !this.images.isLast() ) {
						// Not first, show button
						$('#lightbox-nav-btnNext').show();
					}
					
					// Make navigation current / show it
					$('#lightbox-nav').show();
					
					// Enable keyboard navigation
					this.KeyboardNav_Enable();
					
					// Done
					break;
					
					
				// ---------------------------------
				// Error handling
				default:
					window.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.isSingle() || this.images.isEmpty() )
			{	return true;	}
			
			// Get current image
			var image = this.images.current();
			var index = this.images.index;
			if ( !image ) { return image; }
			var objNext;
			
			// Load previous
			var prev = this.images.prev();
			if ( prev ) {
				objNext = new Image();
				objNext.src = prev.src;
			}
			this.images.setIndex(index); // reset
			
			// Load next
			var next = this.images.next();
			if ( next ) {
				objNext = new Image();
				objNext.src = next.src;
			}
			this.images.setIndex(index); // reset
		},
		
		// --------------------------------------------------
		// 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('prev');
			}
			
			// Next?
			if ( key === this.keys.next || keycode === 39 )
			{	// We want next
				return $.Lightbox.showImage('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

