/**
 * ZigPopup class
 * depends on prototype/scriptaculous
 *
 * @author Jordy de Jong <jojong@zigwebsoftware.nl>
 * @version 1.0
 * @copyright Copyright (c) 2010, Zig Websoftware
 */ 

var ZigPopup = Class.create(
{	
    /**
     * popup is blocking?
     * @var boolean bBlocking
     */
	bBlocking: false,

    /**
     * keep popup in center by vertical scrolling?
     * @var boolean bVerticalScroll
     */
	bVerticalScroll: true,	
	
	/**
     * the popup DOM node we want to attach our functionality to
     * @var object oPopupNode
     */
	oPopupNode: null,
	
	/**
     * the overlay DOM node
     * @var object oOverlay
     */	
	oOverlay: null,

	/**
     * the popup's header node
     * @var object oMoveNode
     */		
	oMoveNode: null,
	

	/**
     * the popup's header close node
     * @var object oMoveNode
     */		
	oHeaderCloseNode: null,
	

	/**
     * the popup's title node
     * @var object oTitleNode
     */		
	oTitleNode: null,

	/**
     * the popup's content node
     * @var object oContentNode
     */		
	oContentNode: null,

	
	/**
     * the popup's ok button
     * @var object oOkButtonNode
     */		
	oOkButtonNode: null,	
	
	/**
     * the popup's ok button
     * @var object oCloseButtonNode
     */		
	oCloseButtonNode: null,	


	/**
     * duration of popup show/hide action
     * @var double fShowHideDuration
     */		
	fShowHideDuration: 0.5,

	/**
     * default title of popup
     * @var string sTitle
     */		
	sDefaultTitle: null,

	/**
     * minumum width of popup
     * @var int iMinWidth
     */		
	iMinWidth: 0,		
	
	/**
     * display mode (block or inline)
     * @var string sDisplayMode
     */		
	sDisplayMode: 'block',		
	
    /**
     * initializes the popup
     *
	 * @param object oPopupNode the dom node we want to attach to
	 * @param array aOptions some options
     * @return void
     */	
	initialize: function(oPopupNode, aOptions) 
	{	
		if (aOptions && typeof(aOptions.blocking) == 'boolean')
		{
			this.bBlocking = aOptions.blocking;
		}

		if (aOptions && typeof(aOptions.minWidth) !== 'undefined')
		{
			this.iMinWidth = parseInt(aOptions.minWidth, 10);
		}		

		if (aOptions && typeof(aOptions.verticalScroll) == 'boolean')
		{
			this.bVerticalScroll = aOptions.verticalScroll;
		}

		if (typeof(oPopupNode) == 'undefined' || oPopupNode == null)
		{	
			if (typeof(console) !== 'undefined')
			{
				console.error('could not initialize popup: dom node not found');		
			}
		}
		else if (oPopupNode.id == null || oPopupNode.id == '')
		{
			if (typeof(console) !== 'undefined')
			{
				console.error('could not initialize popup: dom node needs id attribute');		
			}
		}
		else
		{						
			this.oPopupNode = oPopupNode;
			this.preparePopupNode();	
		
		}
	},

	/**
     * change the "display mode" to inline or block, default block
     *
	 * @param string sMode the mode, inline or block
     * @return void
     */	
	setDisplayMode: function(sMode)
	{
		this.sDisplayMode = sMode;
	},	
	
	/**
     * change width of popup
     *
	 * @param int iWidth the width in pixels
	 * @param function oCompleteHandler the action to perform after resizing, optional
     * @return void
     */	
	setWidth: function(iWidth, oCompleteHandler)
	{	
		// parent div should be at least this.iMinWidth wide
		var iPopupWidth = Math.max(iWidth, this.iMinWidth);

		// resize popup node		
		new Effect.Morph(this.oPopupNode, {
			style: 'width: ' + iPopupWidth.toString() + 'px',
			duration: 0.4
		});			
		
		
		// also resize and position content node
		// take in a account any borders the content node might have when calculating its width
		var sBorderLeft = this.oContentNode.getStyle('borderLeftWidth');
		var sBorderRight = this.oContentNode.getStyle('borderRightWidth');	
		var iBorderCorrection = 0;
		
		if (typeof(sBorderLeft) != 'undefined' && sBorderLeft != null && sBorderLeft != '')
		{
			iBorderCorrection += parseInt(sBorderLeft, 10);
		}

		if (typeof(sBorderRight) != 'undefined' && sBorderRight != null && sBorderRight != '')
		{
			iBorderCorrection += parseInt(sBorderRight, 10);
		}

		this.oPopupNode.parentNode.style.top = ''; // force re-positioning
		this.positionOverlay();
				
		new Effect.Morph(this.oContentNode, {
				style: 'width: ' + (iWidth-iBorderCorrection).toString() + 'px',
				duration: 0.4,
				afterFinish: oCompleteHandler
		});
	
	},

	/**
     * to be overriden, display appropiate nodes for specific type of 
     *
	 * @param void
     * @return void
     */	
	showNodes: function()
	{
		
	},
	
	/**
     * prepares the popup node
	 * applies some necessary styling, and tries to find various nodes
     *
	 * @param void
     * @return void
     */	
	preparePopupNode: function()
	{		
		this.oPopupNode.style.zIndex = "990";

		// try to find nodes in this.oPopupNode
		this.oTitleNode					= $(this.oPopupNode.select('.ZigPopup_title')[0]);
		this.sDefaultTitle 				= (this.oTitleNode ? this.oTitleNode.innerHTML : '');
		
		this.oContentNode				= $(this.oPopupNode.select('.ZigPopup_content')[0]);
		this.oMoveNode					= $(this.oPopupNode.select('.ZigPopup_move')[0]);
		this.oHeaderCloseNode			= $(this.oPopupNode.select('.ZigPopup_headerClose')[0]);
		this.oCloseButtonNode			= $(this.oPopupNode.select('.ZigPopup_closeButton')[0]);
		this.oOkButtonNode				= $(this.oPopupNode.select('.ZigPopup_okButton')[0]);

		// first, we hide all optional nodes and remove all previous listeners these nodes might have
		this.oPopupNode.select("[class^='ZigPopup_']").each(function(oEl) {
			oEl.style.display = 'none';
			oEl.stopObserving();
		});

		// show nodes which are always used
		if (this.oContentNode)
		{
			this.oContentNode.style.display = this.sDisplayMode;			

			// min width set? make sure content node is at least this wide
			if (this.iMinWidth > 0)
			{
				this.oContentNode.style.minWidth = this.iMinWidth.toString() + 'px';
			}
		}
		
		if (this.bBlocking == false)
		{
			if (this.oHeaderCloseNode)
			{
				this.oHeaderCloseNode.style.display = this.sDisplayMode;
			}
		}
		
		// now we ask our subclass to show its appropiate nodes		
		this.showNodes();	
				
		// set shared listeners for all popups
		this.oPopupNode.select('.ZigPopup_move').each(function(oEl) 
		{	
			new Draggable(this.oPopupNode, {handle: oEl});
		}.bind(this));		

		// now we ask our subclass to set its subclass specific listeners
		this.setListeners();	
	},


	/**
     * set appropiate listeners for specific type of popup
     *
	 * @param void
     * @return void
     */		
	setListeners: function()
	{

		document.observe('keydown', function(oEvent)
		{
			this.handleKeypress(oEvent);
		}.bind(this));
		
	
		$(this.oPopupNode.parentNode).observe('click', function(oEvent)
		{
			// a click on the popup's invisible parentNode should be considered a click on the overlay
			// a click on the popup should not
		
			if (oEvent.target == this.oPopupNode.parentNode && this.bBlocking == false)
			{
				this.hide();				
			}
		}.bind(this));

	},

	
	/**
     * handle keypress
     *
	 * @param object oEvent the keypress' event
     * @return void
     */
	handleKeypress: function(oEvent)
	{	
		if (oEvent.keyCode == Event.KEY_ESC && this.bBlocking == false)
		{
			this.hide();
		}
	},

	
	/**
     * sets content in the content node, if found
     *
	 * @param string sHtml
     * @return void
     */		
	setContent: function(sHtml)
	{
		if (this.oContentNode)
		{
			this.oContentNode.innerHTML = sHtml;
		}
		else 
		{
			// console.log('could not set content, contentNode not found');
		}
	},

	/**
     * sets label in the ok button node
     *
	 * @param string sLabel
     * @return void
     */		
	setLabelOkButton: function(sLabel)
	{
		if (this.oOkButtonNode)
		{
			this.oOkButtonNode.innerHTML = sLabel;
		}
		else 
		{
			// console.log('could not set ok button label, Node not found');
		}
	},	

	/**
     * sets label in the cancel button node
     *
	 * @param string sLabel
     * @return void
     */		
	setLabelCloseButton: function(sLabel)
	{
		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.innerHTML = sLabel;
		}
		else 
		{
			// console.log('could not set close button label, Node not found');
		}
	},		
	
	/**
     * sets content in the title node, if found
     *
	 * @param string sHtml
     * @return void
     */			
	setTitle: function(sHtml)
	{
		if (this.oTitleNode)
		{
			this.oTitleNode.innerHTML = sHtml;
		}
		else 
		{
			// console.log('could not set title, contentNode not found');
		}
	},

	/**
     * shows the overlay and popup
     *
     * @return void
     */			
	show: function()
	{	
		this.showOverlay();	
		this.oPopupNode.appear({duration: this.fShowHideDuration});
	},

	/**
     * hides the overlay and popup
     *
	 * @param void
     * @return void
     */			
	hide: function()
	{
		document.stopObserving(); // stop listening to keypresses
		this.hideOverlay();		
		this.oPopupNode.fade({duration: this.fShowHideDuration});
		
		// wait a little before cleanup, until we are sure the fade is done
		var doCleanUp = function() 
		{
			this.cleanUp();
		}.bind(this);
		
		doCleanUp.delay(0.4);
	},

	/**
     * clean up, removes all nodes and listeners
     *
	 * @param void
     * @return void
     */	
	cleanUp: function()
	{		

		// reset top, for correct positioning the next time
		this.oPopupNode.parentNode.style.top = '';
		
		// restore default title
		if (this.oTitleNode)
		{
			this.oTitleNode.innerHTML = this.sDefaultTitle;
		}
		
		// delete calculated dimensions so the dimensions from the original styling will be used the next time
		this.oPopupNode.style.width = null;
		this.oPopupNode.style.height = null;
		this.oContentNode.style.width = null;
		this.oContentNode.style.height = null;		
		this.oContentNode.style.minWidth = null;
		
		// remove overlay
		if (this.oOverlay)
		{
			this.oOverlay.stopObserving();
			this.oOverlay.parentNode.removeChild(this.oOverlay);
			this.oOverlay = null;
		}
		
		// remove popupnode
		$(this.oPopupNode.parentNode).stopObserving();
		
		if (this.oPopupNode)
		{
			// remove listeners
			this.oPopupNode.select("[class^='ZigPopup_']").each(function(oEl) {
				oEl.style.display = 'none';
				oEl.stopObserving();
			});	
		}	
	},

	/**
     * shows the overlay
     *
	 * @param void
     * @return void
     */		
	showOverlay: function()
	{
		if (this.oOverlay == null)
		{
			this.initOverlay();
		}
		
		this.oOverlay.style.display = this.sDisplayMode;
	},

	/**
     * hides the overlay
     *
	 * @param void
     * @return void
     */		
	hideOverlay: function()
	{
		if (this.oOverlay != null)
		{
			$(this.oOverlay).fade({duration: this.fShowHideDuration});
		}
	},

	/**
     * initializes the overlay, creates dom node with correct styling
	 * also sets some necessary event listeners
     *
	 * @param void
     * @return void
     */		
	initOverlay: function()
	{	
		this.oOverlay = $("popupOverlay");
		
		if (this.oOverlay == null)
		{
			this.oOverlay = document.createElement('div');
			this.oOverlay.style.background = '#000';
			this.oOverlay.style.display = 'none';
			this.oOverlay.style.position = 'absolute';
			this.oOverlay.style.zIndex = "900";
			
			if (Prototype.Browser.IE)
			{
				this.oOverlay.style.filter = 'alpha(opacity=60)';
			}
			else
			{
				this.oOverlay.style.opacity = 0.6;		
			}	

			document.body.appendChild(this.oOverlay);
		}
		this.positionOverlay();

		// add listeners so overlay will correct itself after scroll/resize
		Event.observe(window, 'scroll', function() {
			this.positionOverlay();
		}.bind(this));

		Event.observe(window, 'resize', function() {
			this.positionOverlay();
		}.bind(this));

		if (this.bBlocking == false)
		{
			// add listener for closing popup
			$(this.oOverlay).observe('click', function() {
				this.hide();
			}.bind(this));
		}
	},

	/**
     * positions the overlay and popup, called after a scroll or resize
     *
	 * @param void
     * @return void
     */		
	positionOverlay: function()
	{	
		if (this.oOverlay != null)
		{	
			var iScrollX = $(document.body).cumulativeScrollOffset()[0];
			var iScrollY = $(document.body).cumulativeScrollOffset()[1];
			
			var iPopupHeight = this.oPopupNode.getHeight();
			var iPopupWidth = this.oPopupNode.getWidth();
			
			// we also nede to take in account a title node which might be above the popup node	
			
			if ($('popupTitle'))
			{
				var iTitleAbovePopup = this.oPopupNode.viewportOffset()[1] - $('popupTitle').viewportOffset()[1];
		
				if (iTitleAbovePopup > 0)
				{
					iPopupHeight += iTitleAbovePopup;	
				}
			}
			
			var iTop = iScrollY + Math.round((document.viewport.getDimensions().height - iPopupHeight) / 2);
			iTop = Math.max(iTop, 4) + (iTitleAbovePopup > 0 ? iTitleAbovePopup : 0);
			
			// position the popup's parent, left is done by css, top we have to calculate
			// initial positioning is done always, we only re-position the popup if it fits on the screen and vertical scroll is true		
					
			if (this.oPopupNode.parentNode.style.top == '' || (this.bVerticalScroll == true && 
						iPopupHeight < document.viewport.getHeight() &&
						iPopupWidth < document.viewport.getWidth()))
			{
				this.oPopupNode.parentNode.style.top = iTop.toString()+'px';
			}
			
			this.oOverlay.style.top = iScrollY.toString() + "px";
			this.oOverlay.style.left = iScrollX.toString() + "px";
			
			this.oOverlay.style.width = document.viewport.getDimensions().width.toString()+'px';
			this.oOverlay.style.height = document.viewport.getDimensions().height.toString()+'px';					
		}
	}
	
});




var ZigAlertPopup = Class.create(ZigPopup,
{
	bBlocking: false,

	/**
     * overriden, show appropiate nodes 
     *
	 * @param void
     * @return void
     */	
	showNodes: function()
	{
		if (this.oTitleNode	)
		{
			this.oTitleNode.style.display = this.sDisplayMode;
		}
	
		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.style.display = this.sDisplayMode;
			this.setLabelCloseButton('Sluiten');
		}
	},

	/**
     * overriden, set appropiate listeners 
     *
	 * @param void
     * @return void
     */		
	setListeners: function($super)
	{
		$super();
		
		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.observe('click', function() 
			{
				this.hide();
				
			}.bind(this));	
		}
		
		if (this.oHeaderCloseNode)
		{
			this.oHeaderCloseNode.observe('click', function() 
			{
				this.hide();
			}.bind(this));	
		}		
	}

});

var ZigConfirmPopup = Class.create(ZigPopup,
{
	bBlocking: true,
	
	oCloseHandler: function()
    {
   
    },
    
	oOkHandler: function()
	{
	
	},

	/**
     * overriden, show appropiate nodes 
     *
	 * @param void
     * @return void
     */	
	showNodes: function()
	{
		if (this.oTitleNode	)
		{
			this.oTitleNode.style.display = this.sDisplayMode;
		}
		
		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.style.display = this.sDisplayMode;
			this.setLabelCloseButton('OK');
		}
		
		if (this.oOkButtonNode)
		{
			this.oOkButtonNode.style.display = this.sDisplayMode;
			this.setLabelCloseButton('annuleren');
		}		
	},	
	
	/**
     * overriden, set appropriate listeners 
     *
	 * @param void
     * @return void
     */		
	setListeners: function($super)
	{
		$super();

		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.observe('click', function() 
			{
				this.hide();
			}.bind(this));	
		}

		if (this.oHeaderCloseNode)
		{
			this.oHeaderCloseNode.observe('click', function() 
			{
				this.hide();
			}.bind(this));	
		}	
		
		if (this.oOkButtonNode)
		{
			this.oOkButtonNode.observe('click', function() 
			{					
				this.oOkHandler.bind(this)();
			}.bind(this));	
		}			
	},	
	
	setCloseHandler: function(oAction)
    {
		this.oCloseHandler = oAction;
    },
    
	setOkHandler: function(oAction)
	{
		this.oOkHandler = oAction;
	}
});


var ZigImagePopup = Class.create(ZigPopup,
{

	/**
     * the popup's "next image" button node
     * @var object oNextImageButtonNode
     */		
	oNextImageButtonNode: null,

	/**
     * the popup's "previos image" button node
     * @var object oPreviousImageButtonNode
     */		
	oPreviousImageButtonNode: null,
	
	/**
     * the popup's thumbnails node
     * @var object oThumbnailsNode
     */		
	oThumbnailsNode: null,	
	
	/**
     * the thumbnails
     * @var array aThumbs
     */			
	aThumbs: [],

	/**
     * the images
     * @var array aImages
     */
	aImages: [],

	/**
     * the titles
     * @var array aTitles
     */
	aTitles: [],	
	
	/**
     * the currently active image
     * @var object oActiveImage
     */

	oActiveImage: null,

	/**
     * the index of the currently display thumb/image
     * @var int iCurrentImageIndex
     */
	
	iCurrentImageIndex: 0,
	

	/**
     * prepares the popup node
	 * overriden, adds image popup specific nodes
     *
	 * @param void
     * @return void
     */	
	preparePopupNode: function($super)
	{		
		// try to find nodes in this.oPopupNode
		this.oPreviousImageButtonNode	= $(this.oPopupNode.select('.ZigPopup_previousImageButton')[0]);
		this.oNextImageButtonNode		= $(this.oPopupNode.select('.ZigPopup_nextImageButton')[0]);
		this.oThumbnailsNode			= $(this.oPopupNode.select('.ZigPopup_thumbnails')[0]);
		this.oAlbumNumbersNode			= $(this.oPopupNode.select('.ZigPopup_albumNumbers')[0]);
		this.oPagingNode				= $(this.oPopupNode.select('.ZigPopup_paging')[0]);
		
		// add class
		this.oPopupNode.addClassName('popupAlbum');
		
		$super();
	
	},	

	
	/**
     * shows the overlay and popup
     * overriden, also show image
     *
	 * @param int iIndex the index of the image we want to show, default 0
     * @return void
     */			
	show: function($super, iIndex)
	{	
		this.showImage(typeof(iIndex) == 'number' && iIndex < this.aImages.length ? iIndex : 0);


		$super();

		// next/prev etc visible? only if we have more than one image
		
		if (this.oPreviousImageButtonNode)
		{
			this.oPreviousImageButtonNode.style.display	= (this.aImages.length < 2 ? 'none' : this.sDisplayMode);
		}
		
		if (this.oNextImageButtonNode)
		{
			this.oNextImageButtonNode.style.display	= (this.aImages.length < 2 ? 'none' : this.sDisplayMode);
		}
		
		if (this.oThumbnailsNode)
		{
			this.oThumbnailsNode.style.display	= (this.aImages.length < 2 ? 'none' : this.sDisplayMode);
		}
		
		if (this.oAlbumNumbersNode)
		{
			this.oAlbumNumbersNode.style.display	= (this.aImages.length < 2 ? 'none' : this.sDisplayMode);	
		}
		
		if (this.oPagingNode)
		{
			this.oPagingNode.style.display	= (this.aImages.length < 2 ? 'none' : this.sDisplayMode);				
		}
	},
	
	/**
     * overriden, show appropiate nodes 
     *
	 * @param void
     * @return void
     */	
	showNodes: function()
	{
		if (this.oTitleNode	)
		{
			this.oTitleNode.style.display = this.sDisplayMode;
		}
		
		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.style.display = this.sDisplayMode;
			this.setLabelCloseButton('Sluiten');
		}

		if (this.oHeaderCloseButtonNode)
		{
			this.oHeaderCloseButtonNode.style.display = this.sDisplayMode;
		}		
		
		if (this.oPreviousImageButtonNode)
		{
			this.oPreviousImageButtonNode.style.display = this.sDisplayMode;
			this.oPreviousImageButtonNode.observe('click', function()
			{
				this.showPreviousImage();
			}.bind(this));
		}

		if (this.oNextImageButtonNode)
		{
			this.oNextImageButtonNode.style.display = this.sDisplayMode;
			this.oNextImageButtonNode.observe('click', function()
			{
				this.showNextImage();
			}.bind(this));

		}

		if (this.oThumbnailsNode)
		{
			this.oThumbnailsNode.style.display = this.sDisplayMode;
		}		
		
		if (this.oAlbumNumbersNode)
		{
			this.oAlbumNumbersNode.style.display = this.sDisplayMode;		
		}
		
		if (this.oPagingNode)
		{
			this.oPagingNode.style.display = this.sDisplayMode;
		}
			
	},

	/**
     * overriden, set appropriate listeners 
     *
	 * @param void
     * @return void
     */		
	setListeners: function($super)
	{
		$super();

		if (this.oCloseButtonNode)
		{
			this.oCloseButtonNode.observe('click', function() 
			{	
				// custom close handler defined? if not, default "hide" action
				if (this.oCloseHandler)
				{
					this.oCloseHandler.bind(this)();
				}
				else
				{
					this.hide();
				}
				
			}.bind(this));	
		}

		if (this.oHeaderCloseNode)
		{
			this.oHeaderCloseNode.observe('click', function() 
			{
				this.hide();
			}.bind(this));	
		}	
		
	},	

	/**
     * set thumbs and images from provided selector
     * we expect <a rel="someselector" src="http://someimage"><img src="http://somethumb" /></a>
     *
	 * @param string sSelector
     * @return void
     */	
	setImagesFromSelector: function (sSelector)
	{	
		var aThumbs = [];
		var aImages = [];
		this.aTitles = [];
		
		var aAnchors = $$('a([rel^='+sSelector+'])');
			
		for (var i = 0 ; i < aAnchors.length ; i++)
		{
			var sRel = aAnchors[i].rel;
			var aMatches = sRel.match(/\[(.*)\]$/);
			var sTitle = null;
			
			if (aMatches !== null)
			{
				sTitle = aMatches[1];
			}

			this.aTitles.push(sTitle);
			
			aImages.push(aAnchors[i].href);		
			aThumbs.push(aAnchors[i].select('img')[0].src);
		}
		
		this.setThumbs(aThumbs);
		this.setImages(aImages);
	},
	
	/**
     * set thumbs
     *
	 * @param array aThumbs
     * @return void
     */	
	setThumbs: function(aThumbs)
	{	
		var aThumbsFixed = [];
		
		for (var i = 0 ; i < aThumbs.length ; i++)
		{
			if (typeof(aThumbs[i]) == 'object' && typeof(aThumbs[i].src) == 'string')
			{
				aThumbsFixed.push(aThumbs[i].src);
			}
			else if (typeof(aThumbs[i]) == 'string')
			{
				aThumbsFixed.push(aThumbs[i]);
			}
		}

		this.aThumbs = aThumbs;
		this.updateAlbumNumbers();
		this.showPaging();
		this.showThumbs();
	},

	
	/**
     * set images
     *
	 * @param array aImages
     * @return void
     */	
	setImages: function(aImages)
	{
		this.aImages = aImages;
	},

	/**
     * set popup width based on current image and other factors
     *
	 * @param void
     * @return void
     */		
	setWidthFromImage: function()
	{
		var iWidth = this.oActiveImage.getDimensions().width;

		// image too large? resize?
		var iMaxWidth = document.viewport.getDimensions().width-64;
		if (iWidth > iMaxWidth)
		{
			var iWidth = iMaxWidth;
			this.oActiveImage.style.width = iMaxWidth.toString() + 'px';				
		}	
		
		// width can't be smaller than thumbnail node
		if (this.oThumbnailsNode)
		{
			var iThumbnailWidth = this.oThumbnailsNode.getDimensions().width;					
			var iWidth = Math.max(iWidth, iThumbnailWidth);
		}
		
		if (iWidth > 0)
		{
			this.setWidth(iWidth, function() 
			{
				this.style.visibility = 'visible';
			}.bind(this.oActiveImage));		
		}
	},
	
	
	/**
     * shows image
     *
	 * @param int iIndex
     * @return void
     */		
	showImage: function(iIndex)
	{
		this.setTitle(this.aTitles[iIndex] == null ? this.sDefaultTitle : this.aTitles[iIndex]);

		this.oContentNode.innerHTML = '';

		this.oActiveImage = $(document.createElement('img'));
		this.oActiveImage.src = this.aImages[iIndex];
			
		// first we add the image invisible, so we can determine its size
		this.oActiveImage.style.visibility = 'hidden';
		
		this.oContentNode.appendChild(this.oActiveImage);	

		
		// try to set image width now, if image is already loaded
		this.setWidthFromImage();
		
		// also place listener in case the image still has to be loaded
		this.oActiveImage.observe('load', function(oEvent)
		{	
			this.setWidthFromImage.bind(this).delay(0.1);

		}.bind(this));
		
		// last resort, assume image is loaded half a second from now
		this.setWidthFromImage.bind(this).delay(0.5);
		
		this.iCurrentImageIndex = iIndex;
		this.updatePaging();
		this.updateAlbumNumbers();
		this.updateThumbs();	
		this.updateButtons();
	},

	/**
     * shows next image
     *
	 * @param void
     * @return void
     */		
	showNextImage: function()
	{	
		if (this.iCurrentImageIndex < (this.aThumbs.length-1))
		{
			this.showImage(this.iCurrentImageIndex+1);
		}
	},
	
	/**
     * shows previous image
     *
	 * @param void
     * @return void
     */		
	showPreviousImage: function()
	{	
		if (this.iCurrentImageIndex > 0)
		{
			this.showImage(this.iCurrentImageIndex-1);
		}
	},		
	
	/**
     * shows paging, creates links
     *
	 * @param array void
     * @return void
     */	
	showPaging: function()
	{

		// remove all li's
		if (this.oPagingNode)
		{
			this.oPagingNode.select('li').each(function(oEl) {
				
				oEl.parentNode.removeChild(oEl);
			});
		}

		// add items for earch li
		if (this.oPagingNode)
		{
			for (var i = 0 ; i < this.aThumbs.length ; i++)
			{
				var oListItem = document.createElement('li');
				var oAnchor = document.createElement('a');
				oAnchor.href = '#';
				oAnchor.innerHTML = (i+1).toString();
		
				$(oAnchor).observe('click', function(oEvent) { 
				
					var oTarget = oEvent.target;
					
					// determine index of anchor we just clicked
					var aAnchors = this.oPagingNode.select('a');
					
					for (var iClickedIndex = 0 ; iClickedIndex < aAnchors.length ; iClickedIndex++)
					{
						if (aAnchors[iClickedIndex] == oTarget)
						{
							break;
						}
					}
	
					this.showImage(iClickedIndex);
	
				}.bind(this));
				oListItem.appendChild(oAnchor);
				this.oPagingNode.appendChild(oListItem);
			}
		
			this.updatePaging();
		}
	},

	/**
     * updates next, previous buttons with correct classes
     *
	 * @param void
     * @return void
     */		
	updateButtons: function()
	{	
		if (this.oPreviousImageButtonNode)
		{
			this.oPreviousImageButtonNode.removeClassName('disabledButton');
			
			if (this.iCurrentImageIndex == 0)
			{
				this.oPreviousImageButtonNode.addClassName('disabledButton');
			}		
		}
		
		if (this.oNextImageButtonNode)
		{
			this.oNextImageButtonNode.removeClassName('disabledButton');

	
			if (this.iCurrentImageIndex == (this.aImages.length-1))
			{
				this.oNextImageButtonNode.addClassName('disabledButton');
			}	
		}
	},

	/**
     * overridden, left/right for browsing through images
     *
	 * @param object oEvent the keypress' event
     * @return void
     */
	handleKeypress: function($super, oEvent)
	{		
		if (oEvent.keyCode == Event.KEY_LEFT)
		{
			this.showPreviousImage();
		}

		if (oEvent.keyCode == Event.KEY_RIGHT)
		{
			this.showNextImage();
		}	
		
		$super(oEvent);
	},	
	
	/**
     * updates paging, makes current image link active
     *
	 * @param array void
     * @return void
     */		
	updatePaging: function()
	{
		if (this.oPagingNode)
		{
			var iCounter = 0;
			
			this.oPagingNode.select('li').each(function(oEl) 
			{
				oEl.removeClassName('active');
				
				if (iCounter == this.iCurrentImageIndex)
				{
					oEl.addClassName('active');
				}
				
				iCounter++;
				
			}.bind(this));
		}		
	},

	
	/**
     * shows paging, creates images
     *
	 * @param array void
     * @return void
     */	
	
	showThumbs: function()
	{	
		if (this.oThumbnailsNode)
		{	
			this.oThumbnailsNode.innerHTML = '';

			for (var i = 0 ; i < this.aThumbs.length ; i++)
			{
				var oImage = document.createElement('img');		
				
				// invisible for now, we will make it visible when it's resized
				oImage.style.visibility = 'hidden';
				
				$(oImage).observe('load', function(oEvent)
				{	
					// image loaded? resize in a moment...
					this.resizeThumbs.bind(this).delay(1);
				}.bind(this));
				
				oImage.src = this.aThumbs[i];	

				
				$(oImage).observe('click', function(oEvent)
				{
					var sTargetImageSrc = oEvent.target.src;
					var iClickedIndex = this.aThumbs.indexOf(sTargetImageSrc);
					this.showImage(iClickedIndex);

				}.bind(this));
			
				this.oThumbnailsNode.appendChild(oImage);	
			}
			
			this.updateThumbs();
		}
	},

	/**
     * resize thumbnails 
     * height dictated by parent node MINUS room for scrollbar
     *
	 * @param array void
     * @return void
     */			
	resizeThumbs: function()
	{	
		$(this.oThumbnailsNode).select('img').each(function(oImage) {
			
			oImage = $(oImage);
			
			var iTargetHeight = parseInt(oImage.parentNode.getStyle('height'), 10) - 24;					
			var fRatio = oImage.getDimensions().width / oImage.getDimensions().height;
			var iTargetWidth = Math.round(fRatio*iTargetHeight);
			
			oImage.style.width = iTargetWidth.toString() + 'px';
			oImage.style.height = iTargetHeight.toString() + 'px';
			
			oImage.style.visibility = 'visible';	

			// make sure correct thumb is visible
			// this.oThumbnailsNode.select('img')[this.iCurrentImageIndex].scrollIntoView();		
		}.bind(this));
	},


	/**
     * updates thumbs, makes current image link active and scrolls appropiate thumb into view
     *
	 * @param array void
     * @return void
     */		
	updateThumbs: function()
	{
		if (this.oThumbnailsNode)
		{				
			var iCounter = 0;
			
			// make sure correct thumb is visible
			// this.oThumbnailsNode.select('img')[this.iCurrentImageIndex].scrollIntoView();
			
			this.oThumbnailsNode.select('img').each(function(oEl) 
			{	
				oEl.removeClassName('active');
				
				if (iCounter == this.iCurrentImageIndex)
				{
					oEl.addClassName('active');
				}
				
				iCounter++;
			}.bind(this));
		}		
	},

	updateAlbumNumbers: function()
	{	
		if (this.oAlbumNumbersNode)
		{
			this.oAlbumNumbersNode.innerHTML = (this.iCurrentImageIndex+1).toString() + ' / ' + this.aThumbs.length.toString();

		}
	}
});
