/*

$Id: slideshow.js,v 1.15 2011/11/16 14:49:40 JiriF Exp $

--------------------------------------------------------------------------------
Slideshow object for presenting flash or alternate contents in Oriflame v3.5
frontpage. Requires jQuery, jQuery easing, swfObject to be loaded before to
work properly. Also the common functions JS used for writing messages into
console should be present.
--------------------------------------------------------------------------------

<![CDATA[

*/
var ss = slideshow = createDebugObject( {
	
	// object name used for creating debug cookie name, appears in messages, too
	objectName : "slideshow",
	// main area size
	width : 0,
	height : 0,
	// display options, we require flash player 10+ to be supported
	showFlash : swfobject.hasFlashPlayerVersion( "10" ),
	showNavigation : true,
	rewindAtEnd : true,
	// when true, the first slide will be shown in final place also when loading
	showFirstSlideImmediatelly : true,
	// default flash object attributes
	flashAttributes : {
		wmode : "transparent",
		allowfullscreen : true,
		bgcolor : "#ffffff",
		quality : "high",
		allowscriptaccess : "sameDomain"
	},
	// default animation settings
	animation : {
		type : "move",
		easing : "easeInOutQuint",
		duration : 1.5,
		slideSpacing : 13,
		stopWhenOneSlide : true,
		previousFromLeft : true,
		onlyNavigationClickedFromLeft : true,
		running : false
	},
	// navigation button settings
	navigation : {
		// when true, navigation will be disabled during loading
		disableWhenLoading : false,
		// when true, navigation will be disabled during animation
		disableWhenAnimating : false,
		// set to true just after some navigation button was clicked
		clicked : false,
		// set to true when slides are not animating or loading
		enabled : false
	},
	// by default no navigation buttons are displayed when only 1 slide exists
	hideNavigationWhenOneSlide : true,
	// current holder inside main area; 2 holders are replaced periodically
	currentHolder : -1,
	// the other holder, which is not "current"
	otherHolder : -1,
	// slide content holders
	holders : {},
	// showing previous slide indication useful for some animation types
	showingPrevious : false,
	// slide settings
	slide : {
		// this gets set to true after showing the first slide
		firstShown : false,
		// current slide, changes while playback
		current : -1,
		// set to a number equal or greater than 0 when navigation was clicked
		// when animation is in progress, equals to -1 otherwise
		scheduled : -1,
		// autoadvancing of slides in HTML version
		timeout : 6,
		// just a variable to store timeout ID
		timeoutStorage : 0,
		// loaded indication
		loaded : false,
		loading : false
	},
	// slide contents configuration, should be overwritten in init()
	slides : [],
	// loading mask configuration
	mask : {
		background : "#000000",
		image : "/image/common/loading-windows.gif",
		imageWidth : 100,
		imageHeight : 33
	},
	// HTML hover area opacity limit
	hoverOpacity : 0.3,
	// automatic start of playback after init
	autoPlay : true,
	// playback indication
	stopped : false,
	receivedSlideFinishedEvent : false,
	// wrapper padding
	padding : 11,
	// element selectors
	sel : {
		wrapper : "#main-area-wrapper",
		mainArea : "#main-area",
		holder1 : "#holder1",
		holder2 : "#holder2",
		mask : "#main-area-mask",
		navigation : "#main-area-navigation"
	},
	
	// init --------------------------------------------------------------------
	
	/**
	 * Init function can receive startup property values in options object.
	 */
	init : function( options ) {
		if( ! options ) {
			ss.e( "Empty data for initialization." );
			return;
		}
		// initialize properties with provided data
		// TODO : better control of values specified in options
		for( var p in options ) ss[ p ] = options[ p ];
		// just to be sure that running property exists
		if( ! ss.animation.running ) ss.animation.running = false;
		// check slides settings
		ss.checkSlides();
		// initialize main HTML structures
		var w = jQuery( ss.sel.wrapper );
		w.append( "<div id='" + ss.sel.mainArea.substr( 1 ) + "'></div>" );
		// mask initialization for some animation types
		if( typeof ss.animation.type != "undefined" && ss.animation.type == "fade" )
			w.append( "<div id='" + ss.sel.mask.substr( 1 ) + "'></div>" );
		w.append( "<div id='" + ss.sel.navigation.substr( 1 ) + "'></div>" );
		// holders initialization
		for( var i = 1; i < 3; i++ ) {
			jQuery( ss.sel.mainArea ).append(
				"<div id='" + ss.sel[ "holder" + i ].substr( 1 ) + "' class='holder'></div>"
			);
			ss.holders[ i ] = jQuery( ss.sel[ "holder" + i ] );
		}
		// init default size from the main area element
		ss.width = jQuery( ss.sel.mainArea ).width();
		ss.height = jQuery( ss.sel.mainArea ).height();
		// init default height when set
		ss.heightSetup();
		// setup navigation
		ss.navigationSetup();
		// auto playback start
		if( ss.autoPlay ) ss.play();
	},
	
	/**
	 * Sets startup height of the main area. By default called in init. Could be
	 * called immediatelly after slideshow object definition.
	 */
	heightSetup : function() {
		var h = Math.round( ss.height );
		if( ! h ) return;
		var t = Math.round( ( h - ss.mask.imageHeight ) / 2 ) + "px";
		jQuery( ss.sel.wrapper ).css( { height : ( h + "px" ) } );
		jQuery( ss.sel.mainArea ).css( { height : ( h + "px" ) } );
		jQuery( ss.sel.holder1 ).css( { height : ( h + "px" ) } );
		jQuery( ss.sel.holder2 ).css( { height : ( h + "px" ) } );
		jQuery( ss.sel.mask ).css( { height : ( h + "px" ) } );
		jQuery( ss.sel.mask + " img" ).css( { top : t } );
	},

	/**
	 * Creates navigation buttons and applies event handlers to them.
	 */
	navigationSetup : function() {
		ss.i( "Navigation setup ..." );
		if( ! ss.showNavigation || ! ss.slides || ! ss.slides.length ) return;
		// no navigation when only one slide
		if( ss.hideNavigationWhenOneSlide && ss.slides.length == 1 ) return;
		var n = jQuery( ss.sel.navigation );
		for( var i = 0; i < ss.slides.length; i++ )
			jQuery( "<a>" + ( i + 1 ) + "</a>" ).data( {
				slide : i
			} ).click( function() {
				var slide = jQuery( this ).data( "slide" );
				var enabled = jQuery( this ).data( "enabled" );
				ss.i( "Slide clicked : " + ( slide + 1 ) + ", enabled : " + enabled );
				if( slide == ss.slide.current || ! enabled ) return;
				ss.navigation.clicked = true;
				if( ss.animation.running || ss.slide.loading ) {
					ss.slide.scheduled = slide;
					ss.i(
						"Animation or loading data currently in progress. Slide " +
						( slide + 1 ) + " scheduled to be shown later."
					);
				} else
					ss.showSlide( slide );
			} ).appendTo( n );
		ss.setNavigationEnabled( ss.navigation.enabled );
	},
	
	/**
	 * Helper function to enable / disable all navigation buttons. Navigation
	 * should be enabled only when animation or loading is not in progress.
	 */
	setNavigationEnabled : function( value ) {
		value = Boolean( value );
		if( ss.navigation.enabled == value ) return;
		ss.navigation.enabled = value;
		var n = jQuery( ss.sel.navigation + " a" );
		n.data( "enabled", ss.navigation.enabled );
		if( ss.navigation.enabled ) n.addClass( "enabled" ); else n.removeClass( "enabled" );
		ss.i( "Navigation " + ( ( ss.navigation.enabled ) ? "enabled." : "disabled." ) );
	},
	
	// slides ------------------------------------------------------------------
	
	/**
	 * Checks slides settings.
	 */
	checkSlides : function() {
		for( var i = 0; i < ss.slides.length; i++ ) {
			ss.slides[ i ].loaded = false;
			ss.slides[ i ].type = ( ss.slides[ i ].flashUrl ) ? "flash" : "image";
			ss.slides[ i ].url = ( ss.slides[ i ].flashUrl ) ?
				ss.slides[ i ].flashUrl : ss.slides[ i ].imageUrl;
			if( ! ss.slides[ i ].linkTarget )
				ss.slides[ i ].linkTarget = "_self";
			if( ! ss.slides[ i ].timeout )
				ss.slides[ i ].timeout = ss.slide.timeout || 6;
		}	
	},

	/**
	 * Show selected slide according to the specified ID. ID is numeric index
	 * in slides configuration array. Starts loading a hidden holder contents or
	 * shows the first slide immediatelly when configured this way.
	 */
	showSlide : function( id ) {
		if( isNaN( id ) ) return;
		// when only one slide and it's already shown, do no further animations
		if( ss.slides.length == 1 && ss.slide.firstShown && ss.animation.stopWhenOneSlide ) return;
		if( id < 0 ) id = 0;
		if( id > ss.slides.length - 1 ) id = 0;
		ss.i(
			"Showing slide " + ( id + 1 ) +
			", current slide : " + ( ss.slide.current + 1 ) +
			", scheduled slide : " + ( ss.slide.scheduled + 1 ) +
			", navigation clicked : " + ss.navigation.clicked
		);
		// set content holder IDs
		ss.currentHolder = ( ss.currentHolder == 1 ) ? 2 : 1;
		ss.otherHolder = ( ss.currentHolder == 1 ) ? 2 : 1;
		var cHolder = ss.holders[ ss.currentHolder ];
		var oHolder = ss.holders[ ss.otherHolder ];
		// clear current holder HTML
		ss.clearHolder( cHolder );
		// set current slide
		ss.showingPrevious = ( id < ss.slide.current ) && (
			( ss.navigation.clicked && ss.animation.onlyNavigationClickedFromLeft ) ||
			! ss.animation.onlyNavigationClickedFromLeft
		);
		ss.i( "Showing previous slide : " + ss.showingPrevious );
		ss.navigation.clicked = false;
		if( ss.slide.current == ss.slide.scheduled ) ss.slide.scheduled = -1;
		ss.slide.current = id;
		ss.slide.loaded = ss.slides[ id ].loaded;
		// display the slide graphics according to slide type
		var url = ss.slides[ id ].url;
		switch( ss.slides[ id ].type ) {
			case "image" :
				ss.setupImage( url );
				ss.setupTexts();
				ss.loadingStart();
				break;
			case "flash" :
				// no flash plugin, image version setup
				if( ! ss.showFlash ) {
					ss.i( "Flash plugin not found, setting alternate content for slide " + id );
					ss.setupImage( ss.slides[ id ].imageUrl );
					ss.setupTexts();
					ss.loadingStart();
					break;
				}
				// flash plugin exists, initialize flash object
				var flashId = ss.sel[ "holder" + ss.currentHolder ].substr( 1 ) + "flash";
				cHolder.append( "<div id='" + flashId + "'></div>" );
				var attributes = ss.flashAttributes || {};
				attributes.base = url.substr(
					0, url.substr( 0, url.indexOf( ".swf" ) ).lastIndexOf( "/" )
				);
				ss.i( "flash base is " + attributes.base );
				var flashvars = ss.flashVariables( url );
				var params = ss.flashAttributes || {};
				params.wmode = "transparent";
				swfobject.embedSWF(
					url, flashId, ss.width, ss.height, "10",
					false, flashvars, params, attributes
				);
				ss.loadingStart();
				break;
			default :
				ss.e( "Unknown content type in slide " + ( id + 1 ) + ", can't be displayed." );
				ss.showNextSlide();
				return;
		}
		// display selected slide in navigation
		jQuery( ss.sel.navigation + " a" ).removeClass( "selected" );
		jQuery( ss.sel.navigation + " a:eq(" + ss.slide.current + ")" ).addClass( "selected" );
	},
	
	/**
	 * Starts slide background image download when it wasn't yet loaded. Used
	 * when slide type is image or when proper flash plugin was not detected.
	 */
	setupImage : function( url ) {
		ss.i( "Setting up banner image ..." );
		// the events are dispatched only the first time the image gets loaded
		if( ! ss.slide.loaded ) {
			ss.image = new Image();
			ss.image.onload = ss.imageLoaded;
			ss.image.onabort = ss.imageAborted;
			ss.image.onerror = ss.imageError;
			ss.image.src = url;
		}
	},
	
	/**
	 * Setup of texts and link for HTML banner version.
	 */
	setupTexts : function() {
		ss.i( "Setting up banner texts ..." );
		var cHolder = ss.holders[ ss.currentHolder ];
		var cHolderSel = ss.sel[ "holder" + ss.currentHolder ];
		var slide = ss.slides[ ss.slide.current ];
		cHolder.append(
			"<div class='hoverArea'></div>" +
			"<div class='headersArea'>" +
			( ( slide.header ) ? "<h3>" + ss.escq( slide.header ) + "<span class='bg'></span></h3>" : "" ) +
			( ( slide.subheader ) ? "<h2>" + ss.escq( slide.subheader ) + "<span class='bg'></span></h2>" : "" ) +
            "</div>" +
			( ( slide.linkUrl ) ? (
				"<a href='" + slide.linkUrl + "' target='" + slide.linkTarget + "'" +
				( ( slide.linkClass ) ? " class='" + slide.linkClass + "'" : "" ) +
				( ( slide.linkStyle ) ? " style='" + ss.escq( slide.linkStyle ) + "'" : "" ) +
				// ( ( slide.linkTitle ) ? " title='" + ss.escq( slide.linkTitle ) + "'" : "" ) +
				">&nbsp;</a>"
			) : "" )
		);
		jQuery( cHolderSel + " .hoverArea" ).css( { opacity : 0.0 } );
		jQuery( cHolderSel + " .headersArea .bg" ).css( { opacity : ss.hoverOpacity } );
		jQuery( cHolderSel + " a" ).unbind().hover(
			// over animation
			function() {
				// ss.i( "over" );
				jQuery( cHolderSel + " .headersArea .bg" ).stop().animate(
					{ opacity : 0.0 }, 500
				);
				jQuery( cHolderSel + " .hoverArea" ).stop().animate(
					{ opacity : ss.hoverOpacity }, 500
				);
			},
			// out animation
			function() {
				// ss.i( "out" );
				jQuery( cHolderSel + " .headersArea .bg" ).stop().animate(
					{ opacity : ss.hoverOpacity }, 500
				);
				jQuery( cHolderSel + " .hoverArea" ).stop().animate(
					{ opacity : 0.0 }, 500
				);
			}
		);
	},
		
	/**
	 * Returns flasvars object according to given flash movie URL and current
	 * HTML page parameters.
	 */
	flashVariables : function( url ) {
		var flashvars = {};
		// flash parameters initialization, passed into flashvars
		var pString = url.substr( url.indexOf( ".swf" ) + 5 );
		ss.i( "pString " + pString );
		// flash URL params 
		if( pString ) {
			// parse flash URL params
			var a = pString.split( "&amp;" );
			for( var i = 0; i < a.length; i++ ) {
				var b = a[ i ].split( "=" );
				if( b.length && b.length >= 2 ) {
					a[ i ] = b.splice( 0, 1 );
					a[ i ].push( ( b.length > 1 ) ? b.join( "=" ) : b[ 0 ] );
				}
			}
			// add to flashvars object
			for( i = 0; i < a.length; i++ )
				if( a[ i ].length == 2 )
					flashvars[ a[ i ][ 0 ] ] = a[ i ][ 1 ];
		}
		// page URL params are automatically transferred to flashvars
		pString = window.location.href;
		pString = pString.substr( pString.indexOf( "?" ) + 1 );
		// no &amp; entities should appear in the address bar
		pString = pString.replace( /&amp;/gi, "&" );
		ss.i( "pString " + pString );
		if( pString ) {
			// parse page URL params
			a = pString.split( "&" );
			for( i = 0; i < a.length; i++ ) {
				b = a[ i ].split( "=" );
				if( b.length && b.length >= 2 ) {
					a[ i ] = b.splice( 0, 1 );
					a[ i ].push( ( b.length > 1 ) ? b.join( "=" ) : b[ 0 ] );
				}
			}
			// add to flashvars object
			for( i = 0; i < a.length; i++ )
				if( a[ i ].length == 2 )
					flashvars[ a[ i ][ 0 ] ] = a[ i ][ 1 ];
		}
		// add online pages version
		flashvars[ "ver" ] = "v3";
		return flashvars;
	},
	
	/**
	 * Helper function for escaping quotes.
	 */
	escq : function( s ) {
		return utils.htmlEscape( s );
	},
	
	/**
	 * Show next slide according to current slide ID. When rewindAtEnd is set
	 * to true and the last slide playback is finished, slideshow goes to the
	 * first slide. Doesn't apply to flash slide type though.
	 */
	showNextSlide : function() {
		if( ! ss.slides || ! ss.slides.length ) {
			ss.e( "No slides defined, can't start slideshow." );
			return;
		}
		if( ! ss.rewindAtEnd && ( ss.slide.current == ( ss.slides.length - 1 ) ) ) {
			ss.i( "Rewinding to first slide is not set, stopping playback." );
			return;
		}
		var newSlide = ( ss.slide.current < ( ss.slides.length - 1 ) ) ?
			( ss.slide.current + 1 ) : 0;
		if( ss.slide.loading || ss.animation.running ) {
			ss.slide.scheduled = newSlide;
			ss.i(
				"Animation or loading data currently in progress. Slide " +
				( newSlide + 1 ) + " scheduled to be shown later."
			);
		} else
			ss.showSlide( newSlide );
	},
	
	// image event procedures --------------------------------------------------
	
	imageLoaded : function() {
		ss.i( "Image was loaded." );
		ss.slides[ ss.slide.current ].loaded = true;
		ss.slide.loaded = true;
		if( ! ss.animation.running ) ss.loadingEnd();
	},
	
	imageAborted : function() {
		ss.w( "Image load was aborted." );
		ss.slide.loaded = true;
		if( ! ss.animation.running ) ss.loadingEnd();
	},
	
	imageError : function() {
		ss.e( "An error occured while image loading." );
		ss.slide.loaded = true;
		if( ! ss.animation.running ) ss.loadingEnd();
	},
	
	// loading animations ------------------------------------------------------
	
	/**
	 * Loading animation when one slide playback has been finished and loading
	 * next slide is about to be started.
	 */
	loadingStart : function() {
		ss.i( "Loading new slide started ..." );
		ss.setSlideLoading( true );
		switch( ss.animation.type ) {
			case "fade" :
				jQuery( ss.sel.mask ).css( {
					"background" : ss.mask.background,
					"top" : Math.round( ss.padding ) + "px",
					"left" : Math.round( ss.padding ) + "px",
					"width" : Math.round( ss.width ) + "px"
				} );
				if( ss.mask.image && ! jQuery( ss.sel.mask + " img" ).length )
					jQuery( ss.sel.mask ).append(
						"<img src='" + ss.mask.image +
						"' style='position : absolute; left : " +
						Math.round( ( ss.width - ss.mask.imageWidth ) / 2 ) +
						"px' />"
					);
				// immediatelly continue animation when on first slide
				if( ss.showFirstSlideImmediatelly && ! ss.slide.firstShown )
					jQuery( ss.sel.mask ).fadeOut( 0, ss.setHeight );
				else
					jQuery( ss.sel.mask ).fadeIn( ss.animation.duration * 1000, ss.setHeight );
				break;
			case "move" :
				ss.setHeight();
				// for safari the flash must be partially visible to load & communicate to JS
				var safariFlash = (
					navigator.userAgent.toLowerCase().indexOf( "safari" ) >= 0
				) && (
					ss.slides[ ss.slide.current ].type == "flash"
				) &&
					ss.showFlash;
				if( safariFlash )
					ss.i( "Safari detected and flash slide is about to be displayed." );
				// so we show 1 pixel of it when loading ;o)
				var x = 0;
				if( ss.showFirstSlideImmediatelly && ! ss.slide.firstShown )
					x = 0;
				else if( safariFlash )
					x = Math.round( ss.width - 1 );
				else if( ss.showingPrevious && ss.animation.previousFromLeft )
					x = - Math.round( ss.width + ss.animation.slideSpacing );
				else
					x = Math.round( ss.width + ss.animation.slideSpacing );
				ss.holders[ ss.currentHolder ].css( {
					"left" : x + "px",
					"top" : ( safariFlash ? ( ss.height - 1 ) : "0" ) + "px"
				} );
				break;
		}
	},
	
	/**
	 * Animates height of main area holder and its basic subelements according
	 * to the current slide height. Particularly important for HTML version of
	 * slide animation, as it calls the loadingEnd() function, which shows the
	 * loaded slide.
	 */
	setHeight : function() {
		var h = ( ss.slides[ ss.slide.current ].height ) ?
			( ss.slides[ ss.slide.current ].height ) : ss.height;
		var d = ss.animation.duration * 1000;
		// animated loading indication
		var t = Math.round( ( h - ss.mask.imageHeight ) / 2 ) + "px";
		// exit here when the height is already correct
		if( jQuery( ss.sel.mainArea ).height() == h ) {
			ss.i( "No slide height change." );
			jQuery( ss.sel.mask + " img" ).css( { top : t } );
			ss.checkSlideLoaded();
			return;
		}
		ss.setAnimationRunning( true );
		h = h + "px";
		ss.i( "Setting main area height to " + h + " ..." );
		jQuery( ss.sel.wrapper ).animate( { height : h }, d );
		jQuery( ss.sel.mainArea ).animate( { height : h }, d );
		ss.holders[ ss.currentHolder ].animate( { height : h }, d );
		ss.holders[ ss.otherHolder ].animate( { height : h }, d );
		jQuery( ss.sel.mask ).animate(
			{ height : h },
			{
				duration : d,
				easing : ss.animation.easing,
				complete : function() {
					ss.setAnimationRunning( false );
					ss.checkSlideLoaded();
				}
			}
		);
		jQuery( ss.sel.mask + " img" ).animate( { top : t }, d );
	},
	
	/**
	 * Helper function to check whether the ( image ) slide was already loaded.
	 */
	checkSlideLoaded : function() {
		if( ! ss.slide.loaded ) return;
		ss.i( "Image slide was already loaded." );
		ss.loadingEnd();
	},
	
	/**
	 * Loading slide is finished and slide playback is started.
	 */
	loadingEnd : function() {
		ss.i(
			"Loading ended. Scheduled slide : " + ( ss.slide.scheduled + 1 ) +
			". Showing loaded slide ..."
		);
		// reset holders
		var cHolder = ss.holders[ ss.currentHolder ];
		var oHolder = ss.holders[ ss.otherHolder ];
		// swap holder layers
		cHolder.css( { "z-index" : "2" } );
		oHolder.css( { "z-index" : "1" } );
		// reset background on current holder
		var bg = (
				ss.slides[ ss.slide.current ].loaded &&
				( ss.slides[ ss.slide.current ].type == "image" || ! ss.showFlash )
			) ?
			( "url('" + ss.slides[ ss.slide.current ].imageUrl + "') no-repeat" ) :
			"transparent";
		cHolder.css( "background", bg );
		// reveal the loaded content
		var d = ss.animation.duration * 1000;
		var e = ss.animation.easing;
		ss.setAnimationRunning( true );
		ss.setSlideLoading( false );
		switch( ss.animation.type ) {
			case "fade" :
				jQuery( ss.sel.mask ).fadeOut( d, ss.animationEnd );
				break;
			case "move" :
				var x = 0;
				if( ss.showFirstSlideImmediatelly && ! ss.slide.firstShown )
					x = 0;
				else if( ss.showingPrevious && ss.animation.previousFromLeft )
					x = - Math.round( ss.width + ss.animation.slideSpacing );
				else
					x = Math.round( ss.width + ss.animation.slideSpacing );
				cHolder.css( { "left" : x + "px", "top" : "0" } );
				x = ( ss.showingPrevious && ss.animation.previousFromLeft ) ?
					Math.round( ss.width + ss.animation.slideSpacing ) :
					- Math.round( ss.width + ss.animation.slideSpacing );
				setTimeout( function() {
					oHolder.animate(
						{ "left" : x + "px" },
						{ duration : d, easing : e }
					);
					cHolder.animate(
						{ "left" : "0" },
						{
							duration : d, easing : e,
							complete : ss.animationEnd
						}
					);
				}, 0 );
				break;
		}
		// set first slide shown
		ss.slide.firstShown = true;
		// setup the default slide timeout
		if( ss.slides[ ss.slide.current ].type == "flash" && ss.showFlash )
			ss.clearSlideTimeout();
		else
			ss.setSlideTimeout();
	},
	
	/**
	 * Helper function to set slide loading.
	 */
	setSlideLoading : function( value ) {
		ss.slide.loading = Boolean( value );
		ss.i( "Slide loading set to " + ss.slide.loading + "." );
		ss.checkNavigationEnabled();
	},
	
	/**
	 * Slide animation has ended. Do some cleanup and start loading scheduled
	 * slide when it was defined ( this means navigation button was clicked
	 * during animation ).
	 */
	animationEnd : function() {
		ss.i( "Animation has finished." );
		ss.setAnimationRunning( false );
		var oHolder = ss.holders[ ss.otherHolder ];
		ss.clearHolder( oHolder );
		if( ss.slide.scheduled > -1 ) {
			if( ss.slide.scheduled == ss.slide.current ) {
				ss.w( "Scheduled slide is the same as current slide, cancelled." );
				ss.navigation.clicked = false;
				ss.slide.scheduled = -1;
			} else {
				ss.i( "Showing scheduled slide " + ( ss.slide.scheduled + 1 ) + " ..." );
				ss.showSlide( ss.slide.scheduled );
			}
		}
	},
	
	/**
	 * Helper function to set animation running.
	 */
	setAnimationRunning : function( value ) {
		ss.animation.running = Boolean( value );
		ss.i( "Animation running set to " + ss.animation.running + "." );
		ss.checkNavigationEnabled();
	},
	
	/**
	 * Helper function to switch navigation buttons off when loading or animating
	 * and on otherwise.
	 */
	checkNavigationEnabled : function() {
		ss.setNavigationEnabled( ! (
			( ss.navigation.disableWhenLoading && ss.slide.loading ) ||
			( ss.navigation.disableWhenAnimating && ss.animation.running )
		) );
	},
	
	// playback ----------------------------------------------------------------
	
	/**
	 * Starts / resumes slideshow playback from current slide.
	 */
	play : function() {
		ss.stopped = false;
		ss.showNextSlide();
	},
	
	/**
	 * Stops slideshow playback at current slide. Can't stop playing flash slide
	 * though.
	 */
	stop : function() {
		if( ss.slide.timeoutStorage ) clearTimeout( ss.slide.timeoutStorage );
		ss.stopped = true;
	},
	
	/**
	 * Slide timeout setup for HTML slide which does not signal the slide
	 * finished event like from Flash.
	 */
	setSlideTimeout : function() {
		ss.clearSlideTimeout();
		var t = ss.slides[ ss.slide.current ].timeout;
		ss.slide.timeoutStorage = setTimeout( "slideshow.showNextSlide()", t * 1000 );
		ss.i( "New slide timeout set to " + t + " s." );
	},
	
	/**
	 * Clears slide timeout for HTML slide when set.
	 */
	clearSlideTimeout : function() {
		if( ss.slide.timeoutStorage ) {
			clearTimeout( ss.slide.timeoutStorage );
			ss.i( "Old slide timeout cleared." );
		}
	},
	
	/**
	 * Clears old holder contents. Usable for flash, which could trigger
	 * playback earlier then required when playing in background.
	 */
	clearHolder : function( h ) {
		if( ! h ) return;
		ss.i( "Clearing old holder contents ..." );
		h.html( "" );
	}
	
} );

ss.heightSetup();

/* ]]> */
