var $fw=$fw||jQuery.noConflict();
	(function($) {
		$.fn.fwShowcase = function(options) {
			return this.each(function() {
				// Keep track of which image we're current looking at
				var curImage = null,
					curPage = 1,
					playing = false,
					duration = null,
					$this = $(this),
					total = $(this).find("li").length,
					settings = $.extend({
						overlayBg: "",
						overlayOp: 0.5,
						galleryBg: "",
						galleryTxtColor: "#000",
						borderColor: "",
						borderWidth: 0,
						padding: 0,
						startW: 100,
						startH: 100,
						captions: false,
						captionHeight: 0,
						fontFamily: "Helvetica,Arial,sans-serif",
						roll: "",
						controls: true,
						showInfo: true,
						autohideControls: true,
						autohideInfo: true,
						galleryCss: {},
						imageCss: {},
						dir: 0, // 0 = horizontal, 1 = vertical
						thumbs: true
					}, options);
				
				// We need to fire a custom event because the item may be hidden by other Actions
				function isVisibleEvent() {
					if($($this).outerWidth() == 0)
						setTimeout(isVisibleEvent, 100);
					else
						$($this).trigger("visible");
				}
				
				isVisibleEvent();
				
				if(settings.slideshow) {
					if(settings.thumbs) {
						var $wrap = $this.find('.fwShowcase_wrapper'),
							$outer = $this.find('.fwShowcase_outer'),
							$inner = $this.find('.fwShowcase_inner'),
							$list = $inner.find('.fwShowcase_thumbs'),
							$items = $list.find('li'),
							itemWidth = settings.itemWidth,
							itemHeight = settings.itemHeight,
							totalHeight = settings.outerHeight,
							totalWidth = settings.outerWidth
							visible = (settings.dir ? totalHeight / itemHeight : totalWidth / itemWidth),
							pages = Math.ceil($items.length / visible);
						
						$outer.css({overflow:"hidden"})
							  .before("<a href=\"\" class=\"fwShowcase_prev\"></a>")
							  .after("<a href=\"\" class=\"fwShowcase_next\"></a><div class=\"fwShowcase_clearer\"></div>");
						
						if(!settings.dir) { // Horizontal
							// Add extra for the cloned items
							$inner.css({width: parseInt($inner.css("width")) + (totalWidth * 2)});
							$wrap.find('> .fwShowcase_prev, > .fwShowcase_next').css({width:27, height:27, display:"block", "float":"left", marginTop:(totalHeight-27)/2, marginRight:10, background:"url("+settings.controlsPath+") 0px 0px no-repeat"});
							$wrap.find('> .fwShowcase_next').css({marginLeft:10, marginRight:0, backgroundPosition:"-55px 0px"});
							$this.find('.fwShowcase_clearer').css({clear:"both"});
						}
						else { // Vertical
							// Add extra for the cloned items
							$outer.css({height:totalHeight});
							$inner.css({height: (itemWidth - settings.padding) + (totalWidth * 2)});
							$wrap.find('> .fwShowcase_prev, > .fwShowcase_next').css({width:27, height:27, display:"block", margin:"auto", marginBottom:10, background:"url("+settings.controlsPath+") 0px -27px no-repeat"});
							$wrap.find('> .fwShowcase_next').css({marginTop:10, marginBottom:0, backgroundPosition:"-55px -27px"});
						}
						
						function repeat(str, num) {
							return new Array(num + 1).join(str);
						}
						
						if(($items.length % visible) != 0) {
							$items.filter(':last').after(repeat('<li class="fwShowcase_empty" />', visible - ($items.length % visible)));
							$items = $list.find('li');
						}
						
						$items.filter(':first').before($items.slice(- visible).clone().addClass('fwShowcase_cloned'));
						$items.filter(':last').after($items.slice(0, visible).clone().addClass('fwShowcase_cloned'));
						
						function scrollThumbs(){
							if(!settings.dir)
								$outer.scrollLeft(totalWidth);
							else
								$outer.scrollTop(totalHeight);
						}
						
						if(!$($this).is(":visible"))
							$($this).bind("visible", scrollThumbs);
						else
							scrollThumbs();
						
						function gotoPage(page) {
							if(page == curPage)
								return;
							var dir = page < curPage ? -1 : 1,
								n = Math.abs(curPage - page),
								scroll = (!settings.dir ? totalWidth : totalHeight) * n * dir; 
							$this.find('.fwShowcase_outer:not(:animated)').animate({
								scrollLeft : "+=" + (!settings.dir ? scroll : 0),
								scrollTop : "+=" + (!settings.dir ? 0 : scroll)
							}, 500, function() {
								if (page == 0) {
									if(!settings.dir)
										$outer.scrollLeft(totalWidth * pages);
									else
										$outer.scrollTop(totalHeight * pages);
									page = pages;
								} else if (page > pages) {
									if(!settings.dir)
										$outer.scrollLeft(totalWidth);
									else
										$outer.scrollTop(totalHeight);
									// reset back to start position
									page = 1;
								}
								curPage = page;
							});
						}
			
						$this.find('a.fwShowcase_prev').click(function(){
							gotoPage(curPage-1);
							return false;
						});
						
						$this.find('a.fwShowcase_next').click(function(){
							gotoPage(curPage+1);
							return false;
						});
					}
					
					$this.find(".fwShowcase_slideshow > .fwShowcase_main")
							.append("<div class=\"fwShowcase_info\"><\/div>" +
								"<div class=\"fwShowcase_controls\">" +
								"<div class=\"fwShowcase_cred\"><a href=\"http://www.softpress.com\" title=\""+settings.freeway+"\" target=\"_blank\"><\/a><\/div>" +
								"<div class=\"fwShowcase_prev\"><a href=\"\" title=\""+settings.prevTxt+"\"><\/a><\/div>" +
								"<div class=\"fwShowcase_play\"><a href=\"\" title=\""+settings.playTxt+"\"><\/a><\/div>" +
								"<div class=\"fwShowcase_next\"><a href=\"\" title=\""+settings.nextTxt+"\"><\/a><\/div>");
				}
				else {
					$("body:not(:has(#fwShowcase))") // Match a body tag that *doesn't* contain a #fwShowcase id
					.append("<div id=\"fwShowcase_overlay\"><\/div>")
					.append("<div id=\"fwShowcase\">" +
						"<div id=\"fwShowcase_image\"><a class=\"fwShowcase_prev\" title=\""+settings.prevTxt+"\" href=\"\"></a><a class=\"fwShowcase_next\" title=\""+settings.nextTxt+"\" href=\"\"></a><\/div>" +
						"<div class=\"fwShowcase_title\"><\/div>" +
						"<div class=\"fwShowcase_info\"><\/div>" +
						"<div class=\"fwShowcase_controls\">" +
						"<div class=\"fwShowcase_prev\"><a href=\"\" title=\""+settings.prevTxt+"\"><\/a><\/div>" +
						"<div class=\"fwShowcase_play\"><a href=\"\" title=\""+settings.playTxt+"\"><\/a><\/div>" +
						"<div class=\"fwShowcase_next\"><a href=\"\" title=\""+settings.nextTxt+"\"><\/a><\/div>" +
						"<div class=\"fwShowcase_close\"><a href=\"\" title=\""+settings.closeTxt+"\"><\/a><\/div>" +
						"<\/div>" +
						"<\/div>");
				}
				
				var $info = (settings.slideshow ? $this.find(".fwShowcase_info") : $(".fwShowcase_info")),
					$controls = (settings.slideshow ? $this.find(".fwShowcase_controls") : $(".fwShowcase_controls")),
					$prev = (settings.slideshow ? $controls.find(".fwShowcase_prev > a") : $(".fwShowcase_prev > a, a.fwShowcase_prev")),
					$next = (settings.slideshow ? $controls.find(".fwShowcase_next > a") : $(".fwShowcase_next > a, a.fwShowcase_next")),
					$play = (settings.slideshow ? $controls.find(".fwShowcase_play > a") : $(".fwShowcase_play > a")),
					$close = $(".fwShowcase_close > a"),
					$title = (settings.slideshow ? $this.find(".fwShowcase_title") : $(".fwShowcase_title")),
					$caption = $this.find(".fwShowcase_caption"),
					$list = $list || $this.find("ul"),
					$usableItems = $list.find("li:not(.fwShowcase_empty, .fwShowcase_cloned)"),
					$images = $usableItems.find("> a"),
					$viewer = (settings.slideshow ? $(".fwShowcase_main") : $("#fwShowcase")),
					$overlay = $("#fwShowcase_overlay");
					
				if(settings.slideshow) {
					if(!$($this).is(":visible"))
						$($this).bind("visible", function(){
							curImage = $usableItems.first();
							playPause();
							addListeners();
						});
					else {
						curImage = $usableItems.first();
						playPause();
						addListeners();
					}
					if(!settings.controls || settings.autohideControls)
						$controls.css({opacity:0});
				}
				else {
					$(window).resize(adjustGallery)
							 .scroll(adjustGallery);
				}
				
				$($title).add($caption).css({"text-overflow":"ellipsis"});
				
				if(settings.showInfo)
					$info.css({opacity:0}).text("1 / "+total);
				else
					$info.hide();
				if(!settings.autohideInfo)
					$info.css({opacity:0.5});
				
				if($.browser.msie && $.browser.version=="6.0") {
					$usableItems.mouseenter(function() {
							$(this).toggleClass("hover");
						})
						.mouseleave(function() {
							$(this).toggleClass("hover");
						});
				}
				
				// When an image is clicked on:
				$images.click(function() {
					if(settings.slideshow)
						swapImage($(this).parent());
					else
						// Show the overlay
						showOverlay(this);
					
					// Prevent the default behaviour
					return false;
				});
				
				function autohideIn() {
					if(settings.autohideInfo)
						$info.stop().animate({opacity:0.5}, 100);
					if(settings.autohideControls)
						$controls.stop().animate({opacity:1}, 100);
				}
				
				function autohideOut() {
					if(settings.autohideInfo)
						$info.add(settings.autohideControls ? $controls : null).stop().animate({opacity:0}, 100);
					else if(settings.autohideControls)
						$controls.stop().animate({opacity:0}, 100);
				}
				
				function addListeners() {
					// Hide everything when clicking the overlay or the close button
					$($overlay).add($close).click(hideAll);
					
					// Display the next image when clicking the previous image link or the left side of the image
					$prev.click(prevImage);
					
					// Display the next image when clicking the next image link or the right side of the image
					$next.click(nextImage);
						
					// Play/pause the slideshow when the play/pause buttons are clicked
					$play.click(playPause);
					
					// Autohide the controls and info when hovering over the viewer
					if(settings.autohideControls || settings.autohideInfo)
						$viewer.hover(autohideIn, autohideOut);
					
					// track key presses
					$(document).keydown(function(e){ 
							// Hide everything when the escape key is pressed
							if(e.which == 27 && !settings.slideshow)
								hideAll();
							// Play/pause the slideshow when the spacebar is pressed
							else if(e.which == 32) // Spacebar
								return playPause();
							// Go to the prev/next image when the left/right arrows are pressed
							else if(e.which == 37) // Left arrow
								return prevImage();
							else if(e.which == 39) // Right arrow
								return nextImage();
						});
				}
				
				function removeEventListeners() {
					// Unbind the close button
					$overlay.add($close)
					
					// And the prev/next and play buttons
						.add($prev).add($next).add($play).unbind("click");
					
					// Unbind the autohide
					$viewer.unbind("hover");
					
					// Unbind the key presses
					$(document).unbind("keydown");
				}
				
				function playPause() {
					if(!playing) {
						duration = setInterval(nextImage, settings.duration * 1000);
						$play.css({backgroundPosition: "-27px -27px"});
					}
					else {
						clearInterval(duration);
						$play.css({backgroundPosition: "-27px 0px"});
					}
					playing = !playing;
					return false;
				}
				
				function resetDuration() {
					if(playing) {
						clearInterval(duration);
						duration = setInterval(nextImage, settings.duration * 1000);
					}
				}
				
				function prevImage() {
					try {
						if(settings.slideshow) {
							swapImage($(curImage).prev(":not(.fwShowcase_cloned, .fwShowcase_empty)"))
						} else
							showImage($(curImage).prev());
					} catch (e) {
						if(settings.slideshow) {
							swapImage($usableItems.last())
						} else
							showImage($usableItems.last());
					}
					return false;
				}
				
				function nextImage() {
					try {
						settings.slideshow ?
							swapImage($(curImage).next(":not(.fwShowcase_cloned, .fwShowcase_empty)"))
						:
							showImage($(curImage).next());
					} catch (e) {
						settings.slideshow ?
							swapImage($usableItems.first())
						:
							showImage($usableItems.first());
					}
					return false;
				}
				
				function adjustGallery() {
					
					// If the gallery isn't there then return
					if(curImage === null)
						return;
					
					var w = $viewer.width(),
						h = $viewer.height(),
						l = ($(window).width() / 2 + $(window).scrollLeft()),
						t = ($(window).height() / 2  + $(window).scrollTop()),
						mt = -(h / 2),
						ml = -(w / 2);
					
					// If the gallery is at 0 (e.g., top + negative margin top) then make it stay at 0
					if(t + mt < 0)
						t = mt = 0;
					if(l + ml < 0)
						l = ml = 0;
						
					$viewer.css({top: t, marginTop: mt});
					$viewer.css({left: l, marginLeft: ml});
					
					// Match the overlay height to the page height and width
					$overlay.css({
						height: $(document).height(),
						width: $(document).width()
					});
				}
				
				function hideAll() {
					// Find the overlay element and hide it
					$overlay.stop().hide();
					
					// Find the gallery element and hide it
					$viewer.stop().hide().removeClass();
					
					$controls.hide();
					
					$viewer.find("img").empty();
					
					$title.empty();
					
					// Stop to the slideshow
					playing = false;
					clearInterval(duration);
					$play.css({backgroundPosition: "-27px 0px"});
					
					removeEventListeners();
					curImage = null;
					
					return false;
				}
				
				function imageTop(image) {
					var height = $this.height() - (settings.dir ? 0 : (settings.thumbs ? $wrap.outerHeight() : 0))
					return settings.align == 0 ? // Top
								0
							:
								settings.align == 1 ? // center = (overallHeight - imageHeight) / 2
									(height - ((image.height + (settings.captions ? settings.captionHeight : 0)) + ((settings.padding + settings.borderWidth) * 2))) / 2
								: // bottom = (overallHeight - imageHeight)
									(height - ((image.height + (settings.captions ? settings.captionHeight : 0)) + ((settings.padding + settings.borderWidth) * 2)));
				}
				
				function swapImage(input) {
					// Remember the image
					if($(input).parent().hasClass("fwShowcase_thumbs"))
						curImage = input;
					else
						throw "showImage error: Incorrect element passed";
					
					$($list).find(".hover").removeClass("hover");
					curImage.addClass("hover");
					
					// Get the anchor element, the path to the main image and create a new Image
					var anchor = $(curImage).find("> a"),
						path = anchor.attr("href"),
						image = new Image(),
						
						// Find the currently displaying image
						main = $this.find(".fwShowcase_main"),
						imageDiv = $this.find(".fwShowcase_image");
					
					main.stop();
					resetDuration();
					
					// Remove the current Image
					imageDiv.empty();
					
					// Once the image has loaded
					$(image).load(function() {
						
						$(this).css({
							margin: settings.padding,
							width: image.width,
							height: image.height
						});
						imageDiv.append(image);
						// Hide it, append it to main and then animate it in
						main.css({
								opacity:0,
								top: imageTop(this),
								width: image.width + (settings.padding * 2),
								height: image.height + (settings.captions ? settings.captionHeight : 0) + (settings.padding * 2)
							})
							.animate({
								opacity:1
							}, 200, function() {
								// Add the gallery specific event handler
								$(window).unbind("keydown").keydown(function(e){ 
									if(e.which == 37) // Left arrow
										prevImage();
									else if(e.which == 39) // Right arrow
										nextImage();
								});
							});
							if(settings.captions)
								$title.css({width:image.width}).text($(anchor).find("> img").attr("alt"));
							if(settings.showInfo) {
								var listItems = $list.find("li:not(.fwShowcase_empty, .fwShowcase_cloned)");
								$info.text(($(listItems).index(curImage)+1)+" / "+total);
							}
					}).attr("src", path);
				}
				
				function showOverlay(anchor) {
					// Change the color of the overlay and hide it using opacity:0,
					$overlay.css({
						background: settings.overlayBg || "#fff", 
						opacity: 0,
						height: $(document).height(),
						width: $(document).width()
					})
					
					// then show it (this changes its display state)
					.show()
					
					// and finally, animate its opacity up to the specified opacity
					.animate({
							opacity: settings.overlayBg ? settings.overlayOp : 0
						}, 100, function(){
							// Show the gallery placeholder
							showImage($(anchor).parent());
						}
					);
					addListeners();
				}
				
				function showImage(listItem) {
					// Remember the image
					if($(listItem).parent().hasClass("fwShowcase_thumbs"))
					{
						var prevImage = curImage;
						curImage = listItem;
					}
					else
						throw "showImage error: Incorrect element passed";
					
					$viewer.stop();
					resetDuration();
					
					var anchor = $(curImage).find("> a"),
						path = anchor.attr("href"),
						image = new Image(),
						
					// Get the gallery_image element
						$image = $("#fwShowcase_image"),
					
					// Calculate the faux border
						offset = settings.padding,
					
					// And the width/height and top/left
						l = ($(window).width() / 2 + $(window).scrollLeft()),
						t = ($(window).height() / 2 + $(window).scrollTop());
					
					// Remove the old one if there
					$image.find("img").remove();
					$title.empty();
					
					$("#fwShowcase_image > .fwShowcase_prev, #fwShowcase_image > .fwShowcase_next").hide();
					  
					// Set the gallery viewer text color and then hide it
					$title.css({color: settings.galleryTxtColor}).hide();
					$($info).add($controls).hide();
					
					// Show and resize the placeholder if necessary
					$("#fwShowcase:hidden").css(settings.galleryCss)
						.addClass($this.attr("id"))
						.css({
							background: settings.galleryBg, 
							width: settings.startW, 
							height: settings.startH,
							top: t,
							left: l,
							marginLeft: -(settings.startW / 2),// half the width
							marginTop: -(settings.startH / 2), // half the height
							borderWidth: settings.borderWidth,
							borderColor: settings.borderColor,
							fontFamily: settings.fontFamily
						})
						.show();
					
					if(!settings.credit)
						$(".fwShowcase_cred").css({display:"none"});
					
					// Once the new image has loaded place it in the gallery_image div
					$(image).load(function() {
						
						var outer = (offset+settings.borderWidth) * 2,
							totalHeight = image.height + settings.captionHeight + outer,
							totalWidth = image.width + outer,
						
						// Calculate the margins (these are negative for centring
							mt = -totalHeight / 2,
							ml = -totalWidth / 2,
							ih = image.height,
							iw = image.width;
						
						// If the image is wider or shorter than the available space then scale the image
						if(totalHeight > $(window).height() || totalWidth > $(window).width()) {
							var scale = Math.max(image.width / ($(window).width()-outer), image.height / ($(window).height()-(settings.captionHeight+outer)));
							ih /= scale;
							iw /= scale;
							mt = -(ih + settings.captionHeight + outer) / 2;
							ml = -(iw + outer) / 2;
						};
						
						if(t + mt < 0)
							t = mt = 0;
						if(l + ml < 0)
							l = ml = 0;
						if($overlay.is(":visible")) {
							$image.prepend(image);
							// Show and resize the placeholder if necessary
							$viewer.animate({
								height: ih + settings.captionHeight + (offset * 2),
								width: iw + (offset * 2),
								top: t,
								left: l,
								marginLeft: ml,
								marginTop: mt
							}, 200, function(){
								// Show the image once the animation has completed
								$(image).css(settings.imageCss)
									.css({
										opacity: 0, 
										width: iw, 
										height: ih,
										top: offset,
										left: offset
									})
									.show()
									.animate({
										opacity: 1
									}, 200, function() {
										$("#fwShowcase_image > .fwShowcase_prev, #fwShowcase_image > .fwShowcase_next").css({
											width : iw / 2,
											height : ih,
											top : offset,
											right : offset,
											outline : "none"
										}).show();
										$("#fwShowcase_image > .fwShowcase_prev").css({
											left : offset
										});
										if(settings.controls) {
											if(!settings.autohideControls || settings.autohideControls && prevImage)
												$controls.show();
											else if(settings.autohideControls && !prevImage)
												$controls.css({opacity:0}).show();
										}
										if(settings.showInfo) {
											if(!settings.autohideInfo || settings.autohideInfo && prevImage) {
												var listItems = $list.find("li:not(.fwShowcase_empty, .fwShowcase_cloned)");
												$info.text(($(listItems).index(curImage)+1)+" / "+total).show();
											}
											else if(settings.autohideInfo && !prevImage){
												var listItems = $list.find("li:not(.fwShowcase_empty, .fwShowcase_cloned)");
												$info.text(($(listItems).index(curImage)+1)+" / "+total).css({opacity:0}).show();
											}
										}
									});
								if(settings.captions)
									$title.css({width:image.width}).text($(anchor).find("> img").attr("alt")).show();
							});
						}
					}).attr("src", path).hide();
				}
			});
		}
	})(jQuery);
