/*
	@name Artshow
	@version 0.1
	@charset UTF-8
	@modified 23.04.2008 11:10 AM

	@require jQuery 1.2.6
	@require jQuery UI 1.5b2
	@require Timer Plugin
*/

var Artshow = { // 'this' / 'root' in variables below

	globals: {
		debug: true, // debugging mode
		version: 0.3,
		prefix: 'artshow', // prefix added to all class names

		thumbnailHeight: 35, // thumbnail height
		thumbnailWidth: 35,
		pageIndicatorWidth: 11,

		slideshowSpeed: 3500, // slideshow speed
		controlSpeed: 1500, // slideshow control fade speed
		scrollSpeed: 550, // page scrolling speed
		imageBorder: 10,

		pageIndex: 1, // current "page" (fraction of ui.$list) with thumbnails. starts from 1
		pageWidth: 0, // width of a "page"
		pageHeight: 0,
		thumbIndex: 0, // current photo
		$thumbSet: {}, // set with current thumbnails
		thumbHeight: 0, // height of thumbnails
		thumbWidth: 0, // width of a thumbnail

		nextImage: 0,
		prevImage: 0,

		$links: {}, // all links opening single instances of the Artshow
		$sets: {}, // all sets of links opening Artshow in a slideshow mode (thumbnails)
		buildSingle: false, // wheter or not handle single links. changed later after validation
		buildMultiple: false // wheter or not handle sets. changed later after validation
	},

	ui: { }, // container for HTML elements

	start: function() {
		this.globals.$links = jQuery('a[rel="artshow"]'); // TODO: confine selector to [rel]
		this.globals.$sets = jQuery('.artshow');

		this.checkRelevant(); // check wheter or not to proceed

		if (this.globals.buildSingle || this.globals.buildMultiple) {

			this.buildOverlay(); // prepare HTML elements (overlays, containers) for user interface

			if (this.globals.buildSingle) {
				this.bindSingle(); // manage events for single images
			}
			if (this.globals.buildMultiple) {
				this.bindMultiple(); // manage events for sets of images
			}

		}

	}, // start # end

	checkRelevant: function() {
		// FIXME: is it really necessary

		function filterLinks($collection /* jQuery */) {
			$collection.each(function(i) {
				var $imgs = jQuery(this).find('img');
				if ($imgs.length != 1) {
					$collection[i] = '';
				
				}
			});
			$collection = jQuery.grep($collection, function(i) { return i != ''; }); // remove empty items from array
			return $collection
		}

		var $links = filterLinks(this.globals.$links);
		if ($links.length) { // if there are any items left
			this.globals.buildSingle = true; // allow building Artshow for single links
		}

		var $sets = this.globals.$sets;
		$sets.each(function(i) {
			if (!filterLinks(jQuery(this))) {
				$sets[i] = '';
			}
		});

		$sets = jQuery.grep($sets, function(i) { return i != ''; }); // remove empty items from array
		if ($sets.length) { // if there are any items left
			this.globals.buildMultiple = true; // allow building Artshow for single links
		}
		
		
	}, // checkRelevantElements # end

	buildOverlay: function() {

		var root = this;

		// return div element with custom class
		function buildElement(className /* String , [tagName] String */) {
			var element = 'div';
			if (typeof arguments[1] != 'undefined') {
				element = arguments[1]; // build a custom tag, not a div
			}
			return jQuery('<' + element + ' class="' + root.globals.prefix + '-' + className + '"></' + element + '>');
		}

		if (typeof this.ui.$overlay == 'undefined') {

			// prepare elements with class names set
			// main elements
			this.ui.$overlay = buildElement('overlay'); // main container for overlay
			this.ui.$mask = buildElement('mask'); // semitransparent
			this.ui.$scene = buildElement('scene') // something like overlay, but without semitransparency
			this.ui.$view = buildElement('view'); // main content (logo, photo or description or custom HTML)
			this.ui.$photo = buildElement('photo');
			this.ui.$html = buildElement('html');
			this.ui.$close = buildElement('close', 'a')
			this.ui.$preload = buildElement('preload');

			// event hiding overlay
			this.ui.$scene
				.bind('click', this, function(e) {
					var target = e.target;
					var root = e.data;
					// clicking on elements in this 'if' statement will hide overlay
					if ((target.className == root.ui.$scene[0].className) ||
						(target.className == root.ui.$view[0].className) ||
						(target.className == root.ui.$close[0].className)) { // clicking in image doesn't close the Artshow
						root.hideOverlay();
					}
				})
				.append(this.ui.$preload)
				.append(this.ui.$view)

			this.ui.$view
				.append(this.ui.$photo)
				.append(this.ui.$html)

			this.ui.$photo.append(this.ui.$close);

			jQuery(window).bind('resize', function() {
				root.getDimensions();
			});

			// build bottom panel only if it's needed
			//if (this.globals.buildMultiple) {
				// bottom panel
				this.ui.$panel = buildElement('panel'); // black panel at the bottom of overlay
				this.ui.$thumbs = buildElement('thumbs'); // inner container for thumbnail list
				this.ui.$list = buildElement('list', 'ol'); // list with thumbnails
				this.ui.$listPrev = buildElement('list-prev', 'a'); // skip to the previous page button
				this.ui.$listNext = buildElement('list-next', 'a'); // skip to the next page button
				this.ui.$listNav = this.ui.$listPrev.add(this.ui.$listNext); // skipping buttons. combined declaration for clarity
				this.ui.$pager = buildElement('pager'); // dotted pager
				this.ui.$slideshow = buildElement('slideshow');
				this.ui.$slideshowPrev = buildElement('slideshow-prev', 'a');
				this.ui.$slideshowPlay = buildElement('slideshow-play', 'a');
				this.ui.$slideshowPause = buildElement('slideshow-pause', 'a');
				this.ui.$slideshowNext = buildElement('slideshow-next', 'a');
				this.ui.$shadowLeft = buildElement('shadow-left');
				this.ui.$shadowRight = buildElement('shadow-right');

				this.ui.$thumbs
					.append(this.ui.$list)
					.appendTo(this.ui.$panel);

				this.ui.$panel
					.append(this.ui.$pager)
					.append(this.ui.$listPrev)
					.append(this.ui.$listNext)
					.append(this.ui.$shadowLeft)
					.append(this.ui.$shadowRight)

				this.ui.$slideshow
					.append(this.ui.$slideshowPrev)
					.append(this.ui.$slideshowPlay)
					.append(this.ui.$slideshowPause)
					.append(this.ui.$slideshowNext)
					.appendTo(this.ui.$scene)

				this.ui.$slideshow.find('a').attr('href', '')

				jQuery(window).bind('resize', function() {
					root.buildPager();
				});
			//}

			this.ui.$overlay[0].style.display = 'none'; // Safari needs this instead of .hide()
			this.ui.$overlay
				.append(this.ui.$mask)
				.append(this.ui.$scene)
				.append(this.ui.$panel)
				.appendTo('body')

		}

		var $sets = this.globals.$sets;
		if ($sets.length == 1) {
			// if there's only one set, append thumbnails to the list
			this.generateThumbnails($sets[0]);
			this.generateSlideshow();
		}

	}, // buildOverlay # end

	bindSingle: function() {
	
		this.globals.$links.unbind('click').bind('click', this, function(e) {
			var root = e.data;
			root.ui.$overlay.addClass(root.globals.prefix + '-single');
			root.ui.$panel.hide();
			root.showOverlay('single');
			root.showPhoto(this.href);
			e.preventDefault();
		});
	}, // bindSingle # end

	bindMultiple: function() {

		var $sets = this.globals.$sets,
				root = this;
		$sets.each(function(i) {
			var $set = jQuery(this);
			// each is needed for the 'j' iterator
			$set.each(function(j) {
				// click on one of the images in set
				jQuery(this).unbind('click').bind('click', root, function(e) {
					root.ui.$overlay.removeClass(root.globals.prefix + '-single');
					root.ui.$panel.show();
					// if there are more sets, generate thumbs each time
					if ($sets.length > 1) {
						root.generateThumbnails($sets);
						root.generateSlideshow();
					}
					root.showOverlay();
					root.showPhoto(this.href);
					root.highlightThumbnail(j, 'scrollView');
					e.preventDefault();
				});
			});
		});

		this.ui.$listNav.bind('click', this, function(e) {
			var $this = jQuery(this),
					root = e.data,
					standardClass = '.' + root.globals.prefix + '-',
					pageIndex = root.globals.pageIndex;

			if ($this.is(standardClass + 'active')) {
				if ($this.is(standardClass + 'list-prev')) {
					root.scrollToPage(pageIndex - 1);
				} else {
					root.scrollToPage(pageIndex + 1);
				}
			}
			e.preventDefault();
		});
	}, // bindMultiple # end

	generateThumbnails: function(set) {

		var $images = jQuery(set),
				$list = this.ui.$list,
				root = this

		$list.empty()
		$images.each(function(i) {
			var $image = jQuery(this).find('img');
			// creating thumbnail (sized-down $image). not 'new Image()' because we don't want to preload them
			var src = $image.attr('src'),
			
					alt = $image.attr('alt') || '',
					height = root.globals.thumbnailHeight;
					width  = root.globals.thumbnailWidth;
			var $thumbnail = jQuery('<img src="' + src + '" alt="' + alt + '" height="' + height + '" width= "'+width+'"/>');

			// creating <li><a/></li>
			var $listItem = jQuery('<li></li>');
			var $listLink = jQuery('<a href="' + jQuery(this).attr('href') + '"></a>');

			$listLink
				.bind('click', root, function(e) {
					var root = e.data;
					root.showPhoto(this.href);
					root.highlightThumbnail(i);
					e.preventDefault();
				})
				.append($thumbnail)
				.appendTo($listItem);

			$list.append($listItem);

		});

		this.globals.$thumbSet = $list.find('li');

	}, // generateThumbnails # end

	generateSlideshow: function() {

		var root = this,
				activeSlideshow = false,
				$slideshow = root.ui.$slideshow,
				interval = root.globals.slideshowSpeed;

		this.ui.$slideshowPause.hide();

		// slowly fade out slideshow controls when we stop moving mouse
		this.ui.$scene.bind('mousemove', function() {
			$slideshow
				.fadeIn('slow')
				.stopTime('tickle')
				.everyTime(root.globals.controlSpeed, 'tickle', function() {
					$slideshow.fadeOut('slow');
				});
		});

		this.ui.$slideshow.find('a:not(.exclude)').bind('click', function(e) {
			var prefix = root.globals.prefix + '-slideshow-',
					c = this.className,
					photo = 0,
					current = 0;

			if (c != prefix + 'inactive') {

				// determine if we're going backward or forward
				if (c == prefix + 'prev') {
					current = root.globals.thumbIndex - 1;
				} else if (c == prefix + 'next') {
					current = root.globals.thumbIndex + 1;
				}

				// show photo and update thumbnail index
				if ((c == prefix + 'prev') || (c == prefix + 'next')) {
					// prevent slideshow controls from dissapearing when clicking on next/prev (no mousemove)
					$slideshow.show().stopTime('tickle');
					// switch to prev / next photo
					root.showPhoto(current);
					root.highlightThumbnail(current, 'scrollView');
				}

				// play slideshow
				if (c == prefix + 'play') {
					// hide tumbnails panel
					root.globals.thumbHeight = root.ui.$panel.height();
					root.ui.$panel.animate({ bottom: 0 - root.globals.thumbHeight }, 500, function() {
						root.ui.$overlay.addClass(root.globals.prefix + '-fullscreen')
					});


					var index = root.globals.thumbIndex;
					var last = root.globals.$thumbSet.length - 1;
					if (index != last) {
						root.ui.$slideshow.everyTime(interval, 'slideshow', function() {
							if (index != last) {
								index = root.globals.thumbIndex + 1;
								root.showPhoto(index);
								root.highlightThumbnail(index, 'scrollView');
								// preload next photo
								var preload = new Image();
								preload.src = root.globals.$thumbSet.eq(index + 1).find('a').attr('href');
							} else {
								root.stopSlideshow('pause');
							}
						});
						root.ui.$slideshowPlay.hide();
						root.ui.$slideshowPause.show();
					}
				// pause slideshow
				} else if (c == prefix + 'pause') {
					root.stopSlideshow('pause');
				}

			}

			e.preventDefault();
		});
	},

	stopSlideshow: function() {
		var root = this;
		function fullscreenOff() { root.ui.$overlay.removeClass(root.globals.prefix + '-fullscreen'); }
		this.ui.$slideshow.stopTime('slideshow');
		this.ui.$slideshowPlay.show();
		this.ui.$slideshowPause.hide();
		if (typeof arguments[0] == 'undefined') {
			this.ui.$panel.css('bottom', 0);
			fullscreenOff();
		} else {
			this.ui.$panel.animate({ bottom: 0 }, 500, fullscreenOff);
		}
	},

	showPhoto: function(source) {
		var root = this,
				$view = root.ui.$view,
				$photo = root.ui.$photo,
				$html = root.ui.$html,
				$image = jQuery('<img/>');

		// if the number was passed, figure out which photo it is
		sourceOriginal = source;
		if (typeof source == 'number') {
			source = root.globals.$thumbSet.eq(source).find('a')[0].href
		}

		//show progress indicator
		var $progress = jQuery('<div class="' + this.globals.prefix + '-progress"></div>');
		$progress.css('margin-top', this.globals.pageHeight / 2);

		// clean divs and show progress indicator
		$photo.find('img').remove();
		$html.empty();
		$view.append($progress);

		// FIXME: do we really need this kind of preload?
		this.ui.$preload.empty().append($image);

		// TODO: progress indicator
		$image.bind('load', function() {
			$progress.remove();
			var pageHeight = root.globals.pageHeight;
			// figure out image dimensions
			var imageHeight = this.height + 2 * root.globals.imageBorder;
			var imageWidth = this.width + 2 * root.globals.imageBorder;
			if (imageHeight < pageHeight) {
				// center image vertically if it's smaller than viewport
				$photo.css('margin-top', (pageHeight - imageHeight) / 2);
			} else {
				// if it's larger, apply margin
				// FIXME: className!
				$photo.css('margin-top', 20)
			}
			// apply width to image's container (needed for root.ui.$close positioning)
			$photo.css('width', imageWidth);

			// get photo id in order to pull out accompanying content
			photoId = source.substr(source.lastIndexOf('/') + 1, source.length).split('.')[0];
			jQuery('#source-' + photoId).each(function() {
				root.ui.$html.html(jQuery(this).html())
			});

			jQuery(this).appendTo($photo);

			root.ui.$preload.empty();
			root.notifyStats();
		})

		$image.attr('src', source);
	},

	notifyStats: function() {

		function gemiusHit(domain, code) {
			var hitURL = new String(domain + (new Date()).getTime() + '/redot.gif?l=11&id=');
			hitURL += code;
			hitURL += gemius_parameters();
			var hitObject = new Image(1,1);
			hitObject.src = hitURL;
		}

		function ppGemiusHit(domain, code) {
			var ppHitURL = new String(domain + (new Date()).getTime() + '/redot.gif?l=11&id=');
			ppHitURL += code;
			ppHitURL += gemius_parameters();
			var ppHitObject = new Image(1,1);
			ppHitObject.src = ppHitURL;
		}

		//gemiusHit('http://filmweb.hit.gemius.pl/_', Filmweb.globals.gemiusPhoto); // gallery
		//ppGemiusHit('http://pro.hit.gemius.pl/_', "coJAUkMPKyat6DGmWVG19qdzfSrUq.rr13K3QEeT_UT.x7");
		//FW.globals.googleAnalytics._trackPageview();
	},

	showOverlay: function() {
		var root = this;
		this.ui.$overlay.show();

		// is it IE6?
		var isIE6 = (typeof document.body.style.maxHeight == "undefined");

		// remember where we are and scroll view to the top
		if (isIE6) {
			root.globals.scrollTop = document.documentElement.scrollTop;
			document.documentElement.scrollTop = 0;

			// hide select boxes
			jQuery('select:visible').addClass('toHide').hide();
		}

		// disable scrollbars
		jQuery('html').addClass(this.globals.prefix + '-overflow');

		// fix expression not covering place where previously was scrollbar
		if (isIE6) {
			root.ui.$mask.add(root.ui.$scene).add(root.ui.$panel).each(function() {
				this.setExpression('width', 'document.documentElement.clientWidth + "px"');
			});
		}

		// determine the length of $list (number of list items * their width)
		if (typeof arguments[0] == 'undefined') {
			var $list = this.ui.$list,
					$thumbnails = $list.find('a');
			var listWidth = ($thumbnails.length * $thumbnails.filter(':first').outerWidth()) + 'px';
			$list.width(listWidth);
			this.globals.thumbMargin = parseInt(this.ui.$thumbs.css('margin-left')) + parseInt(this.ui.$thumbs.css('margin-right'));
			this.getDimensions();
			this.buildPager();
		}

	}, // showOverlay # end

	hideOverlay: function() {

		this.stopSlideshow();
		this.ui.$overlay.hide();
		jQuery('html').removeClass(this.globals.prefix + '-overflow');

		var isIE6 = this.ui.$overlay.is('.' + this.globals.prefix + '-ie6');


		// restore scroll position
		if (isIE6) {
			document.documentElement.scrollTop = this.globals.scrollTop;

			// show select boxes
			jQuery('select.toHide').removeClass('toHide').show();
		}

	}, // hideOverlay # end

	highlightThumbnail: function(index) {
		var $thumbSet = this.globals.$thumbSet,
				prevIndex = this.globals.thumbIndex,
				prefix = this.globals.prefix + '-slideshow';

		if (index == 0) {
			this.ui.$slideshowPrev.addClass(prefix + '-prev-inactive');
		} else {
			this.ui.$slideshowPrev.removeClass(prefix + '-prev-inactive');
			if (index == ($thumbSet.length - 1)) {
				this.ui.$slideshowNext.addClass(prefix + '-next-inactive');
			} else {
				this.ui.$slideshowNext.removeClass(prefix + '-next-inactive');
			}
		}

		$thumbSet.eq(prevIndex).removeClass('selected');
		$thumbSet.eq(index).addClass('selected');

		this.globals.thumbIndex = index;
		this.preloadNeighbourImage(index);
		this.keyboardActions(index);

		// scroll the page only if we've just opened Artshow
		// prevents unwanted scrolls due to non-integer amount of thumbs on a page
		if (typeof arguments[1] != 'undefined') {
			// check position of thumbnail on "page" (view)
			var pageNumber = this.getPage(index);
			if (this.globals.pageIndex != pageNumber) {
				// if it's not on a current "page", scroll it
				this.scrollToPage(pageNumber);
			}
		}

	},

	getDimensions: function() {
		// occuring every window.resize()

		if (this.ui.$overlay.is(':visible')) {

			var $thumbs = this.ui.$thumbs,
					thumbWidth = this.globals.$thumbSet.eq(0).width(),
					pageWidth = parseInt(jQuery(window).width()) - this.globals.thumbMargin;

					// Fix for Opera 9.5+ window height bug
					if(jQuery.browser.opera && jQuery.browser.version >= 9.50) {
						var pageHeight = window.innerHeight - $thumbs.height();
					} else {
						var pageHeight = jQuery(window).height() - $thumbs.height();
					}

			$thumbs.width(pageWidth + 'px');
			this.globals.thumbWidth = thumbWidth;
			this.globals.pageWidth = pageWidth;
			this.globals.pageHeight = pageHeight;

		}

	},

	buildPager: function() {
		// occuring every refresh & window.resize()

		if (this.ui.$overlay.is(':visible')) {

			var $pager = this.ui.$pager,
					$prev = this.ui.$listPrev,
					$next = this.ui.$listNext,
					$prevs = this.ui.$shadowLeft,
					$nexts = this.ui.$shadowRight,
					active = this.globals.prefix + '-active',
					// specific classes for IE6 issue with .classname1.classnam2 selector
					activePrev = this.globals.prefix + '-list-prev-active',
					activeNext = this.globals.prefix + '-list-next-active',
					pageCount = this.getPageCount(),
					pageIndex = this.globals.pageIndex,
					root = this;

			// eg: resizing window & going from 10 pages down to 2
			if (pageIndex > pageCount) {
				pageIndex = pageCount;
			}

			// switching enabled/disabled state for prev/next buttons
			// if our current position isn't page 1, we can go back
			if (pageIndex != 1) {
				$prev.addClass(activePrev).addClass(active);
				$prevs.show();
			} else {
				$prev.removeClass(activePrev).removeClass(active);
				$prevs.hide();
			}

			// if our current position isn't the last page, we can go forth
			if (pageIndex != pageCount) {
				$next.addClass(activeNext).addClass(active);
				$nexts.show();
			} else {
				$next.removeClass(activeNext).removeClass(active);
				$nexts.hide();
			}

			// position pager in the middle
			$pager
				.css('margin-left', 0 - (pageCount * this.globals.pageIndicatorWidth / 2))
				.empty();

			// if there's a need for pager, build one
			if (pageCount > 1) {
				for (var i = 0; i < pageCount; i++) {
					var pageIndicator = document.createElement('a');
					jQuery(pageIndicator).each(function() {
						this.href = '';
						// if it's the current page, paint it filled
						if ((i + 1) == pageIndex) {
							this.className = 'active';
						}
						jQuery(this).bind('click', (i + 1), function(e) {
							root.scrollToPage(e.data);
							e.preventDefault();
						})
					});
					$pager.append(pageIndicator);
				}
			}

		}

	},

	scrollToPage: function(index) {
		var newPosition = 0 - ((index - 1) * this.globals.pageWidth) + 'px';
		this.ui.$list.animate({
			left: newPosition
		}, this.globals.scrollSpeed);
		this.globals.pageIndex = index;
		this.buildPager();
	},

	getPageCount: function() {
		return Math.ceil(this.ui.$list.width() / this.globals.pageWidth);
	},

	getPage: function(index) {
		var page = 1;
		if (index > 0) {
			var thumbsInPage = Math.ceil(this.globals.pageWidth / this.globals.thumbWidth);
			page = Math.ceil(index / thumbsInPage);
		}
		return page;
	},

	preloadNeighbourImage: function(index) {
		var current = index;
		if (this.globals.$thumbSet.length > current + 1) {
			nextImage = new Image();
			nextImage.src = this.globals.$thumbSet.eq(current + 1).find('a').attr('href');
		}
		if (this.globals.thumbIndex > 0) {
			prevImage = new Image();
			prevImage.src = this.globals.$thumbSet.eq(current - 1).find('a').attr('href');
		}
	},

	keyboardActions: function(index) {

		var root = this,
			$slideshow = root.ui.$slideshow,
			current = index;


		function enbale_keyboard() {
			jQuery(document).unbind().keydown(function(objEvent) {
				action(objEvent);
			});
		}

		function disable_keyboard() {
			jQuery(document).unbind();
		}

		function action(objEvent) {

			// IE detection
			if ( objEvent == null ) {
				keycode = event.keyCode;
			} else {
				keycode = objEvent.keyCode;
			}
			escapeKey = 27;

			// Get the key in lower case form
			key = String.fromCharCode(keycode).toLowerCase();

			// Verify the keys to close the slideshow
			if ( ( key == 'c' ) || ( key == 'x' ) || ( keycode == escapeKey ) ) {
				root.hideOverlay();
			}

			// prevent slideshow controls from dissapearing when clicking on next/prev (no mousemove)
			// switch to prev / next photo
			// TODO: Fix problems with browser memory
			
			if ( ( key == 'p' ) || ( keycode == 37 ) ) {
				if (root.globals.$thumbSet.length > current + 1) {
					$slideshow.show().stopTime('tickle');
					root.showPhoto(current + 1);
					root.highlightThumbnail(current + 1, 'scrollView');
					disable_keyboard();
				}
			}
			if ( ( key == 'n' ) || ( keycode == 39 ) ) {
				if (root.globals.thumbIndex > 0) {
					$slideshow.show().stopTime('tickle');
					root.showPhoto(current - 1);
					root.highlightThumbnail(current - 1, 'scrollView');
					disable_keyboard();
				}
			}
			
		}

		enbale_keyboard();
	}
}
