/**
 * jQuery Grid Style Gallery
 * Copyright (c) 2011 Allan Ma (http://codecanyon.net/user/webtako)
 * Version: 1.1.1 (04/12/2011)
 */
;(function($) {
	$.fn.wtGridGallery = function(params) {		
		var PREV = "previous";
		var NEXT = "next";
		var TIP = "tip";
		var SLIDE_OPACITY = 0.7;
		var DISABLE_OPACITY = 0.5;
		var DEFAULT_DELAY = 5000;
		var TOOLTIP_DELAY = 800;
		var DEFAULT_SPEED = 700;
		var TEXT_SPEED = 300;
		var ANIMATE_SPEED = 400;
		
		var UPDATE_ADV_BUTTONS = "update_adv_buttons";
		var UPDATE_BUTTONS = "update_buttons";
		var UPDATE_GALLERY_INFO = "update_gallery_info";
		var UPDATE_LABEL = "update_label";
		var UPDATE_SCROLLBAR = "update_scrollbar";
		var SWITCH_LIST = "switch_list";
		var CHECK_SINGLE = "check_single";
		var UPDATE_MENU_BUTTONS = "update_menu_buttons";
		var UPDATE_MENU = "update_menu";
		
		//Layer
		function Layer(gallery, areaWidth, areaHeight, duration) {
			var $layer;
			
			//init layer
			var init = function() {
				gallery.addToScreen("<div class='layer'/>");
				$layer = gallery.$el.find(".layer").css("background-color", gallery.bgColor());
			}

			//display previous
			this.displayPrevious = function($currImg, $prevImg, vertical) {
				setLayer($prevImg, areaWidth, areaHeight);
				var props = vertical ? {height:0} : {width:0};
				animate($currImg, props, true);
			}			
			
			//display next
			this.displayNext = function($currImg, vertical) {
				if (vertical) {
					setLayer($currImg, areaWidth, 0);
					animate($currImg, {height:areaHeight}, false);
				}
				else {
					setLayer($currImg, 0, areaHeight);
					animate($currImg, {width:areaWidth}, false);
				}				
			}	
			
			//set layer
			var setLayer = function($layerImg, width, height) {
				$layer.empty().css({width:width, height:height});
				if ($layerImg) {
					$layerImg.clone().appendTo($layer).show();
				}
				$layer.show();
			}
			
			//animate layer
			var animate = function($currImg, props, displayFirst) {
				if (displayFirst) {
					gallery.displayImgFirst($currImg);
				}
				$layer.stop(true).animate(props, duration, gallery.easing(),
					function() {
						gallery.setComplete($currImg);
						$layer.hide();
					}
				);
			}
			
			init();
		}
		
		//Gallery
		function GridGallery($obj, opts) {
			var numCols = 		getPosNumber(opts.num_col, 3);
			var numRows = 		getPosNumber(opts.num_row, 2);
			var screenWidth = 	getPosNumber(opts.screen_width, 900);
			var screenHeight = 	getPosNumber(opts.screen_height, 240);
			var itemWidth = 	getPosNumber(opts.item_width, 75);
			var itemHeight = 	getPosNumber(opts.item_height, 75);
			var itemMargin = 	getNonNegNumber(opts.item_margin, 8);
			var displayNumInfo = 	opts.display_number;
			var displayPlayButton =	opts.display_playbutton;
			var displayDButtons = 	opts.display_dbuttons;
			var displayAdvButtons = opts.display_advbuttons;
			var displayTextButton =	opts.display_textbutton;
			var displayDlButton =	opts.display_downloadbutton;
			var displayTip =		opts.display_tooltip;
			var displayCaption = 	opts.display_caption;
			var displayTimer =		opts.display_timer;
			var displayGalleryInfo =opts.display_gallery_info;
			var effect =		opts.effect.toLowerCase();
			var transSpeed =   	getPosNumber(opts.transition_speed, DEFAULT_SPEED);
			var textOn =		opts.text_on;
			var textEffect = 	opts.text_effect.toLowerCase();
			var textSync =		opts.text_sync;
			var cpFloat =		opts.cpanel_float;
			var cpMouseover = 	opts.cpanel_mouseover;
			var autoScale = 	opts.auto_scale;
			var contNav  = 		opts.cont_nav;
			var autoRotate = 	opts.auto_rotate;
			var delay = 		getPosNumber(opts.delay, DEFAULT_DELAY);
			var initialOpen = 	opts.initial_open;
			var catIndex = 		getPosNumber(opts.selected_category, 1) - 1;
			var currIndex = 	getPosNumber(opts.selected_image, 1) - 1;
			var easing = 		opts.easing;
			var noCategory = 	opts.no_category;
			
			var $slider;
			var $screen;
			var $preloader;
			var $textBox;
			var $mainLink;
			var $cpanel;
			var $prevButton;
			var $nextButton;
			var $rewButton;
			var $ffButton;
			var $playButton;
			var $textButton;
			var $overlay;
			var $gallery;
			var $galleryPanel;
			var $lists;
			var $listItems;
			var $selectedList;
			var $currItems;
			var $strips;
			var $menu;
			var $menuItems;
			var $captionTip;
			var $scrollbar;
			var $thumb;
			var $textScrollbar;
			var $textThumb;
			var $outerText;
			var $innerText;
			var $timer;
			
			var numItems;
			var unitSize;
			var listPos;
			var timerId;
			var rotate;
			var prevIndex;
			var dir;
			var scrollRange;
			var listRatio;
			var displayScrollbar;
			var textPadding;
			var textRange;
			var textScrollRange;
			var textRatio;
			var prevPct;
			var layer;
			this.$el = $obj;
			
			//initialize
			this.init = function() {
				$slider = $obj.find(".grid-gallery");
				$screen = $slider.find(".screen").css("visibility","hidden");
				$galleryPanel = $slider.find(".items");
				$lists = $galleryPanel.find(">ul");
				if (noCategory) {
					setNoCategory();
				}
				$listItems = $lists.find(">li");
				dir = NEXT;
				prevPct = -1;
				prevIndex = -1;
				timerId = null;
			
				rotate = autoRotate;
				if (catIndex >= $lists.size()) {
					catIndex = 0;
				}
				$selectedList = $($lists.get(catIndex));
				if (currIndex >= $selectedList.find(">li").size()) {
					currIndex = 0;
				}
				listPos = 0;
				
				initScreen();
				initItems();
				initGalleryPanel();
				$slider.css({width:screenWidth, height:screenHeight});
				
				setCategory(catIndex);
				$slider.bind(SWITCH_LIST, switchList);
				$screen.css("visibility", "visible");
				
				if (effect == "h_wipe" || effect == "v_wipe") {
					layer = new Layer(this, screenWidth, screenHeight, transSpeed);
				}
				
				if (initialOpen) {
					$cpanel.css({top:$cpanel.data("offset")});
					openGallery();
				}				
				
				loadImg(0);
				loadContent(currIndex);
			}
			
			//set complete
			this.setComplete = function($img) {
				showContent($img);
			}
			
			//display image first
			this.displayImgFirst = function($img) {
				var $currStrip = $($strips.get(catIndex));
				$currStrip.find(".content").removeAttr("id").hide();
				$img.attr("id", "curr-img").show();
			}
			
			//add to screen
			this.addToScreen = function(content) {
				$screen.append(content);
			}
			
			//get auto scale
			this.autoScale = function() {
				return autoScale;
			}
			
			//get easing
			this.easing = function() {
				return easing;
			}
			
			//get background color
			this.bgColor = function() {
				return $slider.css("background-color");
			}
			
			//init screen
			var initScreen = function() {
				var content =  "<div id='timer'/>\
				                <div id='preloader'/>\
								<div id='textbox'>\
							   		<div class='inner-bg'/>\
									<div id='box'>\
										<div class='outer-text'><div class='inner-text'/></div>\
										<div class='text-scrollbar'><div class='text-thumb'/></div>\
									</div>\
							   	</div>\
							   	<div id='cpanel'>\
									<div class='btn'><span class='rewind'/></div>\
									<div class='btn'><span class='prev'/></div>\
									<div class='btn'><span class='play'/></div>\
									<div class='btn'><span class='next'/></div>\
									<div class='btn'><span class='ffwd'/></div>\
									<span id='num-info'/>\
									<div class='btn'><span class='text'/></div>\
									<div class='btn'><span class='download'/></div>\
									<div class='btn'><span class='grid'/></div>\
								</div>";
				for (var i = 0; i < $lists.size(); i++) {
					content += "<span class='strip'/>";
				}
				
				$screen.css({width:screenWidth, height:screenHeight}).append(content);
				$strips = $screen.find(".strip");
				if (effect == "h_slide" || effect == "slide") {
					effect = "h_slide";
					$strips.css({width:2*screenWidth, height:screenHeight});
				}
				else if (effect == "v_slide") {
					$strips.css({width:screenWidth, height:2*screenHeight});
				}
				else {
					$strips.css({width:screenWidth, height:screenHeight}).wrapAll("<a href='#'></a>");
					$mainLink = $strips.parent();
				}
				$strips.css("top", -$strips.height());
				$preloader 	= $screen.find("#preloader");
				$timer =	  $screen.find("#timer").data("pct", 1);
				if (!displayTimer) {
					$timer.css("visibility","hidden");
				}
				
				initTextBox();
				initCPanel();
			}
			
			//init text box
			var initTextBox = function() {
				$textBox = 	$screen.find("#textbox");
				var $container = $textBox.find("#box");
				$outerText = $textBox.find(".outer-text");
				$innerText = $outerText.find(".inner-text");
				textPadding = $container.outerHeight() - $container.height();
				
				$textScrollbar = $textBox.find(".text-scrollbar");
				$textThumb = $textScrollbar.find(".text-thumb").click(preventDefault);
				$textScrollbar.data("width", $textScrollbar.outerWidth(true))
							  .click(function(e) { 
										var pct = Math.round(((e.pageY - $textScrollbar.offset().top)/$(this).height()) * 10)/10;
										$innerText.stop(true, true).animate({top:-Math.round(pct * textRange)}, ANIMATE_SPEED);
										$textThumb.stop(true, true).animate( {top: Math.round(pct * ($(this).height() - $textThumb.height()))}, ANIMATE_SPEED);
									});
				try {
					$textThumb.draggable({containment: "parent"})
						 	 .bind("drag", function() { $innerText.css({top: Math.round(-$(this).position().top * textRatio)}); });
				}
				catch (ex) { 
					//not draggable. 
				}
			}
			
			//init control panel
			var initCPanel = function() {				
				$cpanel = $screen.find("#cpanel");
				if (cpFloat) {
					$cpanel.addClass("float");
				}
				initButtons();
				initNumInfo();
				if (cpFloat) {
					$cpanel.css("margin-left",-$cpanel.outerWidth()/2);
				}
				
				$cpanel.data({offset:screenHeight, pos:screenHeight - $cpanel.outerHeight(true)});
				if (jQuery.browser.msie && parseInt(jQuery.browser.version)  < 8) {
					$cpanel.addClass("bg");
				}
				
				if (cpMouseover) {
					$cpanel.css("top", $cpanel.data("offset"));
					$screen.hover(showCPanel,hideCPanel);
				}
				else {
					$cpanel.css("top", $cpanel.data("pos"));
				}
			}
			
			//init number info
			var initNumInfo = function() {
				var $numInfo = $cpanel.find("#num-info");
				if (displayNumInfo) {
					if (cpFloat) {						
						var arr = $lists.map(function() { return $(this).find(">li").size(); }).get();
						arr.sort(sortDescending);
						var digits = getNumDigits(arr[0]);
						var str = "";
						for (var i = 0; i < digits; i++) {
							str += "0";
						}
						str += " / " + str
						$numInfo.html(str).width($numInfo.width()).html("");
					}
					else {
						$numInfo.css("left",-Math.floor($numInfo.width()/2));
					}
					$slider.bind(UPDATE_LABEL, function() { $numInfo.html((currIndex + 1) + " / " + numItems); });
				}
				else {
					$numInfo.hide();
				}
			}
			
			//init buttons
			var initButtons = function() {
				var size = $cpanel.height();
				var $buttons = $cpanel.find(">div").hover(buttonOver, buttonOut);
				$prevButton = 	$buttons.has(".prev");
				$nextButton =  	$buttons.has(".next");
				$playButton = 	$buttons.has(".play");
				$rewButton = 	$buttons.has(".rewind");
				$ffButton = 	$buttons.has(".ffwd");
				$textButton = 	$buttons.has(".text");
				var $dlButton = $buttons.has(".download");
				var $gridButton = $buttons.has(".grid");
				if (displayAdvButtons) {
					$rewButton.click(goFirst).find(":only-child").css("backgroundPosition", -size + "px " + "0");
					$ffButton.click(goLast).find(":only-child").css("backgroundPosition", -size + "px " + -size + "px");
					$slider.bind(UPDATE_ADV_BUTTONS, updateAdvButtons);
				}
				else {
					$rewButton.hide();
					$ffButton.hide();
				}
				
				if (displayDButtons) {
					$prevButton.click(goPrev).find(":only-child").css("backgroundPosition", "0 0");
					$nextButton.click(goNext).find(":only-child").css("backgroundPosition", "0 " + -size + "px");
					if (!contNav) {
						$slider.bind(UPDATE_BUTTONS, updateDButtons);
					}
				}
				else {
					$prevButton.hide();
					$nextButton.hide();
				}
				
				if (displayPlayButton) {
					$playButton.click(togglePlay);
					if (autoRotate) {
						$playButton.find(":only-child").addClass("pause");
					}
				}
				else {
					$playButton.hide();
				}
				
				if (displayTextButton) {
					$textButton.click(toggleText);
					if (!textOn) {
						$textButton.find(":only-child").addClass("off");
					}
				}
				else {
					$textButton.hide();
				}
				
				if (displayDlButton) {
					$dlButton.click(function () { window.open($($currItems.get(currIndex)).find(">a:first").attr("href"), "_blank"); } )
							 .find(":only-child").css("backgroundPosition", -2 * size + "px " + -size + "px");
				}
				else {
					$dlButton.hide();
				}
				
				$gridButton.click(openGallery).find(":only-child").css("backgroundPosition", -2 * size + "px 0");
				
				if (!cpFloat) {
					$textButton.css("float", "right");
					$dlButton.css("float", "right");
					$gridButton.css("float", "right");
					$textButton.after($gridButton);
					$dlButton.after($textButton);
				}
					
				if (displayTip) {
					$textButton.data(TIP, "info");
					$gridButton.data(TIP, "gallery");
					$playButton.data(TIP, autoRotate ? "pause" : "play");
					$prevButton.data(TIP, PREV);
					$nextButton.data(TIP, NEXT);
					$dlButton.data(TIP, "get content");
					$rewButton.data(TIP, "first");
					$ffButton.data(TIP, "last");
					
					$buttons.each(function(n) {
						$(this).attr("title", $(this).data(TIP));   
					});
				}
			}
			
			//update d-buttons
			var updateDButtons = function() {
				currIndex == 0 ? disableButton($prevButton, false) : enableButton($prevButton, null);
				currIndex == numItems - 1 ? disableButton($nextButton, false) : enableButton($nextButton, null);
			}
			
			//update advance buttons
			var updateAdvButtons = function() {
				currIndex == 0 ? disableButton($rewButton, false) : enableButton($rewButton, null);
				currIndex == numItems - 1 ? disableButton($ffButton, false) : enableButton($ffButton, null);
			}
			
			//button over
			var buttonOver = function() {
				$(this).addClass("button-hl");
			}
			
			//button out
			var buttonOut = function() {
				$(this).removeClass("button-hl");
			}
			
			//disable button
			var disableButton = function($button, unbindClick) {
				$button.css("cursor","default").removeAttr("title").unbind("mouseenter").unbind("mouseleave");
				$button.find(":only-child").css("opacity",DISABLE_OPACITY);
				$button.removeClass("button-hl");
				if (unbindClick) {
					$button.unbind("click");
				}
			}
			
			//enable button
			var enableButton = function($button, clickFn) {
				var $icon = $button.find(":only-child");
				if ($icon.css("opacity") < 1) {
					$button.css("cursor","pointer").unbind("mouseenter").unbind("mouseleave").hover(buttonOver, buttonOut);
					$icon.css("opacity",1);
					if (displayTip) {
						$button.attr("title", $button.data(TIP));
					}
					if (clickFn) {
						$button.unbind("click").click(clickFn);
					}
				}
			}
			
			//show cpanel
			var showCPanel = function() {
				$cpanel.stop(true).animate({top:$cpanel.data("pos"), opacity:1}, ANIMATE_SPEED);
			}
			
			//hide cpanel
			var hideCPanel = function() {
				$cpanel.stop(true).animate({top:$cpanel.data("offset"), opacity:0}, ANIMATE_SPEED);
			}
			
			//init text data
			var initTextData = function($item) {				
				var $p = $item.find(">div:hidden");
				var textWidth =  getPosNumber(parseInt($p.css("width")) - textPadding, 300);
				var textHeight = parseInt($p.css("height"));
				$innerText.width(textWidth).html($p.html());

				var useScrollbar = false;
				var txtRange = 1;
				if (isNaN(textHeight) || textHeight - textPadding < 1) {
					textHeight = $innerText.height();
				}
				else {
					textHeight -= textPadding;
					if (textHeight < $innerText.height()) {
						useScrollbar = true;
						$innerText.width(textWidth - $textScrollbar.data("width")).html($p.html()); 
						txtRange = $innerText.height() - textHeight;
					}
				}
				
				$item.data("textbox", {color:$p.css("color"), bgcolor:$p.css("background-color"), x:$p.css("left"), y:$p.css("top"), 
										w:textWidth + textPadding, h:textHeight + textPadding, areaW:textWidth, areaH:textHeight,
										scroll:useScrollbar, "totalHeight":$innerText.height(), range:txtRange});
			}
			
			//update text box
			var updateText = function() {
				if (!$textBox.data("visible")) {
					var $item = $($currItems.get(currIndex));
					var text = $item.find(">div:hidden").html();
					if (text && text.length > 0) {
						if (textOn) {
							$textBox.data("visible", true);
							var data = $item.data("textbox");
							$innerText.css("color",data.color);
							$textBox.find(".inner-bg").css({"background-color":data.bgcolor, height:data.h});
							var textHeight = data.h+1;
							switch(textEffect) {
								case "fade":
									fadeInText(text, data);
									break;
								case "down":
									expandText(text, data, {width:data.w, height:0}, {height:textHeight});
									break;
								case "right":
									expandText(text, data, {width:0, height:textHeight}, {width:data.w});
									break;
								case "up":
									expandText(text, data, {"margin-top":textHeight, height:0, width:data.w}, {height:textHeight, "margin-top":0});
									break;
								case "left":
									expandText(text, data, {"margin-left":data.w, width:0, height:textHeight}, {width:data.w, "margin-left":0});
									break;
								default:
									showText(text, data);
							}
						}
						
						enableButton($textButton, toggleText);
					}
					else {
						disableButton($textButton, true);
					}
				}
			}
			
			//reset text box
			var resetText = function() {
				$textBox.data("visible", false).stop(true, true);
				$textScrollbar.hide();
				switch(textEffect) {
					case "fade":
						if (jQuery.browser.msie) {
							$innerText.css("opacity",0);
						}
						$textBox.fadeOut(TEXT_SPEED, function() { $(this).css("display", "none"); });
						break;
					case "down":
						$innerText.html("");
						$textBox.animate({height:0, "margin-top":$textBox.outerHeight()}, TEXT_SPEED);
						break;
					case "right":
						$innerText.html("");
						$textBox.animate({width:0, "margin-left":$textBox.outerWidth()}, TEXT_SPEED);
						break;
					case "left":
						$innerText.html("");
						$textBox.animate({width:0}, TEXT_SPEED);
						break;
					case "up":
						$innerText.html("");
						$textBox.animate({height:0}, TEXT_SPEED);
						break;
					default:
						$textBox.css("display", "none");
				}
			}
			
			//set text scrolling
			var setTextScroll = function(data) {
				$innerText.stop(true).css("top",0);
				var areaWidth =  data.areaW;
				var areaHeight = data.areaH;
				if (data.scroll) {
					var thumbSize = Math.ceil((areaHeight/data.totalHeight) * areaHeight);
					$textThumb.stop(true).css({top:0, height:thumbSize});
					$textScrollbar.height(areaHeight);
					$outerText.css({width:areaWidth - $textScrollbar.data("width"), height:areaHeight});
					
					textRange = data.range;
					textScrollRange = areaHeight - thumbSize;
					textRatio = textRange/textScrollRange;
				}
				else {
					$outerText.css({width:areaWidth, height:areaHeight});
				}
				$textScrollbar.hide();
			}
			
			//fade in text effect
			var fadeInText = function(text, data) {
				setTextScroll(data);
				if (data.scroll) {
							$textScrollbar.show();
						}
				$innerText.css("opacity",1).html(text);
				$textBox.css({top:data.y, left:data.x, width:data.w, height:data.h+1})
						.stop(true, true).fadeIn(TEXT_SPEED, function() {
																	if (jQuery.browser.msie) {
																		$innerText[0].style.removeAttribute('filter'); 
																	}
																}); 
			}
			
			//expand text effect
			var expandText = function(text, data, props1, props2) {
				setTextScroll(data);
				$innerText.html("");
				$textBox.stop(true).css({display:"block", top:data.y, left:data.x, "margin-top":0, "margin-left":0}).css(props1).animate(props2, TEXT_SPEED, 
					function () {  
						$innerText.html(text);
						if (data.scroll) {
							$textScrollbar.show();
						}
					});  
			}
			
			//show text effect
			var showText = function(text, data) {
				setTextScroll(data);
				if (data.scroll) {
					$textScrollbar.show();
				}
				$textBox.stop(true).css({display:"block", top:data.y, left:data.x, width:data.w, height:data.h+1});  
				$innerText.html(text);
			}
			
			//init caption
			var initCaptionTip = function() {
				$("body").append("<div id='gg-caption'><div class='tt-txt'/></div>");
				$captionTip = $("body").find("#gg-caption");
				if (jQuery.browser.msie && parseInt(jQuery.browser.version) <= 6) {
					$captionTip.css("background-image", "none").find(":only-child").css("margin",0);
				}
			}
			
			//show caption
			var showCaption = function(e) {
				$captionTip.find(">div.tt-txt").html($(this).data("caption"));
				$captionTip.css({top:e.pageY, left:e.pageX}).stop(true, true).delay(TOOLTIP_DELAY).fadeIn("fast");
			}
			
			//caption move
			var moveCaption = function(e) {
				$captionTip.css({top:e.pageY, left:e.pageX});
			}
			
			//hide caption
			var hideCaption = function() {
				$captionTip.stop(true, true).fadeOut(0);
			}
			
			//init items
			var initItems = function() {
				$listItems.css({width:itemWidth, height:itemHeight, "margin-right":itemMargin, "margin-bottom":itemMargin});
				unitSize = $listItems.outerHeight(true);
				
				$listItems.each(
					function(n) {
						var $link = $(this).find(">a:first");
						var caption = $link.find(">img").attr("alt");
						$(this).data({imgurl:$link.attr("href"), caption:caption});
						initTextData($(this));
						if (displayCaption && caption != "") {
							$(this).hover(showCaption, hideCaption).mousemove(moveCaption);
						}
					}
				).hover(itemMouseover,itemMouseout);
				$innerText.html("").css({width:"auto", height:"auto"});
				$textBox.css("visibility", "visible");
				
				if (displayCaption) {
					$listItems.find("img").removeAttr("alt");
					initCaptionTip();
				}
				
				$itemFrame = $("<span id='item-frame'/>").css({width:itemWidth - 6, height:itemHeight - 6});
			}
			
			//init gallery panel
			var initGalleryPanel = function() {
				var width =  numCols * $listItems.outerWidth(true);
				var height = numRows * $listItems.outerHeight(true);
				$galleryPanel.css({width:width - itemMargin, height:height - itemMargin}).click(selectItem);
				var single = false;
				displayScrollbar = false;
				$lists.each(
					function(n) {
						var num = $(this).find(">li").size();
						if (num < 2) {
							single = true;
						}
						var rows = Math.ceil(num/numCols);
						var scroll = (rows > numRows);
						if (scroll) {
							displayScrollbar = true;
						}
						var listHeight = (rows * unitSize) - itemMargin;
						$(this).css({width:width}).data({numItems:num, numRows:rows, range:listHeight - $galleryPanel.height(), scroll:scroll, pos:0});
					});
				if (single) {
					$slider.bind(CHECK_SINGLE, checkSingle);
				}
				initFrame();
			}
			
			//init item frame
			var initFrame = function() {
				$slider.append("<div id='overlay'></div>");
				$overlay = $slider.find("#overlay").click(closeGallery);
				
				$galleryPanel.wrap("<div id='gallery'><div id='gallery-box'><div id='gallery-frame'/></div></div>");
				$gallery = $slider.find("#gallery");
				$gallery.append("<div id='close-btn'/>");
				var $closeButton = $gallery.find("#close-btn").click(closeGallery);
				if (displayTip) {
					$closeButton.attr("title", "close");
				}
				
				var $galleryFrame = $gallery.find("#gallery-frame").mousedown(preventDefault);
				initScrollbar($galleryFrame);
				initMenu($galleryFrame);
				initGalleryInfo($galleryFrame);
				$gallery.data({top:(screenHeight - $gallery.outerHeight())/2, left:(screenWidth - $gallery.outerWidth())/2});
			}
			
			//init gallery info
			var initGalleryInfo = function($frame) {
				if (displayGalleryInfo) {
					$frame.append("<div id='gallery-info'/>");
					var $galleryInfo = $frame.find("#gallery-info");
					$slider.bind(UPDATE_GALLERY_INFO,  
						function() {
							var total = $selectedList.data("numItems");
							if (total > 0) {
								var index = -(listPos/unitSize);
								var beg = index * numCols;
								var end = Math.min(beg + (numCols * numRows), total);
								$galleryInfo.html((beg + 1) + "-" + end + " of " + total);
							}
							else {
								$galleryInfo.html("");
							}
						});
				}
			}
			
			//init scrollbar
			var initScrollbar = function($frame) {
				if (displayScrollbar) {
					$frame.append("<div class='scrollbar'><div class='thumb'/></div>");
					$scrollbar = $frame.find(".scrollbar");
					$scrollbar.height($galleryPanel.height()).data("height", $scrollbar.height()).click(trackClick);
					$thumb = $scrollbar.find(".thumb").click(preventDefault);
					$frame.width($galleryPanel.outerWidth() + $scrollbar.outerWidth(true));
					
					try {
						$thumb.draggable({containment: "parent"})
							 .bind("drag", function() { $selectedList.css({top: Math.round(-$(this).position().top * listRatio)}); })
							 .bind("dragstop", function() { autoStop($(this).position().top/scrollRange); });
					}
					catch (ex) { 
						//not draggable. 
					}
					$slider.bind(UPDATE_SCROLLBAR, 
								function() {
									$thumb.stop(true, true).animate({top: Math.round(-listPos * (1/listRatio))}, ANIMATE_SPEED); 
								});
				}
			}
			
			//on track click
			var trackClick = function(e) {
				autoStop((e.pageY - $scrollbar.offset().top)/$(this).height());
			}
			
			//init menu
			var initMenu = function($frame) {
				if ($lists.size() > 1) {
					var content = "<div id='menu-bar'><ul id='menu'>";
					$lists.each(function(n) {
						content += "<li>" + $(this).attr("title") + "</li>";
					});
					content += "</ul></div>";
					$gallery.find("#gallery-box").prepend(content);
					
					$menu = $gallery.find("#menu");
					$menuItems = $menu.find(">li");
					$menuItems.hover(function() { $(this).addClass("menu-over"); },
									 function() { $(this).removeClass("menu-over"); })				
							  .click(function() {
											var i = $(this).index();
											if (i != $menuItems.filter(".selected").index()) {
												$slider.trigger(SWITCH_LIST, i);
											}
										});
					
					var arr = $menuItems.map(function() { return $(this).width(); }).get();
					arr.sort(sortDescending);
					$menuItems.width(arr[0]);
					var menuItemWidth = $menuItems.outerWidth();
					$menu.width($menuItems.size() * menuItemWidth);
					var $menuBar = $gallery.find("#menu-bar");
					if ($menu.outerWidth() > $frame.outerWidth()) {
						$menu.wrap("<div id='menu-panel'/>");
						var $menuBar = $menuBar.append("<div id='menu-buttons'><div id='menu-back'/><div id='menu-fwd'/></div>");
						var $menuPanel = $gallery.find("#menu-panel");
						var $menuButtons = $menuBar.find("#menu-buttons");
						var numMenuItemDisplay = Math.ceil(($frame.outerWidth() - $menuButtons.outerWidth())/menuItemWidth);
						$menuPanel.width(numMenuItemDisplay * menuItemWidth);
						if (numMenuItemDisplay < $menuItems.size()) {
							initScrollMenu(numMenuItemDisplay, menuItemWidth);
						}
						else {
							$menuButtons.width(0).children().remove();
						}
						
						var menuBarWidth = $menuPanel.width() + $menuButtons.outerWidth();
						if (menuBarWidth > $frame.outerWidth()) {
							$menuBar.width(menuBarWidth);
							var lPad = Math.floor((menuBarWidth - $frame.width())/2);
							var rPad = menuBarWidth - (lPad + $frame.width());
							$frame.css({"padding-left":lPad, "padding-right":rPad});
						}
						else {
							$menuBar.width($frame.outerWidth());
						}
					}
					else {
						$menu.width($frame.outerWidth() - ($menu.outerWidth() - $menu.width()));
						$menuBar.width($frame.outerWidth());
					}				
					$frame.addClass("square-upper");
				}
				else {
					$gallery.css("padding", Math.round($gallery.find("#close-btn").height()/2));
				}
				$lists.removeAttr("title");
			}
			
			//init scrolling menu
			var initScrollMenu = function(numMenuItemDisplay, menuItemWidth) {
				var $menuBackBtn = $gallery.find("#menu-back");
				var $menuFwdBtn =  $gallery.find("#menu-fwd");
				var menuSlots = $menuItems.size() - numMenuItemDisplay;
				var prevSlots = 0;
				var nextSlots = menuSlots;
				
				$slider.bind(UPDATE_MENU_BUTTONS, function() {
					$menuBackBtn.toggleClass("off", nextSlots == menuSlots);
					$menuFwdBtn.toggleClass("off", prevSlots == menuSlots);
				}).bind(UPDATE_MENU, function(e, i) {
					var slots = Math.min(i, menuSlots);
					prevSlots = slots;
					nextSlots = menuSlots - slots;
					$menu.css("left",-prevSlots * menuItemWidth);
					$slider.trigger(UPDATE_MENU_BUTTONS);
				});
				
				$menuBackBtn.click(function() {
					if (nextSlots < menuSlots) {
						var slots = Math.min(menuSlots - nextSlots, numMenuItemDisplay);
						nextSlots += slots;
						prevSlots -= slots;
						$menu.animate({left:-prevSlots * menuItemWidth}, ANIMATE_SPEED);
						$slider.trigger(UPDATE_MENU_BUTTONS);
					}					
				});
				
				$menuFwdBtn.click(function() {
					if (prevSlots < menuSlots) {
						var slots = Math.min(menuSlots - prevSlots, numMenuItemDisplay);
						prevSlots += slots;
						nextSlots -= slots;
						$menu.animate({left:-prevSlots * menuItemWidth}, ANIMATE_SPEED);
						$slider.trigger(UPDATE_MENU_BUTTONS);
					}					
				});
			}
			
			//switch list view/update gallery panel
			var switchList = function(e, i) {
				prevPct = -1;
				try {
					$menuItems.filter(".selected").removeClass("selected");
					$($menuItems.get(i)).addClass("selected");
				}
				catch(ex) {};
				
				$selectedList.stop(true,true).data("pos", listPos);
				$selectedList = $($lists.get(i));
				listPos = $selectedList.data("pos");
				if (displayScrollbar) {					
					if ($selectedList.data("scroll")) {
						var thumbSize = Math.ceil((numRows/$selectedList.data("numRows")) * $scrollbar.data("height"));
						$thumb.height(thumbSize);
						scrollRange =  $scrollbar.data("height") - thumbSize;
						listRatio = $selectedList.data("range")/scrollRange;
						$thumb.stop(true,true).css("top",-listPos * (1/listRatio));
						$scrollbar.css("visibility","visible");
					}
					else {
						$scrollbar.css("visibility","hidden");
					}
				}	
				$lists.not(":eq("+i+")").css("visibility","hidden");
				$selectedList.stop(true,true).css("visibility","visible");
				
				$slider.trigger(UPDATE_GALLERY_INFO);
			}
			
			//set category
			var setCategory = function(i) {
				var $currStrip = $($strips.get(catIndex));
				$currStrip.find(".content").removeAttr("id");
				$currStrip.css("top", -$strips.height());
				
				catIndex = i;
				$currStrip = $($strips.get(catIndex));
				$currItems = $($lists.get(catIndex)).find(">li");
				numItems = $currItems.size();
				$currStrip.css("top", 0);
			
				$slider.trigger(CHECK_SINGLE);
			}
			
			//check for single image
			var checkSingle = function() {
				if (numItems > 1) {
					if (displayDButtons) {
						$prevButton.show();
						$nextButton.show();
					}
					if (displayAdvButtons) {
						$rewButton.show();
						$ffButton.show();
					}					
					if (displayPlayButton) {
						$playButton.show();
					}
				}
				else {					
					$prevButton.hide();
					$nextButton.hide();
					$rewButton.hide();
					$ffButton.hide();
					$playButton.hide();
				}
				
				if (cpFloat) {
					$cpanel.css("margin-left",-$cpanel.outerWidth()/2);
				}
			}
			
			//auto stop
			var autoStop = function(pct) {
				if (pct != prevPct) {
					prevPct = pct;
					var slots;
					var newPos = pct * $selectedList.data("range");
					if (newPos > -listPos) {
						slots = Math.ceil(newPos/unitSize);
					}
					else if (newPos < -listPos) {
						slots = Math.floor(newPos/unitSize);
					}
					else {
						return;
					}
					
					listPos = -slots * unitSize;
					$selectedList.stop(true, true).animate({top:listPos}, ANIMATE_SPEED);
					$slider.trigger(UPDATE_SCROLLBAR).trigger(UPDATE_GALLERY_INFO);
				}
			}
			
			//previous image
			var goPrev = function() {
				if (currIndex > 0) {
					prevIndex = currIndex;
					currIndex--;
				}
				else if (contNav) {
					prevIndex = currIndex;
					currIndex = numItems - 1;
				}
				else {
					return;
				}
				resetTimer();
				dir = PREV;
				loadContent(currIndex);
				return false;
			}
			
			//next image
			var goNext = function() {
				if (currIndex < numItems - 1) {
					prevIndex = currIndex;
					currIndex++;
				}
				else if (contNav) {
					prevIndex = currIndex;
					currIndex = 0;
				}
				else {
					return;
				}
				resetTimer();
				dir = NEXT;
				loadContent(currIndex);
				return false;
			}
			
			//go to first
			var goFirst = function() {
				if (currIndex > 0) {
					prevIndex = currIndex;
					currIndex = 0;
					resetTimer();
					dir = PREV;
					loadContent(currIndex);
				}
				return false;
			}
			
			//go to last
			var goLast = function() {
				if (currIndex < numItems - 1) {
					prevIndex = currIndex;
					currIndex = numItems - 1;
					resetTimer();
					dir = NEXT;
					loadContent(currIndex);
				}
				return false;
			}

			//rotate content
			var rotateImg = function() {
				resetTimer();
				prevIndex = currIndex;
				currIndex = currIndex < numItems - 1 ? currIndex + 1 : 0;
				dir = NEXT;
				loadContent(currIndex);
			}
			
			//play/pause
			var togglePlay = function() {				
				var tooltip;
				rotate = !rotate;
				
				if (rotate) {
					$playButton.find(":only-child").addClass("pause");
					startTimer();
					tooltip = "pause";
				}
				else {
					$playButton.find(":only-child").removeClass("pause");
					 pauseTimer();
					 tooltip = "play";
				}
				
				if (displayTip) {
					$playButton.data(TIP, tooltip).attr("title", $playButton.data(TIP));
				}
				
				return false;
			}
			
			//start timer
			var startTimer = function() {
				if (rotate && timerId == null) {
					var duration = Math.round($timer.data("pct") * delay);
					$timer.animate({width:screenWidth+1}, duration, "linear");
					timerId = setTimeout(rotateImg, duration);
				}
			}
			
			//reset timer
			var resetTimer = function() {
				clearTimeout(timerId);
				timerId = null;
				$timer.stop(true).width(0).data("pct", 1);
			}
			
			//pause timer
			var pauseTimer = function() {
				clearTimeout(timerId);
				timerId = null;
				$timer.stop(true).data("pct", 1 - ($timer.width()/(screenWidth+1)));
			}
			
			//resume slide show
			var resumeSlideshow = function() {
				if ($playButton.find(":only-child").hasClass("pause") && numItems > 1) {
					rotate = true;
					startTimer();
				}
			}
			
			//pause slide show
			var pauseSlideshow = function() {
				if (rotate) {
					rotate = false;
					pauseTimer();
				}
			}
			
			//toggle text
			var toggleText = function() {
				textOn = !textOn;
				$(this).find(":only-child").toggleClass("off", !textOn);
				if (textOn) {
					updateText();
				}
				else {
					resetText();
				}
				return false;
			}
			
			//open gallery
			var openGallery = function() {
				$slider.trigger(SWITCH_LIST, catIndex);
				$slider.trigger(UPDATE_MENU, catIndex);
				$overlay.stop(true,true).fadeTo(ANIMATE_SPEED,.4);
				$gallery.stop(true,true).hide().css({"z-index":11, top:$gallery.data("top"), left:$gallery.data("left")}).fadeIn(ANIMATE_SPEED);
				
				pauseSlideshow();
				hideCPanel();
				return false;
			}
			
			//close gallery
			var closeGallery = function() {
				$overlay.stop(true,true).fadeOut();
				$gallery.stop(true,true).fadeOut(function() {
													listPos = 0;
													$lists.data("pos", 0).css("top", 0); 
												});
				$currItems.trigger("mouseleave");
				
				resumeSlideshow();
				showCPanel();
				return false;
			}
			
			//select item
			var selectItem = function(e) {
				var $item = $(e.target);
				if ($item[0].nodeName != "LI") {
					$item = $item.parents("li").eq(0);
				}
				var i = $item.index();
				if (i > -1) {
					prevIndex = currIndex;
					var pIndex = $item.parent().index();
					if (pIndex != catIndex) {
						setCategory(pIndex);
						prevIndex = -1;
					}
					else if (i == currIndex) {
						closeGallery();
						return false;
					}
					
					resetTimer();
					currIndex = i;
					dir = NEXT;
					loadContent(currIndex);
					closeGallery();
				}
				return false;
			}
			
			//on item mouseover
			var itemMouseover = function() {
				if (!(catIndex == $(this).parent().index() && $(this).index() == currIndex)) {
					$(this).find("img:first").stop().animate({opacity:SLIDE_OPACITY}, ANIMATE_SPEED);
				}
			}
			
			//on item mouseout
			var itemMouseout = function(e) {
				$(this).find("img:first").stop().animate({opacity:1}, ANIMATE_SPEED);
			}
			
			//load current content
			var loadContent = function(i) {
				$slider.trigger(UPDATE_LABEL).trigger(UPDATE_BUTTONS).trigger(UPDATE_ADV_BUTTONS);
				
				var $item = $($currItems.get(i));
				$item.append($itemFrame);
				
				//reset text
				resetText();
				if (!textSync) {
					updateText();
				}
				
				//set link
				if ($mainLink) {
					var $currLink = $item.find(">a:nth-child(2)");
					var href = $currLink.attr("href");
					if (href) {
						$mainLink.unbind("click",preventDefault).css("cursor","pointer").attr({href:href, target:$currLink.attr("target")});
					}
					else {
						$mainLink.click(preventDefault).css("cursor", "default");
					}
				}
				
				//load image
				if ($item.data("img")) {
					$preloader.hide();
					displayContent($item.data("img"));
				}	
				else {	
					//load new image
					var path = $item.data("imgurl");
					var $img = $("<img class='content'/>");
					$img.attr("src", path);
					if ($img[0].complete || $img[0].readyState == "complete") {		
						$preloader.hide();
						storeImg($item, $img);
						displayContent($img);
					}
					else {
						$preloader.show();
						$img.load(
							function() {
								$preloader.hide();
								storeImg($item, $(this));
								displayContent($(this));
							}
						).error(
							function() {
								alert("Error loading image");
							}
						);
					}
				}	    
			}
			
			//display content
			var displayContent = function($img) {
				switch(effect) {
					case "fade":
						fadeInContent($img);
						break;
					case "h_slide":
						slideContent($img, "left", screenWidth);
						break;
					case "v_slide":
						slideContent($img, "top", screenHeight);
						break;
					case "h_wipe":
						hWipeContent($img);
						break;
					case "v_wipe":
						vWipeContent($img);
						break;
					default:
						showContent($img);
				}
			}
			
			//get previous
			var getPrevious = function() {
				var $prevImg;
				try {
					if (prevIndex >= 0) {
						$prevImg = $($currItems.get(prevIndex)).data("img");
						var currSrc = $($currItems.get(currIndex)).data("img").attr("src");
						var prevSrc = $prevImg.attr("src");
						if (currSrc != prevSrc) {
							var contents = $($strips.get(catIndex)).find("img.content");
							contents.removeAttr("id").hide();
							var $img = contents.filter(function() { return $(this).attr("src") == prevSrc; });
							$($img.get(0)).show();
						}
					}
				}
				catch (ex) {
				}
			
				return $prevImg;
			}
			
			//show content
			var showContent = function($img) {
				var $currStrip = $($strips.get(catIndex));
				if (textSync) {
					updateText();
				}
				$currStrip.find(".content").removeAttr("id").hide();
				$img.attr("id", "curr-img").show();
				startTimer();
			}
			
			//fade content
			var fadeInContent = function($currImg) {
				var $currStrip = $($strips.get(catIndex));
				$currStrip.find("#curr-img").stop(true, true);
				$currStrip.find(".content").removeAttr("id").css("z-index", 0);
				$currImg.attr("id", "curr-img").stop(true, true).css({opacity:0,"z-index":1}).show().animate({opacity:1}, transSpeed, easing,
					function() {
						$currStrip.find(".content:not('#curr-img')").hide();
						if (textSync) {
							updateText();
						}
						startTimer();
					}
				);
			}
			
			//slide content
			var slideContent = function($currImg, pos, moveby) {
				var $currStrip = $($strips.get(catIndex)).stop(true,true);
				var $prevImg = $currStrip.find("#curr-img");
				if ($prevImg.size() > 0) {
					$currStrip.find(".content").removeAttr("id").parents(".content-box").css({top:0,left:0});
					$currImg.attr("id", "curr-img").parents(".content-box").show();
					var $img, dest;
					if (dir == PREV) {
						$currStrip.css(pos, -moveby);
						$img = $prevImg;
						dest = 0;
					}
					else {
						$img = $currImg;
						dest = -moveby;
					}
					$img.parents(".content-box").css(pos,moveby);
					var prop = (pos == "top") ? {top:dest} : {left:dest};
					$currStrip.stop(true,true).animate(prop, transSpeed, easing,
										function() {
											$currStrip.find(".content:not('#curr-img')").parents(".content-box").hide();
											$img.parents(".content-box").css({top:0,left:0});
											$currStrip.css({top:0,left:0});
											if (textSync) {
												updateText();
											}
											startTimer();
										});
				}
				else {
					$currStrip.css({top:0,left:0});
					$currStrip.find(".content").parents(".content-box").hide().css({top:0,left:0});
					$currImg.attr("id", "curr-img").parents(".content-box").show();
					if (textSync) {
						updateText();
					}
					startTimer();
				}
			}
			
			//horizontal wipe content
			var hWipeContent = function($img) {
				var $prevImg = getPrevious();
				if (dir == PREV) {
					layer.displayPrevious($img, $prevImg, false);
				}
				else {
					layer.displayNext($img, false);
				}
			}	
			
			//vertical wipe content
			var vWipeContent = function($img) {
				var $prevImg = getPrevious();
				if (dir == PREV) {
					layer.displayPrevious($img, $prevImg, true);
				}
				else {
					layer.displayNext($img, true);
				}
			}		
			
			//load image
			var loadImg = function(loadIndex) {
				var $item = $($listItems.get(loadIndex));
				var path = $item.data("imgurl");
				var $img = $("<img class='content'/>");
				$img.attr("src", path);
				$img.load(function() {
							if (!$item.data("img")) {
								storeImg($item, $(this));
							}
							if (++loadIndex < $listItems.size()) {
								loadImg(loadIndex);
							}
						})
					.error(function() {
							if (++loadIndex < $listItems.size()) {
								loadImg(loadIndex);
							}
						});
			}
			
			//store image
			var storeImg = function($item, $img) {
				var pIndex = $item.parent().index();
				var $strip = $($strips.get(pIndex));
				$strip.append($img);
				if (autoScale) {
					processImg($img);
				}
				if (effect == "h_slide" || effect == "v_slide") {
					var $div = $("<div class='content-box'/>").css({width:screenWidth, height:screenHeight});
					$img.wrap($div);
					$img.css("display","block");
					var $link = $item.find(">a:nth-child(2)");
					if ($link) {
						$img.wrap($link);
					}
				}
				$item.data("img", $img);
			}
			
			//process image
			var processImg = function($img) {
				if ($img.outerWidth() > 0 && $img.outerHeight() > 0) {
					var sizeRatio;
					if ($img.outerWidth() > screenWidth) {
						sizeRatio = $img.outerHeight()/$img.outerWidth();
						$img.width(screenWidth);
						$img.height(sizeRatio * screenWidth);
					}
					
					if ($img.outerHeight() > screenHeight) {
						sizeRatio = $img.outerWidth()/$img.outerHeight();
						$img.width(sizeRatio * screenHeight);
						$img.height(screenHeight);
					}
					$img.css({left:Math.round((screenWidth - $img.outerWidth())/2), top:Math.round((screenHeight - $img.outerHeight())/2)});
				}
			}
			
			var setNoCategory = function() {				
				var $singleList = $($lists.get(0));
				for (var i = 1; i < $lists.size(); i++) {
					var $tmpList = $($lists.get(i));
					$singleList.append($tmpList.find(">li"));
					$tmpList.remove();
				}
				$lists = $galleryPanel.find(">ul");
			}
			
			//prevent default behavior
			var preventDefault = function() {
				return false;
			}
		}
		
		//get positive number
		var getPosNumber = function(val, defaultVal) {
			if (!isNaN(val) && val > 0) {
				return val;
			}
			return defaultVal;
		}
		
		//get nonnegative number
		var getNonNegNumber = function(val, defaultVal) {
			if (!isNaN(val) && val >= 0) {
				return val;
			}
			return defaultVal;
		}
		
		//get number of digits
		var getNumDigits = function(num) {
			var count = 1;
			num = Math.abs(num);
			num = parseInt(num/10);
			while(num > 0) {
				count++;
				num = parseInt(num/10);
			}
			return count;
		}
		
		//descending sort
		var sortDescending = function(a,b){
			return b - a;
		}
		
		var defaults = { 
			num_col:3,
			num_row:2,
			screen_width:900,
			screen_height:240,
			item_width:75,
			item_height:75,
			item_margin:8,
			display_number:false,
			display_playbutton:true,
			display_dbuttons:true,
			display_advbuttons:false,			
			display_textbutton:false,
			display_downloadbutton:false,			
			display_tooltip:true,
			display_caption:true,
			display_timer:false,
			display_gallery_info:true,
			effect:"fade",
			transition_speed:DEFAULT_SPEED,			
			text_effect:"fade",
			text_sync:true,
			text_on:true,
			cpanel_float:false,			
			cpanel_mouseover:true,
			auto_scale:false,			
			cont_nav:true,
			auto_rotate:true,
			delay:DEFAULT_DELAY,			
			initial_open:false,
			selected_category:1,
			selected_image:1,
			easing:"",
			no_category:false
		};
		
		var opts = $.extend({}, defaults, params);

		return this.each(
			function() {
				var slider = new GridGallery($(this), opts);
				slider.init();
			}
		);
	}
})(jQuery);
