
	/**
	 * CustomerGardens is the client side logic behind customer_gardens
	 * @param {Object} options
	 * options["url"] = the AJAX service to interface with
	 * options["main_image"] = The ID of the main img tag for an image
	 * options["image_title"] = The ID of the image title div
	 * options["customer_name"] = The ID of the customer name div
	 * options["image_desc"] = The ID of the image description div
	 * options["next_page_btn"] = The ID of the next page button
	 * options["prev_page_btn"] = The ID of the prev page button
	 * options["next_img_btn"] = The ID of the next image button
	 * options["prev_img_btn"] = The ID of the prev image button
	 * options["image_select_bar"] = The ID of the Image select bar
	 * options["images_per_page"] = How many images per page?
	 * options["image_loading"] = The URL of the loading image
	 * options["slider"] = The optional image page slider for easier page navigation
	 */
	function CustomerGardens(options)
	{		
		this.init(options)
		return this;
	}
	
	/**
	 * Utility function
	 */
	function IsNumeric(input)
	{
	   return (input - 0) == input && input.length > 0;
	}
	
	$.extend(CustomerGardens.prototype, {
		
		ImagePages 		: [],
		ToAdd			: [],
		IgnoreHis		: false,
		Elements		: null,
		CurrentPage		: 1,
		CurrentImage	: 1,
		InitalImageId	: null,
		CurrentThumb	: null,
		Slider			: null,
		Loaded			: false,
		CurrentShade	: null,
		ZoomOptions		: null,
		ImgRefCount		: 0, /* represents the ref count for changing images at the same time */
		DispOptions		: {
			// defaults here			
		},
		
		/**
		 * Inits the customer gallery
		 */
		init : function(options)
		{
			var that = this;
						
			$(document).ready(function(){
				
				// add in our sweet historyness
				$.history.init(function(hash){that.navEvent(hash)});
				that.DispOptions = options.display;
				that.ZoomOptions = options.zoom;
				
				// Get all of our elements
				that.Options = options;
				that.Elements = {
					MainImage		: $("#" + options.main_image),
					ImageTitle		: $("#" + options.image_title),
					CustomerName	: $("#" + options.customer_name),
					ImageDesc		: $("#" + options.image_desc),
					NextPage		: $("#" + options.next_page_btn),
					PrevPage		: $("#" + options.prev_page_btn),
					NextImg			: $("#" + options.next_img_btn),
					PrevImg			: $("#" + options.prev_img_btn),
					ImgSelect		: $("#" + options.image_select_bar),
					PageNumber		: $("#" + options.page_number),
					PageCount		: $("#" + options.page_count)
				};
				
				if (options.slider)
					that.Elements.Slider = $("#" + options.slider);
				
				// now go ahead and set our dims
				that.Elements.ImgSelect.parent().css("width", "470px");
				
				that.Loaded = true;
				
				// Add all queued images
				for (var i = 0; i < that.ToAdd.length; i++)
				{
					that.addImages(that.ToAdd[i].page, that.ToAdd[i].images);
				}
				
				if (that.InitalImageId)
					that.setImageById(that.InitalImageId);
				
				// Add the events
				that.Elements.NextPage.click(function(){
					that.nextPage();
					return false;		
				});
				
				that.Elements.PrevPage.click(function(){
					that.prevPage();
					return false;		
				});
				
				that.Elements.NextImg.click(function(){
					that.nextImage();
					return false;		
				});
				
				that.Elements.PrevImg.click(function(){
					that.prevImage();
					return false;		
				});
				
				that.resetSlider();
				that.resetZoom();
				
				// now select the first image
				if (that.ImagePages.length && !that.InitalImageId)
					that.switchImage(that.CurrentPage, that.CurrentImage, false);
			});
		},
		
		/**
		 * Pre-loads an image and runs a callback when done
		 * @param {Object} url
		 */
		loadImage : function(url)
		{
			var that = this;
			var imageLoader = new Image();
			imageLoader.onload = function(){
				that.finishSwitch($(this).attr("src"));
			}
			$(imageLoader).attr("src", url);
		},
		
		/**
		 * Used for back/forward/bookmarks
		 */
		navEvent : function(hash, that)
		{
			if (this.IgnoreHis) 
			{
				this.IgnoreHis = false;
				return;
			}
			
			var match = hash.match(/^(\d+)/);
			
			if (match)
				this.setImageById(match[1]);
			else if (hash == "" && this.ImagePages.length 
				&& this.ImagePages[0].ImageWrappers.length)
				this.switchImage(1, 1, false);
		},
		
		/**
		 * 
		 */
		setImageById : function(id)
		{
			if (!this.Loaded)
			{
				this.InitalImageId = id;
				return;
			}
			
			var found = null;
			
			for (var i = 0; i < this.ImagePages.length; i++)
			{
				for (var j = 0; j < this.ImagePages[i].ImageWrappers.length; j++)
				{
					var image = this.ImagePages[i].ImageWrappers[j].Data;
					if (image.id == id)
					{
						found = [i + 1, j + 1];
						break;
					}
				}
				
				if (found)
					break;
			}
			
			if (!found)
				return;
			
			this.switchImage(found[0], found[1], false);
		},
		
		/**
		 * Goes to the next page, or stops
		 */
		nextPage : function()
		{
			if (this.ImagePages.length > this.CurrentPage)
			{
				this.switchPage(this.CurrentPage + 1);
				return true;
			}
			return false;
		},
		
		/**
		 * Goes to the prev page, or stops
		 */
		prevPage : function()
		{
			if (this.CurrentPage > 1)
			{
				this.switchPage(this.CurrentPage - 1);
				return true;
			}
			return false;
		},
		
		/**
		 * Goes to the next image, or stops. Also moves to the next page if required
		 */
		nextImage : function()
		{
			if (this.CurrentImage < this.ImagePages[this.CurrentPage - 1].ImageData.length)
			{
				this.switchImage(this.CurrentPage, this.CurrentImage + 1);
			}
			else
			{
				if (this.nextPage())
				{
					this.switchImage(this.CurrentPage, 1);
				}
			}
		},
		
		/**
		 * Goes to the prev image, or stops. Also moves to the next page if required
		 */
		prevImage : function()
		{
			if (this.CurrentImage > 1)
			{
				this.switchImage(this.CurrentPage, this.CurrentImage - 1);
			}
			else
			{
				if (this.prevPage())
				{
					this.switchImage(this.CurrentPage, 
						(this.DispOptions.per_row * this.DispOptions.rows));
				}
			}
		},
		
		/**
		 * Switches to a different image in the main image box
		 * @param {Object} page
		 * The page this image is on
		 * @param {Object} image
		 * The image index of this image
		 * @param {bool} doHistory
		 * Should we add this to the history?
		 */
		switchImage : function(page, image, doHistory)
		{
			if (doHistory == null)
				doHistory = true;
			 
			if (!this.ImagePages.length)
				return;
				
			if (this.CurrentThumb)
			{
				$(this.CurrentThumb).removeClass("ImageSelected");
			}
			
			if (page != this.CurrentPage)
				this.switchPage(page);
			
			var imageRow = this.ImagePages[page - 1].ImageWrappers[image - 1];
			var imageData = imageRow.Data;
			
			if (doHistory)
			{
				this.IgnoreHis = true;
				document.title = imageData.title;
				$.history.load(imageData.id + "-" + imageData.title);
			}
			
			var ele = $(imageRow.Element);
			ele.addClass("ImageSelected");
			this.CurrentThumb = ele;
			
			if (this.CurrentShade == null)
			{
				var shadeEle = document.createElement("div");
				document.body.appendChild(shadeEle);
				
				var shade = $(shadeEle);
				shade.css("position", "absolute");
				shade.css("width", ele.width() + "px");
				shade.css("height", ele.height() + "px");
				shade.css("opacity", 0.5);
				shade.css("background", "#444");
				shade.css("top", ele.offset().top + "px");
				shade.css("left", ele.offset().left + "px");
				shade.css("z-index", "1000")
				shade.css("border", "1px solid #444");

				if (!jQuery.browser.msie) {
					shade.animate({
						//		opacity		: 0.15,
						width: this.Elements.MainImage.width() + "px",
						height: this.Elements.MainImage.height() + "px",
						top: this.Elements.MainImage.offset().top + "px",
						left: this.Elements.MainImage.offset().left + "px"
					}, 100);
				}
				else{
					shade.css("width", this.Elements.MainImage.width() + "px");
					shade.css("height", this.Elements.MainImage.height() + "px");
					shade.css("top", this.Elements.MainImage.offset().top + "px");
					shade.css("left", this.Elements.MainImage.offset().left + "px");
				}
				
				this.CurrentShade = shade;
			}
			
			this.ImgRefCount++;
			
			this.loadImage(imageData.url)
			this.CurrentImage = image;
			
			this.Elements.CustomerName.html(imageData.name);
			this.Elements.ImageTitle.html(imageData.title);
			this.Elements.ImageDesc.html(imageData.desc);
			this.Elements.ImageDesc.css("top", (
				(50 / 2) -
				(this.Elements.ImageDesc.height() / 2) 
			) + "px");
		},
		
		finishSwitch : function(url)
		{
			var that = this;
			window.setTimeout(function(){
				if (--that.ImgRefCount <= 0) {
					that.Elements.MainImage.attr("src", url);
					if (that.CurrentShade) {
						that.CurrentShade.animate({
							opacity: 0
						}, 100, function(){
							if (that.CurrentShade != null) {
								that.CurrentShade.remove();
								that.CurrentShade = null;
							}
						});
					}
				}
			}, 500);
		},
		
		/**
		 * Switches to a new page
		 * @param {Object} page
		 */
		switchPage : function(page)
		{
			var imageRow = this.ImagePages[page - 1].ImageRow;
			var current = this.ImagePages[this.CurrentPage - 1].ImageRow;
						
			this.CurrentPage = page;
			this.Elements.ImgSelect.stop();
			this.Elements.ImgSelect.animate({
				marginLeft		: ((
				(
					(this.DispOptions.per_row * this.DispOptions.cell_padding) +
					(this.DispOptions.per_row * this.DispOptions.wid)
				) * (page - 1)) * -1) + "px"
			}, 500);
			
			if (this.Slider)
				this.Slider.slider("value", this.CurrentPage);
				
			this.Elements.PageNumber.html(page);
		},
		
		/**
		 * Adds a page of images to the selctor. Can be called from AJAX call or from the page itself
		 * (coldfusion). It will eaither use a set of already created elements, or create new ones if
		 * they don't exist - including the event handelrs for the img tags
		 * @param {Object} page
		 * The page index, starting at 1, of the page these images go in
		 * @param {Object} images
		 * An array of images. They have the following type:
		 * images[0] = {url, name, description}
		 */
		addImages : function(page, images)
		{
			if (!this.Loaded)
			{
				this.ToAdd.push({"page" : page, "images" : images});
				return;
			}
			
			// first, see if the HTML already exists for this set
			var ele = this.Elements.ImgSelect;
			var find = $("#" + this.Options.image_select_bar + " #ImagePage" + page);
			var imagePageEle = null;
			
			if (!find[0])
			{				
				// nopers, we have to create it
				var newImages = 
					$(document.createElement("div"))
						.attr("class", "ImagePage")
						.attr("id", "ImagePage" + page)
						.css("width", (
							(this.DispOptions.per_row * this.DispOptions.cell_padding) +
							(this.DispOptions.per_row * this.DispOptions.wid)
						) + "px");
						
				// add the rows...
				for (var i = 0; i < images.length; i++)
				{
					var newRow =
						$(document.createElement("div"))
							.attr("class", "ImageWrapper")
							.attr("id", "ImageP" + page + "I" + (i + 1));
					
					var newImg =
						$(document.createElement("img"))
							.attr("class", "GalleryImage")
							.attr("src", images[i].thumb)
							.attr("width", this.DispOptions.wid);
							
					if (this.DispOptions.hei)
					{
						newImg.attr("height", this.DispOptions.hei);
					}
							
					if (i && !(i % this.DispOptions.per_row))
						newRow.css("clear", "both");
							
					newRow.append(newImg);
					newImages.append(newRow);
				}
				
				// and add the PAGE
				ele.append(newImages);
				imagePageEle = newImages;
			}
			else
				imagePageEle = find[0];
			
			// now add all of the images
			var imageData = null;

			if (this.ImagePages.length >= (page - 1)) // < damn 1-bassed indexing
			{
				// we have to add this to our internal structure...
				imageData = {
					ImageData		: images,
					ImageRow		: $("#" + this.Options.image_select_bar + " #ImagePage" + page)[0],			
					ImageWrappers	: []
				};
				
				for (var i = 0; i < images.length; i++)
				{
					var imageEle = $("#ImageP" + page + "I" + (i + 1));
					imageEle = imageEle[0];
					
					imageEle.Gallery = this;
					
					imageData.ImageWrappers.push({
						Element	: imageEle
					});
					
					$(imageEle).click(function(){
						if (this.Gallery.CurrentImage == this.ImageData.index
							&& this.Gallery.CurrentPage == (this.ImageData.page + 1))
							return false;
						this.Gallery.switchImage(this.ImageData.page, this.ImageData.index);
						return false;
					});
					
					var that = this;
					
					$(imageEle).mouseover(function(){
						var me = $(this);
						if (that.ZoomEle)
						{
							var meLeft = me.offset().left;
							var meTop = me.offset().top;
							var meWid = me.width();
							var meHei = me.height();
							
							that.ZoomEle.stop();
							that.ZoomEle.data("imageEle", me);
							that.ZoomEle.css("display", "block");
							
							if (that.ZoomEle.Status != "closed") 
							{
								that.ZoomEle.animate({
									left: (meLeft - ((meWid * 0.3) / 2)) + "px",
									top: (meTop - ((meHei * 0.3) / 2)) + "px",
									width: (meWid + (meWid * 0.3)) + "px",
									height: (meHei + (meHei * 0.3)) + "px",
									opacity : 1
								}, 200, null, function(){
									that.ZoomEle.attr("src", me.find("img")[0].src);									
								});
							}
							else 
							{
								that.ZoomEle.attr("src", me.find("img")[0].src);
								that.ZoomEle.css("left", meLeft + "px");
								that.ZoomEle.css("top", meTop + "px");
								that.ZoomEle.css("width", meWid + "px");
								that.ZoomEle.css("height", meHei + "px");
								that.ZoomEle.animate({
									left: (meLeft - ((meWid * 0.3) / 2)) + "px",
									top: (meTop - ((meHei * 0.3) / 2)) + "px",
									width: (meWid + (meWid * 0.3)) + "px",
									height: (meHei + (meHei * 0.3)) + "px",
									opacity : 1
								}, 200);
							}
							
							that.ZoomEle.Status = "open";
							that.ZoomEle.MouseOver = true;
						}
					});
					$(imageEle).hover(function(){
						var me = $(this);
						me.addClass("ImageOver");												
					}, function(){
						$(this).removeClass("ImageOver");
					});
				}
				
				this.ImagePages.push(imageData);
			}
			else
				imageData = this.ImagePages[page];
				
			// whohooo now we can add/edit the images
			for (var i = 0; i < images.length; i++)
			{
				images[i].page = page;
				images[i].index = i + 1;
				
				imageData.ImageWrappers[i].Data = images[i];
				imageData.ImageWrappers[i].Element.ImageData = images[i];
				
				var img = $("#ImageP" + page + "I" + (i + 1) + " img");

				var find = img;
				
				if (!find)
					alert("WTF?!");
					
				find.attr("src", images[i].thumb);
			}
			
			// Now we need to set the width of the inner image select to contain all pages
			this.Elements.ImgSelect.css("width", 
				(this.ImagePages.length * 
					((this.DispOptions.per_row * this.DispOptions.cell_padding) +
					(this.DispOptions.per_row * this.DispOptions.wid))
				+ "px"));
			
			// if we have more then one page, show the buttons
			if (this.ImagePages.length <= 1)
			{
				this.Elements.NextPage.css("display", "none");
				this.Elements.PrevPage.css("display", "none");
			}
			else
			{
				this.Elements.NextPage.css("display", "block");
				this.Elements.PrevPage.css("display", "block");
			}
			
			this.Elements.PageCount.html(this.ImagePages.length);
		},
		
		/**
		 * Called once after init and has to be called if you change the Image collection
		 * after init.
		 */
		resetSlider : function()
		{
			/**
			 * Create/Update our slider
			 */
			if (!this.Slider && this.ImagePages.length > 1 && this.Elements.Slider) 
			{
				var that = this;
				$('#PagingWrapper').show();
        		this.Elements.Slider.slider({
					value		: this.CurrentPage,
					min			: 1,
					max			: this.ImagePages.length,
					slide		: function(event, ui){
						that.switchPage(ui.value);
					}
				});
				
				this.Slider = this.Elements.Slider;
			}
		},
		
		/**
		 * If zoom is enabled, this will init it
		 */
		resetZoom : function()
		{
			if (this.ZoomOptions)
			{
				var zoomEle = document.createElement("img");
				document.body.appendChild(zoomEle);
				
				zoom = $(zoomEle);
				zoom.css("cursor", "pointer");
				zoom.css("display", "block");
				zoom.css("position", "absolute");
				zoom.css("display", "none");
				zoom.css("border", "1px solid #000");
				zoom.css("z-index", 2000);
				
				zoom.click(function(){
					var ele = $(this).data("imageEle");
					if (ele)
					{
						ele.click();
					}
				});
				this.ZoomEle = zoom;
				this.ZoomEle.Status = "closed";
				this.ZoomEle.MouseOver = false;
				
				var that = this;
				$(this.ZoomEle).mouseout(function(){
					that.ZoomEle.MouseOver = false;
				});
				
				$(this.ZoomEle).mouseover(function(){
					that.ZoomEle.MouseOver = true;
				});
				
				$(document.body).mousemove(function(){
					if (!that.ZoomEle.MouseOver)
					{
						that.ZoomEle.Status = "closing";
						window.setTimeout(function(){
							if (that.ZoomEle.Status == "closing")
							{
								that.ZoomEle.animate({opacity : 0}, 200, null, function(){
									that.ZoomEle.css("display", "none");
								});
								that.ZoomEle.Status = "closed";
							}
						}, 200);
					}
				});
			}	
		}
		
	});
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
