/*
// GH-Slides v 0.1.0

// Last modified: 2008.03.05 by SK


// Usage explanation
// ==============================

// Required files:

// GH_events.js
// GH_tools.js
// GH_slides.js
// GH_slides.css

<script type="text/javascript" src="GH_events.js"></script>
<script type="text/javascript" src="GH_tools.js"></script>
<script type="text/javascript" src="GH_slides.js"></script>
<style type="text/css">
@import url('GH_slides.css');
</style>


// Add a new slideshow:

// Javascript:

GHslideShow.add('myNewSlideShow',5000,5);

// 1st argument	:	html-element-ID of the main slidecontainer
// 2nd argument	:	milleseconds before moving on to the next slide
// 3rd argument	:	milliseconds to delay the fading; how long to wait to show the next percentage of opacity

// HTML:

<div id="myNewSlideShow">

    <!-- summary Template start -->
    <div class="GHslideViewer">
        
        <div class="GHslideNav"></div>
        <div class="GHslideContainer">
            
            <div class="GHslide">.....This is one slide, there can be many more!....</div>
            <div class="GHslide">.....This is a second slide, there can be many more!....</div>
            
        </div>
    
    </div>
    <!-- summary Template start -->

</div>

// CSS:
// Modify CSS as you like by e.g.

#myNewSlideShow .GHslideContainer
{
	width:300px;
	height:75px;
}

#myNewSlideShow .GHslideContainer
{
	border:3px solid #000;
}
// ..and so on!


// You can add several slideshows, just give the main container a new ID, e.g. 'myNewSlideShow_2', keep the rest of the HTML structure the same!

*/

function GHslides()
{
	
	
	// Class that sets values for an instance of class-factory "GHslides"
	function instanceProps( instName , showDelay , fadeDelay )
	{
		// Properties
		this.iFadeDelay			 = fadeDelay;
		this.iShowDelay			 = showDelay;
		this.aProps				 = [];
		this.sInstName			 = instName;
		this.oSlideShowEl		 = {};
		this.oFaderEl			 = {};
		this.oPicFrameEl		 = {};
		this.iCurrentSlideIndex	 = 0;
		this.iTimeout			 = 0;
		
		// Methods
		this.init				 = funcInitialize;
		this.buildNav			 = funcBuildNav;
		this.buildPicFrame		 = funcBuildPicFrame;
		this.buildFader			 = funcBuildFader;
		this.createOnclickNavSC	 = funcCreateOnclick_NavigateShortcut;
		this.createOnclickNavDN	 = funcCreateOnclick_NavigateDirection;
		this.onFinishFade		 = funcOnFinishFade;
		this.getSafeIndex		 = funcGetSafeSlideIndex;
		this.start				 = funcStartSlideshow;
		this.show				 = funcShowSlide;
		this.activate			 = funcActivate;
	}
	
	
	// Class
	function slide( instName )
	{
		// Properties
		this.sInstName			 = instName;
		this.oSlideEl			 = {};
		
		// Methods
		this.init				 = funcInitializeSlide;
	}
	
	
	// Adds instance names to the slide-show-instances-container array
	// They have no value yet, we wait until the page is fully loaded and calls GHslides::funcLoadInstances()
	// Used as a method of the GHslides object
	function funcAddInstance( instName , showDelay , fadeDelay )
	{
		var oMyInstance = this.createInstance( instName , showDelay , fadeDelay );
	}
	
	
	// The page is loaded; time to load all slide-show-instances and initiate them (giving them the right properties)
	// Used as a method of the GHslides object
	function funcLoadInstances()
	{
		if( !W3CDOM ) return;//No need to continue, too bad...
		
		for(var instName in this.aInstances)
		{
			var oMyInstance = this.aInstances[ instName ];
			oMyInstance.init();
		}
	}
	
	
	// Creates an instance of class instanceProps, adds it to the instances array
	// Used as a method of the GHslides object
	function funcCreateInstance( instName , showDelay , fadeDelay )
	{
		this.aInstances[ instName ] = new instanceProps( instName , showDelay , fadeDelay );
		return this.aInstances[ instName ];
	}
	
	
	// Initializes a slide-show instance (member of array GHslides::aInstances)
	// Used as a method of the GHslides object
	function funcInitialize()
	{
		// Access HTML element that holds this slide-show
		this.oSlideShowEl = id(this.sInstName);
		// Cancel process if it doesn't exist
		if( !this.oSlideShowEl )
		{
			debug('"this.oSlideShowEl" is not available in GHslides::funcInitialize() ');
			return;
		}
		
		var aSlides = getElementsByClassName('GHslide',this.oSlideShowEl);
		var oSlide;
		var sSlideInstName;
		for( var i=0; i<aSlides.length; i++ )
		{
			sSlideInstName = this.sInstName + '_item_' + (i+1);
			aSlides[i].id = sSlideInstName;
			oSlide = new slide( sSlideInstName );
			oSlide.init();
			this.aProps.push(oSlide);
		}
		
		this.buildNav();
		this.buildPicFrame();
		this.buildFader();
		this.start();
	}
	
	
	// Initializes a slide instance (member of array GHslides::aInstances::aProps)
	// Used as a method of the GHslides::slide object
	function funcInitializeSlide()
	{
		// Access HTML element that holds this slide
		this.oSlideEl = id(this.sInstName);
		// Cancel process if it doesn't exist
		if( !this.oSlideEl )
		{
			debug('"this.oSlideEl" is not available in GHslides::funcInitializeSlide() ');
			return;
		}
		
		this.oSlideEl.style.display = 'none';
		
	}
	
	
	// Shows a slide as being active
	// Used as a method of the GHslides::instanceProps object
	function funcActivate( slideIndex )
	{
		// Access HTML element that holds this slide
		var navContainer = getElementsByClassName('GHslideNav',this.oSlideShowEl)[0];
		// Cancel process if it doesn't exist
		if( !navContainer )
		{
			debug('"navContainer" is not available in GHslides::funcActivate() ');
			return;
		}
		
		var navItems = getElementsByClassName('navNum',navContainer);
		// Reset all
		for( var i=0; i<navItems.length; i++ )
		{
			if( navItems[i].className.indexOf('active') != -1 )
				navItems[i].className = navItems[i].className.replace(/\sactive/,'');
		}
		
		navItems[slideIndex].className += ' active';
		
	}
	
	
	// Builds the slideshow navigation
	// Used as a method of the GHslides::instanceProps object
	function funcBuildNav()
	{
		// Access HTML element that holds this slide
		var navContainer = getElementsByClassName('GHslideNav',this.oSlideShowEl)[0];
		// Cancel process if it doesn't exist
		if( !navContainer )
		{
			debug('"navContainer" is not available in GHslides::funcBuildNav() ');
			return;
		}
		
		
		var navList = document.createElement( 'ul' );
		
		// Nav button-prev
		var navBtnPrevItem = document.createElement( 'li' );
		var navBtnPrev = document.createElement( 'a' );
		navBtnPrev.className = 'navBtn prev';
		navBtnPrev.href = '#';
		navBtnPrev.onclick = this.createOnclickNavDN(-1);
		navBtnPrev.appendChild( document.createTextNode( '‹' ) );
//		navBtnPrev.appendChild( document.createTextNode( '\u00a0' ) );
		// Add to DOM
		navBtnPrevItem.appendChild( navBtnPrev );
		navList.appendChild( navBtnPrevItem );
		
		// Nav numbers
		var navNumberItem;
		var navNumber;
		for( var i=0; i<this.aProps.length; i++)
		{
			var navNumberItem = document.createElement( 'li' );
			navNumber = document.createElement( 'a' );
			navNumber.className = 'navNum';
			navNumber.href = '#';
			navNumber.onclick = this.createOnclickNavSC( i+1 );
			navNumber.appendChild( document.createTextNode( i+1 ) );
			// Add to DOM
			navNumberItem.appendChild( navNumber );
			navList.appendChild( navNumberItem );
		}
		
		// Nav button-next
		var navBtnNextItem = document.createElement( 'li' );
		var navBtnNext = document.createElement( 'a' );
		navBtnNext.className = 'navBtn next';
		navBtnNext.href = '#';
		navBtnNext.onclick = this.createOnclickNavDN(+1);
		navBtnNext.appendChild( document.createTextNode( '›' ) );
		// Add to DOM
		navBtnNextItem.appendChild( navBtnNext );
		navList.appendChild( navBtnNextItem );
		
		navContainer.appendChild( navList );
	}
	
	
	// Builds the element that holds the displaying slide
	// Used as a method of the GHslides::instanceProps object
	function funcBuildPicFrame()
	{
		// Access HTML element that represents the slide-container
		var slideContainer = getElementsByClassName('GHslideContainer',this.oSlideShowEl)[0];
		// Cancel process if it doesn't exist
		if( !slideContainer )
		{
			debug('"slideContainer" is not available in GHslides::funcBuildPicFrame() ');
			return;
		}
		
		var picFrameId = this.sInstName + '_picFrame';
		
		// Building the frame that holds the displaying picture
		var picFrame = document.createElement( 'div' );
		picFrame.id = picFrameId;
		picFrame.className = 'GHpicFrame';
		
		// Add to DOM
		slideContainer.appendChild( picFrame );
		
		// Save it
		this.oPicFrameEl = picFrame;
	}
	
	
	// Builds the element that fades in the new slide
	// Lays on top of 'picFrame'
	// A slide fades in in this element 'fader', then it moves over to 'picFrame'
	// Used as a method of the GHslides::instanceProps object
	function funcBuildFader()
	{
		// Access HTML element that represents the slide-container
		var slideContainer = getElementsByClassName('GHslideContainer',this.oSlideShowEl)[0];
		// Cancel process if it doesn't exist
		if( !slideContainer )
		{
			debug('"slideContainer" is not available in GHslides::funcBuildFader() ');
			return;
		}
		
		var faderId = this.sInstName + '_fader';
		
		// Building the frame that holds the displaying picture
		var fader = document.createElement( 'div' );
		fader.id = faderId;
		fader.className = 'GHfader';
		fader.onfinish = this.onFinishFade;
		fader.myInstName = this.sInstName;
		
		// Add to DOM
		slideContainer.appendChild( fader );	
		
		// Save it
		this.oFaderEl = fader;	
	}
	
	
	// Returns a slideIndex of the GHslides::aProps array, which always exists:
	// When a given index is too low or too high, the function respectively returns the last or the first index
	// Used as a method of the GHslides::instanceProps object
	function funcGetSafeSlideIndex( slideIndex )
	{
		var iMySlideIndex = slideIndex;
		
		// Make a circle - you never will reach the end by clicking the prev-next buttons
		if( iMySlideIndex >= this.aProps.length )
			iMySlideIndex = 0;
		else
		if( iMySlideIndex < 0 )
			iMySlideIndex = this.aProps.length - 1;
		
		return iMySlideIndex;
	}
	
	
	// Keeps the show sliding; shows the requested slide, ad prepares to show the next one
	// Used as a method of the GHslides object
	function funcLoopSlideshow( instName , slideIndex )
	{
		var oMyInstance = this.aInstances[ instName ];
		var sMySlide = oMyInstance.sInstName + '_item_' + (slideIndex + 1);
		
		oMyInstance.show( sMySlide , slideIndex );
		
		// Roll the show
		var iMyNextSlideIndex = oMyInstance.getSafeIndex( slideIndex + 1 );
		oMyInstance.iTimeout = setTimeout('GHslideShow.loop( "' + instName + '" , ' + iMyNextSlideIndex + ' )',oMyInstance.iShowDelay);
	}
	
	
	// Next/Previous navigation to browse through the slideshow
	// Used as a method of the GHslides object
	function funcNavigateDirection( instName , direction , htmlEl )
	{
		var oMyInstance = this.aInstances[ instName ];
		var iMySlideIndex = oMyInstance.getSafeIndex( oMyInstance.iCurrentSlideIndex + direction );
		
		// Cancel the current slideshow-loop
		try{clearTimeout(oMyInstance.iTimeout);}
		catch(e){}	
		
		if( oMyInstance.oFaderEl.finishedFade )
			this.loop( oMyInstance.sInstName , iMySlideIndex );// Current slide has finished fading in
		else
			oMyInstance.oFaderEl.desiredSlide = iMySlideIndex+1;// Wait for the current slide to fade in, move to requested slide afterwards
	}
	
	
	// Shortcut navigation to browse through the slideshow, by clicking the numbers
	// Used as a method of the GHslides object
	function funcNavigateShortcut( instName , slideNum , htmlEl )
	{
		var oMyInstance = this.aInstances[ instName ];
		
		// Cancel the current slideshow-loop
		try{clearTimeout(oMyInstance.iTimeout);}
		catch(e){}	
		
		if( oMyInstance.oFaderEl.finishedFade )
			this.loop( oMyInstance.sInstName , slideNum-1 );// Current slide has finished fading in
		else
			oMyInstance.oFaderEl.desiredSlide = slideNum;// Wait for the current slide to fade in, move to requested slide afterwards
	}
	
	
	// Create clickevent - moving scope : 
	// '_scope' in this function contains the object-scope that calls this function
	// 'this' inside this functions variable (typeOf=function) called 'call_onclick' contains the object-scope that the eventhandler is assigned to (e.g. <a/>-tag)
	// Used as a method of the GHslides::instanceProps object
	function funcCreateOnclick_NavigateShortcut( slideNum )
	{
		var _scope = this;
		var call_onclick = function()
		{
			GHslideShow.navigateShortcut(_scope.sInstName,slideNum,this);
			return false;
		}
		return call_onclick;
	}
	
	
	// Create clickevent - moving scope : 
	// '_scope' in this function contains the object-scope that calls this function
	// 'this' inside this functions variable (typeOf=function) called 'call_onclick' contains the object-scope that the eventhandler is assigned to (e.g. <a/>-tag)
	// Used as a method of the GHslides::instanceProps object
	function funcCreateOnclick_NavigateDirection( direction )
	{
		var _scope = this;
		var call_onclick = function()
		{
			GHslideShow.navigateDirection(_scope.sInstName,direction,this);
			return false;
		}
		return call_onclick;
	}
	
	
	// Start rolling the slideshow...
	// Used as a method of the GHslides::instanceProps object
	function funcStartSlideshow()
	{
		// Make this 'GHslideViewer' visible again
		var slideViewer = getElementsByClassName('GHslideViewer',this.oSlideShowEl)[0];
		slideViewer.style.display = 'block';
		GHslideShow.loop( this.sInstName , 0 );
	}
	
	
	// Shows the coming slide 'id(slide)' in the slideshow
	// Used as a method of the GHslides::instanceProps object
	function funcShowSlide( slide , slideIndex )
	{
		if( !this.oFaderEl )
		{
			debug('"this.oFaderEl" is not available in GHslides::funcShowSlide() ');
			return;	
		}
		if( !id(slide) )
		{
			debug('"id(slide)" is not available in GHslides::funcShowSlide() ');
			return;	
		}
		
		this.oFaderEl.finishedFade = false;
		
		changeOpac( 0 , this.oFaderEl.id );
		this.oFaderEl.style.display = 'block';
		this.oFaderEl.appendChild( id(slide) );
		this.iCurrentSlideIndex = slideIndex;
		id(slide).style.display = 'block';
		this.activate(slideIndex);
		fade( this.oFaderEl.id , 0 , 100 , this.iFadeDelay , function(){GHslideShow.onFinishFade(ob);} );
	}
	
	
	// Called when a fade in of a slide is finished
	// Used as a method of the slideshows 'fader' object
	function funcOnFinishFade()
	{
		var oMyInstance =  GHslideShow.aInstances[ this.myInstName ];
		var oSlideEl =  this.firstChild;
		var oPicFrameEl = oMyInstance.oPicFrameEl;
		var oCurrentPicFramePicEl = oPicFrameEl.firstChild;
		var oSlideContainerEl = getElementsByClassName('GHslideContainer',oMyInstance.oSlideShowEl)[0];
		var iSlideNum = oSlideEl.id.substr(oSlideEl.id.lastIndexOf('_')+1);
		
		this.finishedFade = true;
		
		//Empty the PicFrame; move slide from picFrame to the slideContainer
		if( oCurrentPicFramePicEl )
			oSlideContainerEl.appendChild( oCurrentPicFramePicEl );
		
		// Move the slide from the Fader to the PicFrame
		// Now the Fader is empty, ready to recieve the next slide
		if( oSlideEl )
			oPicFrameEl.appendChild( oSlideEl );
		this.style.display = 'none';
		
		// When this slide was fading in, a new one was requested (this.desiredSlide)
		// Now this fade is finished, the next timeOut has been skipped
		// So lets move on to the requested slide
		if( this.desiredSlide )
		{
			GHslideShow.loop( oMyInstance.sInstName , this.desiredSlide - 1 );// -1 becuase the loop()-method automatically adds +1
			this.desiredSlide = false;
		}
		
	}
	
	
	
	/*
	<!-- DEBUGGING -->
	<div id="debugField"></div>
	*/
	function debug( msg )
	{
		var oDebugField = id( 'debugField' );
		if( !oDebugField ) return;
		oDebugField.innerHTML += msg + '<br />';
	}
	
	
	
	
	
	// Properties
	this.aInstances			 = [];
	
	
	// Methods
	this.add				 = funcAddInstance;
	this.loadInstances		 = funcLoadInstances;
	this.createInstance		 = funcCreateInstance;
	this.navigateDirection	 = funcNavigateDirection;
	this.navigateShortcut	 = funcNavigateShortcut;
	this.loop				 = funcLoopSlideshow;
	
	
	
}

var GHslideShow = new GHslides();

addEvent( window , 'load' , function(){GHslideShow.loadInstances();} );