var Fluxbox = function() {

	// === private data ================================================================================

	var oNode = {
				oFluxbox: { sID: '#fluxbox', fnClick: ButtonClose, bRequired: true },
				oOverlay: { sID: '#fluxbox-overlay', bRequired: true },
				oMain: { sID: '#fluxbox-main', bRequired: true },
				oMainInner: { sID: '#fluxbox-main-inner', fnClick: ButtonPause, bRequired: true },
				oDisplay: { sID: '#fluxbox-display', bRequired: true },
				oInterface: { sID: '#fluxbox-interface', bRequired: true },
				oTitle: { sID: '#fluxbox-title' },
				oCaption: { sID: '#fluxbox-caption' },
				oCounter: { sID: '#fluxbox-counter' },
				oButtonPrevious: { sID: '#fluxbox-button-previous', fnClick: ButtonPrevious },
				oButtonPlay: { sID: '#fluxbox-button-play', fnClick: ButtonPlay },
				oButtonPause: { sID: '#fluxbox-button-pause', fnClick: ButtonPause },
				oButtonNext: { sID: '#fluxbox-button-next', fnClick: ButtonNext },
				oButtonClose: { sID: '#fluxbox-button-close', fnClick: ButtonClose }
			},
			oKey = {
				'27': ButtonClose, // ESC
				'32': function() { return bSlideshow ? ButtonPause() : ButtonPlay() }, // SPACE, toggle play/pause
				'33': ButtonFirst, // PgUp
				'34': ButtonLast, // PgDn
				'35': ButtonLast, // End
				'36': ButtonFirst, // Home
				'37': ButtonPrevious, // Left arrow
				'38': ButtonPrevious, // Up arrow
				'39': ButtonNext, // Right arrow
				'40': ButtonNext, // Down arrow
				'67': ButtonClose, // C key
				'78': ButtonNext, // N key
				'79': ButtonClose, // O key
				'80': ButtonPrevious, // P key
				'81': ButtonClose, // Q key
				'88': ButtonClose // X key
			},
			// dimensions
			oSizeChromeOuter,
			oSizeChromeInner,
			oSizeContent,
			oSizeAvailable,
			oSizeInitial = { iWidth: 200, iHeight: 150 },
			oSizeDisplay,
			oSizeInterface,
			
			rButtonTitle = /\{(\w+)\}/g,
			rExtension = /\.(\w*)(?:[?#*!]|$)/,
			rGallery = /^fluxbox\[([^\]]+)\]/i,
			rParameter = /(\w+)\s*=\s*(\w+)/g,
			rAlphaFilter = /[\w\.]*alpha\(.*?\);?/i,
			bActivated = false,
			bAnimating = false,
			iResizeDuration = 500,
			iFadeDuration = 350,
			fOverlayOpacity,
			bDisableInterface = false,
			
			oHandlers = {},
			aoLinkCache = [],
			aoGallery,
			iCurrent,
			oCurrentDisplayItem,
			bSlideshow = false,
			iSlideshowTimer,
			iResizeTimer,
			sInactiveClass = 'fluxbox-inactive',
			sLoadingClass = 'fluxbox-loading';
	
// === private functions ================================================================================

// display handling / animation segments
		
	function AnimationHideContent() {
		bAnimating = true;
		oNode.oDisplay.css({ opacity: 0 });
		oNode.oInterface.css({ opacity: 0 });
		oNode.oMain.addClass(sLoadingClass);
		if (oCurrentDisplayItem) {
			oCurrentDisplayItem.Remove();
		}
		AnimationPrepareContent();
	}

	function AnimationPrepareContent() {
		oCurrentDisplayItem = aoGallery[iCurrent];
		CalcSizeAvailable({ iWidth: 0, iHeight: 0 });
		oCurrentDisplayItem.Prepare(oSizeAvailable);
		// prepare adjacent items, to preload them
		// real dimensions aren't needed, since they won't be displayed without being prepared again
		if (aoGallery.length > 1) {
			// previous
			(aoGallery[iCurrent - 1] || aoGallery[aoGallery.length - 1]).Prepare(oSizeAvailable);
			// next
			(aoGallery[iCurrent + 1] || aoGallery[0]).Prepare(oSizeAvailable);
		}
		
		// update title
		oNode.oTitle.html(oCurrentDisplayItem.sTitle || '').toggle(!!oCurrentDisplayItem.sTitle);
		// update caption
		oNode.oCaption.html(oCurrentDisplayItem.sCaption || '').toggle(!!oCurrentDisplayItem.sCaption);
		// update counter
		oNode.oCounter.html((iCurrent + 1) + ' ' + Fluxbox.oLanguage['of'] + ' ' + aoGallery.length).toggle(!!aoGallery.length);
		// update buttons
		oNode.oButtonPrevious.toggleClass(sInactiveClass, !bHasPrevious());
		oNode.oButtonNext.toggleClass(sInactiveClass, !bHasNext());
		oNode.oButtonPlay.toggleClass(sInactiveClass, !(Fluxbox.oOptions.iSlideshowDelay && !bSlideshow));
		oNode.oButtonPause.toggleClass(sInactiveClass, !(Fluxbox.oOptions.iSlideshowDelay && bSlideshow));
		AnimationWaitForReady();
	}
	
	function AnimationWaitForReady() {
		if (oCurrentDisplayItem) {
			oCurrentDisplayItem.bReady ? AnimationResizeFluxbox() : setTimeout(AnimationWaitForReady, 50);
		}
	}
	
	function AnimationResizeFluxbox() {
		CalcSizeInterface();
		oNode.oMain.animate({ width: oSizeDisplay.iWidth + oSizeChromeInner.iWidth }, iResizeDuration);
		oNode.oDisplay.animate({ height: oSizeDisplay.iHeight }, iResizeDuration);
		oNode.oInterface.animate({ height: oSizeInterface.iHeight }, iResizeDuration, AnimationShowContent);
	}

	function AnimationShowContent() {
		oNode.oMain.removeClass(sLoadingClass);
		oCurrentDisplayItem.Append(oNode.oDisplay);
		oNode.oDisplay.animate({ opacity: 1 }, iFadeDuration);
		oNode.oInterface.animate({ opacity: 1 }, iFadeDuration, AnimationOnNewItem);
	}

	function AnimationOnNewItem() {
		ClearOpacity(oNode.oDisplay);
		ClearOpacity(oNode.oInterface);
		// clear the animating flag before using a button or it will fail
		bAnimating = false;
		if (bSlideshow) {
			ButtonPlay();
		}
	}
	
	function ResizeFluxbox() {
 		oNode.oMain.css({ width: oSizeDisplay.iWidth + oSizeChromeInner.iWidth });
		oNode.oDisplay.css({ height: oSizeDisplay.iHeight });
		oNode.oInterface.css({ height: oSizeInterface.iHeight });
	}

	function ClearOpacity(jQueryElement) {
		if (jQueryElement.length) {
			var oStyle = jQueryElement[0].style;
			// clear opacity attribute
			oStyle.opacity = '';
			// clear custom mozilla attribute
			oStyle['-moz-opacity'] = '';
			// clear custom khtml attribute
			oStyle['-khtml-opacity'] = '';
			// if an alpha filter is in place, clear it
			if (typeof(oStyle.filter) == 'string') {
				oStyle.filter = oStyle.filter.replace(rAlphaFilter, '');
			}
		}
	}

	function IE6PositionFix() {
		oNode.oFluxbox.css({ left: document.documentElement.scrollLeft + 'px', top: document.documentElement.scrollTop + 'px' });
	}

	function CalcSizeContent() {
		oSizeContent = {
			iWidth: $(window).width() - oSizeChromeOuter.iWidth - oSizeChromeInner.iWidth,
			iHeight: $(window).height() - oSizeChromeOuter.iHeight - oSizeChromeInner.iHeight
		}
	}
	
	function CalcSizeAvailable(oAdjustSize) {
		oSizeAvailable = {
			iWidth: Math.max(Fluxbox.oOptions.iMinimumWidth, Math.min(oCurrentDisplayItem.iMaxWidth, oSizeContent.iWidth - oAdjustSize.iWidth)),
			iHeight: Math.max(Fluxbox.oOptions.iMinimumHeight, Math.min(oCurrentDisplayItem.iMaxHeight, oSizeContent.iHeight - oAdjustSize.iHeight))
		}
	}

	function CalcSizeDisplay() {
		oSizeDisplay = {
			iWidth: Math.max(Fluxbox.oOptions.iMinimumWidth, oCurrentDisplayItem.iWidth),
			iHeight: Math.max(Fluxbox.oOptions.iMinimumHeight, oCurrentDisplayItem.iHeight)
		}
	}
	
	function CalcSizeInterface() {	
		CalcSizeDisplay();
		if (bDisableInterface) {
			oSizeInterface = { iWidth: 0, iHeight: 0 };
		}
		else {
			oNode.oInterface.css({ position: 'absolute', left: -5000, top: -5000, width: oSizeDisplay.iWidth, height: 'auto', overflow: 'visible' });
			var oOldInterface = oSizeInterface;
			oSizeInterface = { iWidth: 0, iHeight: oNode.oInterface.height() };
			while (oSizeDisplay.iHeight > Fluxbox.oOptions.iMinimumHeight && oSizeDisplay.iHeight + oSizeInterface.iHeight > oSizeContent.iHeight) {
				CalcSizeAvailable(oSizeInterface);
				oCurrentDisplayItem.Prepare(oSizeAvailable);
				CalcSizeDisplay();
				oNode.oInterface.css({ width: oSizeDisplay.iWidth });
				oSizeInterface = { iWidth: 0, iHeight: oNode.oInterface.height() };
			}
			// restore Interface to previous state
			oNode.oInterface.css({ position: 'static', left: '', top: '', width: 'auto', height: oOldInterface.iHeight, overflow: 'hidden' });
		}
	}

// event handlers

	function KeyHandler(oEvent) {
		// look up the keycode's handler function, unless Ctrl or Alt were pressed
		// if Ctrl or Alt were pressed, it will look up a handler for 'true' and fail to find one
		var fnKeyHandler = oKey[String(oEvent.ctrlKey || oEvent.altKey || oEvent.keyCode)];
		// if no entry in the key list, returns null (which doesn't cancel the event)
		// returns the function's value otherwise (always false, which cancels the event)
		return fnKeyHandler && fnKeyHandler();
	}
	
	function ResizeHandler(oEvent) {
		// delay resizing to reduce flicker
		if (iResizeTimer) {
			clearTimeout(iResizeTimer);
		}
		iResizeTimer = setTimeout(function() {
			// update dimensions to reflect the new screen size
			CalcSizeContent();
			CalcSizeInterface();
			// tell the current link item to resize itself
			CalcSizeAvailable(oSizeInterface);
			oCurrentDisplayItem.Resize(oSizeAvailable);
			CalcSizeDisplay();
			// adjust the display box
			ResizeFluxbox();
		}, 50);
	}

	function ButtonOpen(oEvent) {
		if (!bActivated) {
			var oDisplayItem = aoLinkCache[this.iFluxboxCacheKey];
			if (oDisplayItem) {
				bActivated = true;
				// build the gallery
				iCurrent = 0;
				if (oDisplayItem.sGallery) {
					aoGallery = [];
					for (var i = 0; i < aoLinkCache.length; ++i) {
						if (aoLinkCache[i].sGallery == oDisplayItem.sGallery) {
							if (aoLinkCache[i] == oDisplayItem) {
								iCurrent = aoGallery.length;
							}
							aoGallery[aoGallery.length] = aoLinkCache[i];
						}
					}
				}
				else {
					aoGallery = [oDisplayItem];
				}
				// if there is only a single entry in the gallery, and it has no labels,
				// hide the interface panel for a nicer look
				bDisableInterface = aoGallery.length == 1 && !oDisplayItem.sTitle && !oDisplayItem.sCaption;
				// read overlay opacity from the stylesheet
				// done at this point rather than at initialisation to avoid problems with
				// reading the value imediately after inserting the HTML
				// the reading method is a workaround for a bug in jQuery 1.3.2 which returns 1 for IE opacity
				if (!fOverlayOpacity) {
					fOverlayOpacity = parseFloat(oNode.oOverlay[0].currentStyle ? oNode.oOverlay[0].currentStyle.opacity : oNode.oOverlay.css('opacity'));
				}
				// fix for IE6, to keep fluxbox at the top of the viewport
				if (oNode.oFluxbox.css('position') == 'absolute') {
					IE6PositionFix();
					$(window).scroll(IE6PositionFix);
				}
				// hide elements which may conflict with the overlay
				$('select, object, embed').css({ visibility: 'hidden' });
				// set fluxbox visible but transparent
				oNode.oFluxbox.css({ opacity: 0, visibility: 'visible' });
				// ensure fluxbox-main is visible during calculations
				oNode.oMain.show();
				// ensure fluxbox-interface has no height during calculations
				oNode.oInterface.css({ height: 0, overflow: 'hidden' });
				oSizeInterface = { iWidth: 0, iHeight: 0 };
				// calc the available display area
				oSizeChromeOuter = {
					iWidth: oNode.oMain.outerWidth() - oNode.oMain.width(),
					iHeight: oNode.oMain.outerHeight() - oNode.oMain.height()
				};
				oSizeChromeInner = {
					iWidth: oNode.oMain.width() - oNode.oDisplay.width(),
					iHeight: oNode.oMain.height() - oNode.oDisplay.height()
				};
				CalcSizeContent();
				oSizeDisplay = oSizeInitial;
				ResizeFluxbox();
				// set the overlay opacity to 0 before fade in
				oNode.oOverlay.css({ opacity: 0 });
				// ensure fluxbox main is hidden
				oNode.oMain.hide();
				// make fluxbox opaque again
				ClearOpacity(oNode.oFluxbox);
				// fade in the overlay
				oNode.oOverlay.animate({ opacity: fOverlayOpacity }, iFadeDuration, function() {
					// show fluxbox-main
					oNode.oMain.show();
					// start managing resize
					$(window).resize(ResizeHandler)
					// start managing keys
					$(document).keydown(KeyHandler)
					// start the content transition chain
					AnimationHideContent();
				});
			}
		}
		return false;
	}

	function ButtonFirst(oEvent) {
		if (!(oEvent && $(this).hasClass('inactive')) && !bAnimating) {
			SetCurrent(0);
		}
		return false;
	}

	function ButtonPrevious(oEvent) {
		if (!(oEvent && $(this).hasClass('inactive')) && !bAnimating) {
			SetCurrent(iCurrent - 1);
		}
		return false;
	}
	
	function ButtonNext(oEvent) {
		if (!(oEvent && $(this).hasClass('inactive')) && !bAnimating) {
			SetCurrent(iCurrent + 1);
		}
		return false;
	}
	
	function ButtonLast(oEvent) {
		if (!(oEvent && $(this).hasClass('inactive')) && !bAnimating) {
			SetCurrent(aoGallery.length - 1);
		}
		return false;
	}

	function ButtonPlay(oEvent) {
		if (!(oEvent && $(this).hasClass('inactive')) && !bAnimating && Fluxbox.oOptions.iSlideshowDelay) {
			if (bHasNext()) {
				if (iSlideshowTimer) {
					clearTimeout(iSlideshowTimer);
					iSlideshowTimer = null;
				}
				iSlideshowTimer = setTimeout(ButtonNext, Fluxbox.oOptions.iSlideshowDelay);
				oNode.oButtonPlay.toggleClass('fluxbox-inactive', true);
				oNode.oButtonPause.toggleClass('fluxbox-inactive', false);
				bSlideshow = true;
			}
			// turn off if there's nothing more to see
			else {
				ButtonPause();
			}
		}
		return false;
	}
	
	function ButtonPause(oEvent) {
		if (!(oEvent && $(this).hasClass('inactive')) && !bAnimating && Fluxbox.oOptions.iSlideshowDelay) {
			bSlideshow = false;
			oNode.oButtonPlay.toggleClass('fluxbox-inactive', false);
			oNode.oButtonPause.toggleClass('fluxbox-inactive', true);
			if (iSlideshowTimer) {
				clearTimeout(iSlideshowTimer);
			}
		}
		return false;
	}
	
	function ButtonClose(oEvent) {
		if (bActivated) {
			// stop managing keys
			$(document).unbind('keydown', KeyHandler)
			// stop managing resize
			$(window).unbind('resize', ResizeHandler)
			// cancel all animations in progress (clear queue: true, complete anims: false)
			$('#fluxbox div').stop(true, false);
			bAnimating = false;
			// hide fluxbox-main
			oNode.oMain.hide();
			// remove the content
			if (oCurrentDisplayItem) {
				oCurrentDisplayItem.Remove();
				oCurrentDisplayItem = null;
			}
			// reset the slideshow
			bSlideshow = false;
			// fade out the overlay
			oNode.oOverlay.animate({ opacity: 0 }, iFadeDuration, function() {
				// once fully faded, make fluxbox hidden
				oNode.oFluxbox.css({ visibility: 'hidden' });
				// remove IE6 fix
				if (oNode.oFluxbox.css('position') == 'absolute') {
					$(window).unbind('scroll', IE6PositionFix);
				}
				// bring back hidden elements
				$('select, object, embed').css({ visibility: 'visible' });
				// flag as inactive
				bActivated = false;
			});
		}
		return false;
	}
	
// link / gallery handling

	function bHasPrevious() {
		return aoGallery.length > 1 && (iCurrent > 0 || Fluxbox.oOptions.bLoop);
	}

	function bHasNext() {
		return aoGallery.length > 1 && ((iCurrent < aoGallery.length - 1) || Fluxbox.oOptions.bLoop);
	}

	function SetCurrent(iIndex) {
		if (aoGallery) {
			if (iSlideshowTimer) {
				clearTimeout(iSlideshowTimer);
				iSlideshowTimer = null;
			}
			if (!aoGallery[iIndex]) {
				if (!Fluxbox.oOptions.bLoop) {
					return;
				}
				else {
					// loop finish to start
					iIndex = iIndex < 0 ? (aoGallery.length - 1) : 0;
				}
			}
			iCurrent = iIndex;
			// start the content transition sequence
			AnimationHideContent();
		}
	}
	
	function oBuildDisplayItem(oLink) {
		// create the content object for this item
		var asExtension = oLink.href.match(rExtension);
		var sHandler = (asExtension && asExtension[1]) ? oHandlers[asExtension[1]] || 'catchall' : 'catchall'
		var oDisplayItem = new Fluxbox.oPlugins[sHandler]();
		// copy the link details
		oDisplayItem.sURL = oLink.href;
		oDisplayItem.sTitle = oLink.getAttribute('title') || '';
		oDisplayItem.sCaption = $(oLink).nextAll('.caption').html() || '';
		// extract options from the rel attribute
		var sRel = oLink.getAttribute('rel');
		if (sRel) {
			// extract gallery name from fluxbox[name] format
			var asMatch = sRel.match(rGallery);
			if (asMatch) {
				oDisplayItem.sGallery = asMatch[1];
			}
			// extract other options
			for (var asMatch = rParameter.exec(sRel); asMatch; asMatch = rParameter.exec(sRel)) {
				if (asMatch[1] == 'width') {
					oDisplayItem.iMaxWidth = parseInt(asMatch[2]);
				}
				else if (asMatch[1] == 'height') {
					oDisplayItem.iMaxHeight = parseInt(asMatch[2]);
				}
			}
		}
		// set the maximum width and height
		oDisplayItem.iMaxWidth = isNaN(oDisplayItem.iMaxWidth) ? Number.MAX_VALUE : oDisplayItem.iMaxWidth;
		oDisplayItem.iMaxHeight = isNaN(oDisplayItem.iMaxHeight) ? Number.MAX_VALUE : oDisplayItem.iMaxHeight;
		return oDisplayItem;
	}

	// === public data ================================================================================
	
	var oPublic = {

		sHTML : 
			'<div id="fluxbox">' +
				'<div id="fluxbox-overlay"></div>' +
				'<div id="fluxbox-inner">' +
					'<div id="fluxbox-main"><div id="fluxbox-main-inner">' +
						'<div id="fluxbox-display"></div>' +
						'<div id="fluxbox-interface">' +
							'<div id="fluxbox-nav">' +
								'<div id="fluxbox-button-previous" title="{previous}"></div>' +
								'<div id="fluxbox-button-play" title="{play}"></div>' +
								'<div id="fluxbox-button-pause" title="{pause}"></div>' +
								'<div id="fluxbox-button-next" title="{next}"></div>' +
								'<div id="fluxbox-counter"></div>' +
							'</div>' +
							'<div id="fluxbox-title"></div>' +
							'<div id="fluxbox-caption"></div>' +
							'<div id="fluxbox-clear"></div>' +
						'</div>' +
						'<div id="fluxbox-button-close" title="{close}"></div>' +
					'</div></div>' +
				'</div>' +
			'</div>',
							
		oLanguage : {
			previous: 'Previous',
			next: 'Next',
			play: 'Play slideshow',
			pause: 'Pause slideshow',
			close: 'Close',
			of: 'of'
		},

		oPlugins: {},

		oOptions: {
			bLoop: false,
			iSlideshowDelay: 0,
			iMinimumWidth: 100,
			iMinimumHeight: 75
		},

	// === public functions ================================================================================

		OpenGallery: function(sGallery) {
			$('a[rel*="fluxbox[' + sGallery + ']"]:first').click();
		},
	
		AddExtensions: function(asExtensions, sHandler) {
			for (var i = 0 ; i < asExtensions.length; ++i) {
				oHandlers[asExtensions[i]] = sHandler;
			}
		},

		Initialise: function() {
			// add Fluxbox HTML to the document, with language conversion
			$(document.body).append(Fluxbox.sHTML.replace(rButtonTitle, function(sMatch, sTag) {
				return Fluxbox.oLanguage[sTag];
			}));
			// find and set up the collection of Fluxbox elements
			for (var x in oNode) {
				if (oNode.hasOwnProperty(x)) {
					var oProperties = oNode[x];
					oNode[x] = $(oProperties.sID);
					if (oProperties.bRequired && !oNode[x].length) {
						// if one of the required elements is missing, bail out
						return false;
					}
					// hook up a click handler if one exists
					if (oProperties.fnClick) {
						oNode[x].click(oProperties.fnClick);
					}
				}
			}
			
			// PNG fix for IE6
			if (oNode.oFluxbox.css('position') == 'absolute') {
				$('#fluxbox div').each(function() {
					var asMatch = $(this).css('backgroundImage').match(/url\("(.*\.png)"\)/);
					if (asMatch && $(this).css('backgroundColor') == 'transparent') {
						$(this).css({
							backgroundImage: 'none', 
							filter: 'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true,src=' + asMatch[1] + ',sizingMethod=crop);'
						});
					}
				});
			}
			// VML setup for IE
			if (document.namespaces) {
				document.createStyleSheet().cssText = ' fluxbox_vml\\:* {behavior:url(#default#VML)} fluxbox_vml\\:image {behavior:url(#default#VML);display:inline-block;}';
			}
			Fluxbox.InitialiseGalleries();
			return true;
		},
		
		InitialiseGalleries: function() {		
			// find and set up suitable A links
			aoLinkCache = [];
			$('a[rel^=fluxbox]').each(function() {
				// don't cache the DOM link object directly, to sidestep IE memory leaks
				this.iFluxboxCacheKey = aoLinkCache.length;
				aoLinkCache[this.iFluxboxCacheKey] = oBuildDisplayItem(this);
				// hook up click handler
				$(this).click(ButtonOpen);
			});
		}
		
	};
	
	return oPublic;
}();

// === IMAGE HANDLER ================================================================================

(function() {

	Fluxbox.AddExtensions(['gif', 'jpg', 'jpeg', 'png'], 'image');

	Fluxbox.oPlugins.image = function() {
		this.sContentID = 'fluxbox-display-item-image';
		this.bReady = false;
		this.oPreloader;
	};
	
	function sBuildContent(oItem) {
		return '<canvas id="' + oItem.sContentID + '-canvas" width="' + oItem.iWidth + '" height="' + oItem.iHeight + '" style="position: absolute;"></canvas>' +
			'<fluxbox_vml:image src="' + oItem.oPreloader.src + '" style="width:' + oItem.iWidth + 'px; height:' + oItem.iHeight + 'px;"></fluxbox_vml:image>';
	}

	Fluxbox.oPlugins.image.prototype = {

		Prepare: function(oAvailable) {
			var that = this;
			if (this.bReady) {
				Update();
			}
			else if (!this.oPreloader) {

				if ($.browser.msie) {
					this.oVMLPreloader = document.createElement('fluxbox_vml:image');
					this.oVMLPreloader.src = this.sURL;
					$(this.oVMLPreloader).css({ display: 'none' });
					$('#fluxbox').append(this.oVMLPreloader);
					setTimeout(function() {
						$(that.oVMLPreloader).remove();
						that.oVMLPreloader = null;
					}, 50);
				}

				this.oPreloader = new Image();
				this.oPreloader.onload = function() {
					that.iDesiredWidth = that.oPreloader.width;
					that.iDesiredHeight = that.oPreloader.height;
					Update();
					that.bReady = true;
					that.oPreloader.onload = null;
				};
				this.oPreloader.src = this.sURL;
			}
			function Update() {
				var fScale = Math.min(1.0, Math.min(oAvailable.iWidth / that.iDesiredWidth, oAvailable.iHeight / that.iDesiredHeight));
				that.iWidth = parseInt(Math.round(that.iDesiredWidth * fScale));
				that.iHeight = parseInt(Math.round(that.iDesiredHeight * fScale));
			}
		},

		Append: function(oParent)	{
			var that = this;
			oParent.append('<div id="'+this.sContentID+'" style="width:'+this.iWidth+'px; height:'+this.iHeight+'px; overflow:hidden;"></div>');
			$('#' + that.sContentID)[0].innerHTML = sBuildContent(that);
			$('#' + this.sContentID + '-canvas').each(function() {
				this.getContext && this.getContext('2d').drawImage(that.oPreloader, 0, 0, that.iWidth, that.iHeight);
			});
		},
		
		Resize: function(oAvailable) {
			var that = this;
			this.Prepare(oAvailable);
			$('#' + this.sContentID).css({ width: this.iWidth, height: this.iHeight }).html(sBuildContent(this));
			$('#' + this.sContentID + '-canvas').each(function() {
				this.getContext && this.getContext('2d').drawImage(that.oPreloader, 0, 0, that.iWidth, that.iHeight);
			});
		},
		
		Remove: function() {
			$('#' + this.sContentID).remove();
		}
	};
})();

// === SWF HANDLER (=====================================================================================

(function() {

	Fluxbox.AddExtensions(['swf'], 'swf');

	Fluxbox.oPlugins.swf = function() {
		this.sContentID = 'fluxbox-display-item-swf';
		this.bReady = false;
		this.sRequiredVersion = '6.0.0';
	};

	Fluxbox.oPlugins.swf.prototype = {

		Prepare: function(oAvailable) {
			this.iWidth = oAvailable.iWidth;
			this.iHeight = oAvailable.iHeight;
			this.bReady = true;
		},
		
		Append: function(oParent)	{
			oParent.append('<div id="'+this.sContentID+'"><br>Oops! Please <a href="http://www.adobe.com/go/getflash/">upgrade to the latest Adobe Flash Player</a> for your browser.</div>');
			swfobject.embedSWF(
				this.sURL,
				this.sContentID,
				this.iWidth,
				this.iHeight,
				this.sRequiredVersion,
				false, // express loader
				{ // flashvars
				},
				{ // params
					wmode: 'transparent'
				},
				{ // attributes
				}
			);
			// hack to force Mac FF3 to redraw the overlay area,
			// to clear a misdrawn early copy of the content
			$('#fluxbox-overlay').height('101%')			
			setTimeout(function() { $('#fluxbox-overlay').height('100%') }, 100);
		},
		
		Resize: function(oAvailable) {
			this.Prepare(oAvailable);
			$('#' + this.sContentID).css({ width: this.iWidth, height: this.iHeight });
		},
		
		Remove: function() {
			swfobject.removeSWF(this.sContentID);
			$('#' + this.sContentID).remove();
		}
	};
})();

// === FLV HANDLER (=====================================================================================

(function() {

	Fluxbox.AddExtensions(['flv', 'mp4'], 'flashvideo');

	Fluxbox.oPlugins.flashvideo = function() {
		this.sContentID = 'fluxbox-display-item-flashvideo';
		this.bReady = false;
		this.sRequiredVersion = '8.0.0';
	};

	Fluxbox.oPlugins.flashvideo.prototype = {

		Prepare: function(oAvailable) {
			this.iWidth = oAvailable.iWidth;
			this.iHeight = oAvailable.iHeight;
			this.bReady = true;
		},

		Append: function(oParent)	{
			oParent.append('<div id="' + this.sContentID + '"><br>Oops! Please <a href="http://www.adobe.com/go/getflash/">upgrade to the latest Adobe Flash Player</a> for your browser.</div>');
			swfobject.embedSWF(
				'../common/fluxbox/player.swf',
				this.sContentID,
				this.iWidth,
				this.iHeight,
				this.sRequiredVersion,
				false, // express loader
				{ // flashvars
					file: this.sURL
				},
				{ // params
					wmode: 'transparent',
					allowfullscreen: 'true',
					allowscriptaccess: 'always'
				},
				{ // attributes
				} 
			);
			// hack to force Mac FF3 to redraw the overlay area,
			// to clear a misdrawn early copy of the content
			$('#fluxbox-overlay').height('101%')			
			setTimeout(function() { $('#fluxbox-overlay').height('100%') }, 100);
			
		},
		
		Resize: function(oAvailable) {
			this.Prepare(oAvailable);
			$('#' + this.sContentID).css({ width: this.iWidth, height: this.iHeight });
		},
		
		Remove: function() {
			swfobject.removeSWF(this.sContentID);
			$('#' + this.sContentID).remove();
		}
	};
})();

// === IFRAME HANDLER (CATCHALL) ===========================================================================

(function() {

	Fluxbox.oPlugins.catchall = function() {
		this.sContentID = 'fluxbox-display-item-catchall';
		this.bReady = false;
	};

	Fluxbox.oPlugins.catchall.prototype = {

		Prepare: function(oAvailable) {
			this.iWidth = oAvailable.iWidth;
			this.iHeight = oAvailable.iHeight;
			this.bReady = true;
		},

		Append: function(oParent)	{
			oParent.append([
				'<iframe',
				' id="' + this.sContentID + '"',
				' name="' + this.sContentID + '"',
				' style="width:100%; height: 100%"',
				' frameBorder="0"',
				' marginwidth="0"',
				' marginheight="0"',
				' scrolling="auto"',
				' allowtransparency="true"',
				' src="' + this.sURL + '"',
				'></iframe>'
			].join(''));
		},
		
		Resize: function(oAvailable) {
			this.Prepare(oAvailable);
			$('#' + this.sContentID).css({ width: this.iWidth, height: this.iHeight });
		},

		Remove: function() {
			$('#' + this.sContentID).remove();
			try {
				// ensure reference to frame is removed
				delete window.frames[this.sContentID];
			}
			catch(oEvent) {
			}
		}
	};
})();

$(Fluxbox.Initialise);

// VML setup for IE
// namespaces seems to have some problems being
// accessed after DOMload, so do it immediately
if (document.namespaces) {
	document.namespaces.add('fluxbox_vml', 'urn:schemas-microsoft-com:vml', '#default#VML');
}

// rounded corners
(function($){
	$.fn.rounded = function() {
		var jCorners = null;
		var fnCorner = null;
		var iWidth = 0;
		var sColour = '';
		
		function VMLCorner(sVt, sHz) {
			return '<div class="vml-corner" style="'+sVt+': -'+(iWidth)+'px; '+sHz+':-'+(iWidth)+'px; position: absolute; overflow:hidden; width:'+ (iWidth) +'px; height: ' + (iWidth) + 'px;">' + 
			'<fluxbox_vml:oval stroke="false" filled="true" fillcolor="'+sColour+'" style="position: absolute; '+sVt+': 0px; '+sHz+': 0px; width:' + (2*iWidth) + 'px; height:' + (2*iWidth) + 'px" />' +
			'</div>';
		}
		
    function CanvasCorner(sVt, sHz) {
			var jCanvas = $(('<canvas width="'+iWidth+'" height="'+iWidth+'" style="position:absolute; ' + sVt + ':-' + iWidth + 'px;' + sHz + ':-' + iWidth + 'px;"></canvas>').replace('bottom:-'+iWidth+'px', 'top:100%'));
	    var oContext = jCanvas[0].getContext('2d');
	    oContext.beginPath();
	    oContext.arc(sHz=='left' ? iWidth : 0, sVt=='top' ? iWidth : 0, iWidth, 0, 2*Math.PI+0.001, true);
			oContext.fillStyle = sColour;
	    oContext.fill();
			jCorners.append(jCanvas);
			return '';
    }

		return this.each(function() {
			var jElement = $(this);
			// read the border width from the top border
			iWidth = parseInt(jElement.css('borderTopWidth'));
			if (iWidth > 1) {
				// read the border colour from the top border
				sColour = jElement.css('borderTopColor');
				// use canvas if present, vml if not
				var fnCorner = !!document.createElement('canvas').getContext ? CanvasCorner : VMLCorner;
				jCorners = $('<div></div>');
				jElement.append(jCorners);
				// add edges
				jCorners.append($(('<div style="position: absolute; width:100%; height:'+iWidth+'px; left:0; top:-'+iWidth+'px; background:'+sColour+';"></div>')));
				jCorners.append($(('<div style="position: absolute; width:100%; height:'+iWidth+'px; left:0; top:100%; background:'+sColour+';"></div>')));
				// add corners
				var sHTML = fnCorner('top', 'left') + fnCorner('top', 'right') + fnCorner('bottom', 'left') +	fnCorner('bottom', 'right');
				if (sHTML) {
					jCorners[0].innerHTML += sHTML;
				}
				if (this.style.position != 'absolute') {
					this.style.position = 'relative';
				}
				// remove existing top+bottom borders, replace with margin
				// leave the other two sides in place; free edges, and it solves an IE 6 height issue
				jElement.css( { 'border-top': 'none', 'border-bottom': 'none' } );
				jElement.css( { margin: 2 * iWidth + 'px ' + iWidth +'px' } );
			}
		});
	}
})(jQuery);

$(function() {
	$('#fluxbox-main-inner').rounded();
});

