/*
 * AcmeMenu v2.4 AcmeMenuEffects v2
 *
 * Copyright (c) 2003 Mackley F. Pexton.  All rights reserved.
 *
 * This is free software for individual, educational, and non-profit
 * use provided that this copyright notice appears on all copies.
 * Instructions and source code are available at www.acmebase.org/menu.
 * Optimized version is for sale at https://order.acmebase.com/AcmeMenu.php.
 * Commercial web sites are required to have an inexpensive license.
 * This software is part of the AcmeBase project at www.acmebase.org.
 * Send correspondence and feedback to: mack_pexton@acmebase.org.
 *
 * This file enhances AcmeMenu and it should be loaded after AcmeMenu.js.
 */

/******************************************************************************

AcmeMenu v2.4 AcmeMenuEffects -- Ancillary methods to AcmeMenu.

This is an ancillary script file for AcmeMenu. It provides fades, triggers
and autoplay for menus. These functions have been separated from the main
AcmeMenu.js file to keep it small. These effects were incorporated into
AcmeMenu after experiments with using Fixed Menus on home pages where large
panels fade-in and then trigger an animation.

These functions enhance AcmeMenu, but they can be used stand-alone. They can
be used with any document element.



Fades

The fades currently only work with MS Internet Explorer 5.5+ and Netscape 6+.

Menus, or for that matter, any document element, can be faded in or faded out
with the following functions. 

	fadeIn(e,duration,speed,trigger)	// fade-in document element
	fadeOut(e,duration,speed,trigger)	// fade-out document element
	fadeReset(e,opacity,trigger);		// set element opacity (0-100)

These routines produce a slightly different fading effect than the browser
filters in Internet Explorer or Netscape, and work better with AcmeMenu.
If a fadeIn() request is executed before a fadeOut() request if finished,
the fade-in operation cancels the fade-out and starts fading in where the
fade-out left off. The filters built into browsers always complete a fade
operation before beginning the next request.

Fades adjust the element's CSS "visibility" style setting. The "display"
style setting also affects whether an element is visible or not, however
adjusting the "display" setting is not always appropriate. The "display"
setting will be adjusted if the element's style attribute "display" is
set, such as: <... style="display:block;">  Another method is to define
an extra argument in fadeIn() for the display style to set the element
when shown. The fadeOut() function also accepts an extra argument if the
display style is to be set to "none" when faded out. The fadeReset()
function accepts the display_style argument too.

	fadeIn(e,duration,speed,trigger,display_style)
	fadeOut(e,duration,speed,trigger,"none")

Document elements can have their own fade parameters assigned to them by
using the attribute settings fadeinduration, fadeoutduration, fadeinspeed,
and fadeoutspeed in the document element tag. In addition, individual trigger
functions can be assigned to the document element by using the fadeintrigger
and fadeouttrigger attributes. For example:

	<img id="example" fadeinduration="1" fadeintrigger="alert('hi')">



Triggers

The trigger arguments are functions that are executed just after the fade-in
or just after the fade-out has completed. The functions are passed only one
argument, a reference to the document element.

Two properties are added to the element object, "fade_id" and if using
autoplay(), "play_id". They are references to the Fade control object and
to the PlayList object.

The PlayList object has a "cmd" property set to "play", "next", "prev",
"first", "last", or "go".  It identifies the playlist method used to render
the element which caused the trigger to be executed. The Fade object also
has a property "cmd" that is set to "show", "hide", or "reset" depending on
whether the element was faded in, faded out, or set to a specified value.

Autoplay adds a couple of triggers: autoplay.BeforeHideTrigger and 
autoplay.BeforeShowTrigger, which are exectued before a panel is
faded in or faded out. If they return a non-null value that is
false, the fade in or fade out operation is canceled.

Triggers can also be strings, in which case, they are eval'ed.



Autoplay

The autoplay() function automatically "plays" a menu (or just an element). The
term "play" here means to show the menu or element, and if it has a trigger,
execute the trigger function. The arguments are the number of seconds between
each interval, followed by a list of either element references, their id's
or menu id's.

	playlist = autoplay(interval,'elem_id1','elem_id2', ...)

	autoplay.play();		// restart the play list
	autoplay.stop();		// stop the autoplay

Several play lists can play simultaneously. All of them are started and
stopped with the autoplay.play() and autoplay.stop() methods. Individual
playlists can be controlled by the object returned by autoplay().

	playlist.play();		// start autoplay on this list
	playlist.stop();		// stop autoplay

	playlist.next();		// play (show) next item in playlist
	playlist.prev();		// play (show) previous item in playlist

	playlist.first();		// play (show) first item in playlist
	playlist.last();		// play (show) last item in playlist

The playlist.start() method can be used to play the list again from the
beginning.  The "delay" argument schedules the autoplay to restart after
its specified number of seconds.

	playlist.start(delay)		// re-play the playlist

A playlist can play repeatedly by setting autoplay.Loop variable to the number
of times to loop the playlist.

	autoplay.Loop = -1;		// loop playlist repeatedly
	autoplay.Loop = 0;		// play the list once ending on last panel
	autoplay.Loop = 1;		// play the list once ending on first panel
	autoplay.Loop > 1;		// play the list Loop number of times

A playlist calls the playlist.status() method whenever the status of the playlist
changes from play to pause to stop. The method is intended to be defined by a
control panel designer. 

These AcmeMenuEffects functions use functions and objects defined in
AcmeMenu.js. They are:

	bv	-- browser version
	debug   -- debug function

Stubs for the above functions are defined if AcmeMenu.js is not loaded.

******************************************************************************/

/*
 * Define stubs if AcmeMenu software is not loaded.
 */

if (! self.Menu) {

	// Uncomment next to display an error message if AcmeMenu.js is not loaded.
	//alert("The 'AcmeMenu.js' file needs to be loaded before this file 'AcmeMenuEffects.js'.");

	// Define stubs for functions found in AcmeMenu.js
	Menu = new Function();
	Menu.menus = {};
	debug = new Function();

	// Define browser sniffer and minimal set of flags.
	bv = new Function();
	bv.isNS     = (navigator.userAgent.indexOf("Netscape") >= 0) ? true : false;
	bv.isOpr    = (navigator.userAgent.indexOf("Opera")    >= 0) ? true : false;
	bv.isIE     = (document.all && ! bv.isOpr);
	bv.isIE6    = (bv.isIE && document.compatMode && document.compatMode.indexOf("CSS1") >= 0) ? true : false;
	bv.isIE5    = (bv.isIE && ! bv.isIE5Mac && ! bv.isIE6) ? true : false;
	bv.isMoz    = (! bv.isNS && ! bv.isOpr && ! bv.isIE && navigator.userAgent.indexOf("Mozilla") == 0) ? true : false;
}

/*
 * More menu settings
 */

Menu.is_effects_loaded = true;		// set flag that this file is loaded

Menu.reset_effects = function() { Menu.initialize_effects(); }	// old name

Menu.initialize_effects = function() {

	// Initialize all class constants.

// Configuration Variables (change as desired) /////////////////////////////////

// Menus can fade-in and fade-out if you set FadeDuration to the number of 
// milliseconds over which to fade. To set only the fade-in duration, set
// Menu.FadeInDuration, and to control just the fade-out, set Menu.FadeOutDuration.
Menu.FadeDuration = 500;
Menu.FadeInDuration = 0;
Menu.FadeOutDuration = 0;

// The speed of a fade is determined by FadeSpeed, which is the number
// of milliseconds between each iteration of the fade. FadeInSpeed
// controls only the fade-in speed and FadeOutSpeed controls the fade-out.
Menu.FadeSpeed = 2000;
Menu.FadeInSpeed = 0;
Menu.FadeOutSpeed = 0;

// Each iteration of fading reduces or increases the opacity by the
// percentage amount assigned to FadePercent. This value is used to
// compute FadeSpeed if it is not set to a non-zero number.
Menu.FadePercent = 25;
Menu.FadeInPercent = 0;
Menu.FadeOutPercent = 0;


// Triggers are functions that are executed when a menu is shown
// or hidden. They are executed asynchronously and they are passed
// the menu object as their sole argument.
Menu.Trigger = null;
Menu.ShowTrigger = null;
Menu.HideTrigger = null;

// End of Configuration Variables //////////////////////////////////////////////
}

Menu.initialize_effects(); 	// initialize extra class constants


/*
 * Fade methods
 */

Menu.prototype.setup_fade = function() {

	// Setup fade parameters.

	this.show_fade_duration = this.FadeInDuration   ? this.FadeInDuration
				: this.FadeDuration     ? this.FadeDuration
				: 0;

	this.hide_fade_duration = this.FadeOutDuration  ? this.FadeOutDuration
				: this.FadeDuration     ? this.FadeDuration
				: 0;

	if (! this.show_fade_duration && ! this.hide_fade_duration) return;

	this.show_fade_speed	= this.FadeInSpeed	? this.FadeInSpeed
				: this.FadeSpeed	? this.FadeSpeed
				: this.FadePercent
				? Math.ceil(this.show_fade_duration * this.FadePercent/100)
				: Math.ceil(this.show_fade_duration * 10/100);	// default

	this.hide_fade_speed	= this.FadeOutSpeed	? this.FadeOutSpeed
				: this.FadeSpeed	? this.FadeSpeed
				: this.FadePercent
				? Math.ceil(this.hide_fade_duration * this.FadePercent/100)
				: Math.ceil(this.hide_fade_duration * 10/100);	// default
}

/*
 * Trigger methods
 */

Menu.prototype.setup_triggers = function() {

	this.show_trigger = this.ShowTrigger ? this.ShowTrigger : this.Trigger;
	this.hide_trigger = this.HideTrigger ? this.HideTrigger : this.Trigger;
}

/*
 * Display methods using fades and triggers
 */

Menu.prototype.show_menu_fade = function() {

	// Show the menu using fades and triggers.

	var display_style = this.e.style.display  ? this.e.style.display
			  : this.set_display_none ? "block"
			  : null;

	// Check for fade parameters and triggers from autoplay() to allow
	// different settings for manual menu operation and autoplay operation.

	var duration,speed,trigger;

	if (this.override) {
		duration = this.override.duration;
		speed    = this.override.speed;
		trigger  = this.override.trigger;
	}
	else {
		duration = this.top.show_fade_duration;
		speed    = this.top.show_fade_speed;
		trigger  = this.top.show_trigger;
	}

	fadeIn(this.e, duration, speed, trigger, display_style);

	this.is_visited = true;		// set flag for autoplay 

	// Clear autoplay arguments from menu object
	this.override = null;
}

Menu.prototype.hide_menu_fade = function() {

	// Hide the menu using fades and triggers.

	var display_style = this.e.style.display || this.e.style.display == "none" ? "block"
			  : this.set_display_none ? "none"
			  : null;

	// Check for fade parameters and triggers from autoplay() to allow
	// different settings for manual menu operation and autoplay operation.

	var duration,speed,trigger;

	if (this.override) {
		duration = this.override.duration;
		speed    = this.override.speed;
		trigger  = this.override.trigger;
	}
	else {
		duration = this.top.hide_fade_duration;
		speed    = this.top.hide_fade_speed;
		trigger  = this.top.hide_trigger;
	}

	fadeOut(this.e, duration, speed, trigger, display_style);

	// Clear autoplay arguments from menu object
	this.override = null;
}

/* ------------------------------------------------------------------------ */

/*
 * Fade functions
 */

function fadeIn(e,duration,speed,trigger,display_style) {

	debug("fadeIn(): duration="+duration+", speed="+speed,e);

	var f = Fade.get_fade(e, duration,speed,trigger,display_style);

	if (f.opacity == null) f.opacity = 0;	// set to invisible

	// Store command in object to inform triggers.
	f.cmd = "show";

	Fade.now_fade_in(f.fade_id);
}

function fadeOut(e,duration,speed,trigger,display_style) {

	debug("fadeOut(): duration="+duration+", speed="+speed,e);

	var f = Fade.get_fade(e, null,null,null,null, duration,speed,trigger,display_style);

	if (f.opacity == null) { f.opacity = 100; }	// set to visible

	// Store command in object to inform triggers.
	f.cmd = "hide";

	Fade.now_fade_out(f.fade_id);
}

function fadeReset(e,opacity,trigger,display_style) {

	debug("fadeReset(): opacity="+opacity,e);

	// Set the element's opacity to specified setting.
	var f = Fade.get_fade(e);

	// Store command in object to inform triggers.
	f.cmd = "reset";

	f.set_fade(opacity,trigger,display_style);
}

/*
 * Fade control object.
 */

Fade.next_fade_id = 0;
Fade.fades = {};		// collection of fade control objects by fade_id
Fade.browser_has_fades = true;	// assume true until found false


function Fade(e) {

	// Arguments:
	//	document element e,

	debug("new Fade: ",e);

	this.fade_id	= Fade.next_fade_id++;

	this.e		= e;
	this.opacity	= null;			// 0 - 100%, initialized later

	this.fade_in_timer  = null;
	this.fade_out_timer = null;

	Fade.fades[this.fade_id] = this;	// register
}

Fade.get_fade = function(e,
			fade_in_duration, fade_in_speed, fade_in_trigger, display_style,
			fade_out_duration,fade_out_speed,fade_out_trigger,display_style_none) {

	// Get fade control object associated to element e.
	// One is constructed if it doesn't exist.

	debug("Fade.get_fade:"+
	  "  fade_in_duration="+fade_in_duration+
	  ", fade_in_speed="+fade_in_speed+
	  ", fade_out_duration="+fade_out_duration+
	  ", fade_out_speed="+fade_out_speed,e);

	var f;
	if (e.fade_id) {
		f = Fade.fades[e.fade_id];
	}
	else {
		// Construct new fade control object.
		f = new Fade(e);

		// Save id of fade control object in document element for next time.
		e.fade_id = f.fade_id;
	}

	// Assign fade control settings
	// Settings in document element overrides function arguments.
	f.fade_in_speed     = e.fadeinspeed != null
			    ? e.fadeinspeed		// element setting
			    : e.fadespeed != null
			    ? e.fadespeed		// element setting
			    : fade_in_speed != null
			    ? fade_in_speed		// function argument
			    : f.fade_in_speed != null
			    ? f.fade_in_speed		// previous setting
			    : 100;			// default 1/10 sec

	f.fade_in_duration  = e.fadeinduration != null
			    ? e.fadeinduration		// element setting
			    : e.fadeduration != null
			    ? e.fadeduration		// element setting
			    : fade_in_duration != null
			    ? fade_in_duration		// function argument
			    : f.fade_in_duration != null
			    ? f.fade_in_duration	// previous setting
			    : f.fade_in_speed * 5;	// default 1/2 sec

	if (f.fade_in_duration > 0 && f.fade_in_speed <= 0) {
		f.fade_in_speed = f.fade_in_duration / 5;	// default of 5 steps
	}

	f.fade_in_step	= f.fade_in_duration > 0
			? Math.ceil( (f.fade_in_speed / f.fade_in_duration) * 100 )
			: 100;				// set to disable fading

	f.fade_in_trigger   = e.fadeintrigger
			    ? e.fadeintrigger		// element setting
			    : e.fadetrigger != null
			    ? e.fadetrigger		// element setting
			    : fade_in_trigger
			    ? fade_in_trigger		// function argument
			    : f.fade_in_trigger
			    ? f.fade_in_trigger		// previous setting
			    : null;			// default

	f.fade_out_speed    = e.fadeoutspeed != null
			    ? e.fadeoutspeed		// element setting
			    : e.fadespeed != null
			    ? e.fadespeed		// element setting
			    : fade_out_speed != null
			    ? fade_out_speed		// function argument
			    : f.fade_out_speed != null
			    ? f.fade_out_speed		// previous setting
			    : 100;			// default 1/10 sec

	f.fade_out_duration = e.fadeoutduration != null
			    ? e.fadeoutduration		// element setting
			    : e.fadeduration != null
			    ? e.fadeduration		// element setting
			    : fade_out_duration != null
			    ? fade_out_duration		// function argument
			    : f.fade_out_duration != null
			    ? f.fade_out_duration	// previous setting
			    : f.fade_out_speed * 5;	// default 1/2 sec

	if (f.fade_out_duration > 0 && f.fade_out_speed <= 0) {
		f.fade_out_speed = f.fade_out_duration / 5;	// default of 5 steps
	}

	f.fade_out_step	= f.fade_out_duration > 0
			? Math.ceil( (f.fade_out_speed / f.fade_out_duration) * 100 )
			: 100;				// set to disable fading

	f.fade_out_trigger  = e.fadeouttrigger
			    ? e.fadeouttrigger		// element setting
			    : e.fadetrigger != null
			    ? e.fadetrigger		// element setting
			    : fade_out_trigger
			    ? fade_out_trigger		// function argument
			    : f.fade_out_trigger
			    ? f.fade_out_trigger	// previous setting
			    : null;			// default

	if (display_style_none) f.display_style = "block";
	if (display_style) f.display_style = display_style == "none" ? "block" : display_style;

	return f;
}

Fade.prototype.set_fade = function(opacity,trigger,display_style) {

	// Clear current fades in progress.
	clearTimeout(this.fade_in_timer);
	clearTimeout(this.fade_out_timer);

	if (opacity) {
		this.opacity = parseInt(opacity,10);
		if (isNaN(this.opacity)) this.opacity = 100;
		if (this.opacity > 100)  this.opacity = 100;
		if (this.opacity < 0)    this.opacity = 0;
	}
	else {
		this.opacity = 0;
	}

	if (this.opacity == 0) {
		// Hide the element.
		this.e.style.visibility = "hidden";
		if (this.display_style) this.e.style.display = "none";
	}
	else {
		// Show the element.
		if (this.display_style) this.e.style.display = this.display_style;
		this.e.style.visibility = "visible";
	}

	if (Fade.browser_has_fades) this.do_fade();

	if (trigger) {

		// To mimic fadeIn() and fadeOut() triggers, we briefly delay
		// execution of the trigger function. The primary side effect
		// of this is to allow this javascript function to end so that 
		// document events get executed by the browser before the trigger
		// function is executed. For example, AcmeSlideShow requires the 
		// the browser to start downloading a new image before the
		// trigger function executes.

		this.set_trigger = trigger;
		setTimeout('Fade.now_trigger_reset("'+this.fade_id+'")',100);
	}
}

// Class Method
Fade.now_trigger_reset = function(fade_id) {

	var f = Fade.fades[fade_id];	// get fade control object

	Fade.trigger(f.e,f.set_trigger);
}

// Class Method
Fade.now_fade_in = function(fade_id) {

	// This function is called by setTimeout().

	var f = Fade.fades[fade_id];	// get fade control object

	// Clear current fades in progress.
	clearTimeout(f.fade_in_timer);
	clearTimeout(f.fade_out_timer);

	debug("Fade.now_fade_in("+fade_id+"): opacity="+f.opacity+", step="+f.fade_in_step,f.e);

	if (f) {
		if (f.opacity == 0) {

			// Show the element.
			if (f.display_style) f.e.style.display = f.display_style;
			f.e.style.visibility = "visible";
		}

		f.opacity += f.fade_in_step;
		if (f.opacity > 100) f.opacity = 100;

		if (Fade.browser_has_fades) f.do_fade();

		if (f.opacity == 100 || ! Fade.browser_has_fades) {

			// Execute trigger after element is shown 100%.
			Fade.trigger(f.e,f.fade_in_trigger);
		}
		else {
			// Schedule next iteration of fade-in.
			f.fade_in_timer = setTimeout("Fade.now_fade_in('"+fade_id+"')", f.fade_in_speed);
		}
	}
}

// Class Method
Fade.now_fade_out = function(fade_id) {

	// This function is called by setTimeout().

	var f = Fade.fades[fade_id];	// get fade control object

	// Clear current fades in progress.
	clearTimeout(f.fade_in_timer);
	clearTimeout(f.fade_out_timer);

	debug("Fade.now_fade_out("+fade_id+"): opacity="+f.opacity+", step="+f.fade_out_step,f.e);

	if (f) {
		f.opacity -= f.fade_out_step;
		if (f.opacity < 0) f.opacity = 0;

		if (Fade.browser_has_fades) f.do_fade();

		if (f.opacity == 0 || ! Fade.browser_has_fades) {

			// Hide the element.
			f.e.style.visibility = "hidden";
			if (f.display_style) f.e.style.display = "none";

			// Execute trigger after element is hidden.
			Fade.trigger(f.e,f.fade_out_trigger);
		}
		else {
			// Schedule next iteration of fade-out.
			f.fade_out_timer = setTimeout("Fade.now_fade_out('"+fade_id+"')", f.fade_out_speed);
		}
	}
}

Fade.prototype.do_fade = function() {

	// Set the alpha filter to the current opacity.
	// Used for both showing an hiding document elements.

	if (bv.isIE5 || bv.isIE6) {
		this.e.style.filter =
		  "progid:DXImageTransform.Microsoft.Alpha(opacity="+this.opacity+")";
	}
	else if (bv.isNS || bv.isMoz) {
		this.e.style.MozOpacity = this.opacity/100;
	}
	else if (bv.isSafari || bv.isKonq) {
		this.e.style.KhtmlOpacity = this.opacity/100;	// not working on Safari 1.1.1
	}
	else if (this.e.style.opacity) {
		this.e.style.opacity=this.opacity/100;	// CCS3
	}
	else  {
		// Cancel fades
		//this.fade_in_step	= this.fade_out_step    = 100;
		//this.efadeinduration	= this.efadeoutduration = 0;
		//Fade.browser_has_fades	= false;
	}
}

// Class method
Fade.trigger = function(this_e,trigger) {

	// Execute trigger functions

	if (! trigger) return;

	// Execute trigger after element is hidden.
	if (typeof trigger == 'string') {

		// Replace "this" in trigger text with reference to
		// the document element, which here is "this_e".
		trigger = trigger.replace(/\bthis\b/g,"this_e");

		return eval(trigger);
	}
	else {
		// Execute trigger function.
		// Pass it the document element reference as its sole argument.
		return trigger(this_e);
	}
}

/* ------------------------------------------------------------------------ */

/*
 * Autoplay function
 */

function autoplay(interval) {

	// Show and hide a list of document elements every interval seconds.

	debug("autoplay():"+
	  " FadeDuration="+autoplay.FadeDuration+
	  ", FadeSpeed="+autoplay.FadeSpeed+
	  ", Loop="+autoplay.Loop);

	// Get playlist settings from configuration variables.
	var fade_in_speed	= autoplay.FadeInSpeed      ? autoplay.FadeInSpeed
				: autoplay.FadeSpeed;

	var fade_out_speed	= autoplay.FadeOutSpeed     ? autoplay.FadeOutSpeed
				: autoplay.FadeSpeed;

	var fade_in_duration	= autoplay.FadeInDuration   ? autoplay.FadeInDuration
				: autoplay.FadeDuration;

	var fade_out_duration	= autoplay.FadeOutDuration  ? autoplay.FadeOutDuration
				: autoplay.FadeDuration;

	var show_trigger = autoplay.ShowTrigger ? autoplay.ShowTrigger : autoplay.Trigger;
	var hide_trigger = autoplay.HideTrigger ? autoplay.HideTrigger : autoplay.Trigger;

	var before_show_trigger = autoplay.BeforeShowTrigger;
	var before_hide_trigger = autoplay.BeforeHideTrigger;

	// Construct new play list control object.
	var pl = new PlayList(interval, autoplay.Direction, autoplay.Loop);

	// Set NoAutoPause flag to keep autoplay() from stopping list.
	if (autoplay.NoAutoPause) pl.NoAutoPause = true;

	// Fill control object with list of elements to "play".
	for (i = 1; i < arguments.length; i++) {
		pl.add_player(arguments[i],
			fade_in_duration, fade_in_speed, before_show_trigger, show_trigger,
			fade_out_duration, fade_out_speed, before_hide_trigger, hide_trigger);
	}

	autoplay.start(pl);

	return pl;	// return PlayList control object
}

/*
 * Autoplay configuration
 */

function reset_autoplay() { initialize_autoplay(); }	// old name

function initialize_autoplay() {
	// Initialize configuration variables

// Configuration variables.
autoplay.Direction = 1;			// 1 is forward, -1 is reverse
autoplay.Loop = -1;			// play list repeatedly
autoplay.StartDelay = .2;		// number of seconds before starting
autoplay.NoAutoPause = false;		// don't pause playlist with autoplay.pause()
autoplay.FadeInSpeed = 0;		// speed between iterations in miliseconds
autoplay.FadeInDuration = 0;		// duration of fade in milliseconds
autoplay.FadeOutSpeed = 0;		// speed between iterations in miliseconds
autoplay.FadeOutDuration = 0;		// duration of fade in milliseconds
autoplay.FadeSpeed = 0;			// speed between iterations in miliseconds
autoplay.FadeDuration = 0;		// duration of fade in milliseconds
autoplay.BeforeShowTrigger = null;	// function or string to execute before showing
autoplay.BeforeHideTrigger = null;	// function or string to execute before hiding
autoplay.ShowTrigger = null;		// function or string to execute after showing
autoplay.HideTrigger = null;		// function or string to execute after hiding
autoplay.Trigger = null;		// executed after showing and after hiding
}

initialize_autoplay();		// initialize configuration variables

// Class flags
autoplay.is_activated = false;	// status flag true if autoplay() was called
autoplay.is_playing = false;	// status flag true if autoplay() is running

/*
 * Autoplay methods
 */

autoplay.start = function(playlist) {
	// Start playlists from beginning.
	var start_delay = autoplay.StartDelay ? autoplay.StartDelay * 1000 : 0;

	debug("autoplay.start(): start_delay="+start_delay);

	if (playlist) {
		playlist.start(start_delay);
	}
	else {
		var playlist_id;
		for (playlist_id in PlayList.playlists) {
			PlayList.playlists[playlist_id].start(start_delay);
		}
	}

	// Set flag that autoplay is in progress.
	autoplay.is_playing = true;

	// Set flag that autoplay was called and activated.
	autoplay.is_activated = true;
}

autoplay.stop = function(hide_players) {
	// Stop play lists.
	debug("autoplay.stop():");
	var playlist_id;
	for (playlist_id in PlayList.playlists) {
		PlayList.playlists[playlist_id].stop(hide_players);
	}
	autoplay.is_playing = false;
	autoplay.is_activated = false;
}

autoplay.play = function() {
	// Start all play lists after they have been paused.
	debug("autoplay.play():");
	if (! autoplay.is_activated) autoplay.start();
	var playlist_id, pl;
	for (playlist_id in PlayList.playlists) {
		pl = PlayList.playlists[playlist_id];
		if (! pl.is_playing && ! pl.is_finished) {
			debug('autoplay.play: playing '+pl.playlist_id);
			pl.play();
			autoplay.is_playing = true;
		}
	}
}

autoplay.resume = function() {
	// Start all play lists after they have been paused.
	debug("autoplay.resume():");
	if (! autoplay.is_activated) autoplay.start();
	var playlist_id, pl;
	for (playlist_id in PlayList.playlists) {
		pl = PlayList.playlists[playlist_id];
		if (! pl.is_playing && ! pl.is_finished) {
			debug('autoplay.play: resuming play '+pl.playlist_id);
			pl.resume();
			autoplay.is_playing = true;
		}
	}
}

autoplay.pause = function(hide_players) {
	// Pause autoplay temporarily
	debug("autoplay.pause():");
	var playlist_id, pl;
	for (playlist_id in PlayList.playlists) {
		pl = PlayList.playlists[playlist_id];
		if (! pl.NoAutoPause) {
			debug("autoplay.pause(): pausing "+playlist_id);
			pl.pause(hide_players);
		}
	}
	autoplay.is_playing = false;
}

/*
 * PlayList control object.
 */

PlayList.next_playlist_id = 0;
PlayList.playlists = {};	// collection of autoplay control objects by playlist_id

function PlayList(interval,direction,loop) {

	// Arguments:
	//	interval   -- interval between plays in seconds
	//	direction  -- 1 forward, -1 reverse
	//	loop       -- number of times play list

	debug("new PlayList: interval="+interval);

	this.playlist_id  = PlayList.next_playlist_id++;

	this.direction	  = direction;			// 1: forward, -1: backward
	this.interval	  = interval * 1000;		// convert to milliseconds
	this.delay	  = 0;				// seconds before starting

	this.loop	  = loop;			// number of times to play list
	this.loop_count   = 0;				// number of times list played

	this.firsttime	  = true;
	this.is_started   = false;
	this.is_playing   = false;
	this.is_finished  = false;

	this.player_list  = [];				// list of Players
	this.player_index = 0;

	this.play_timer	  = null;

	this.cmd	  = 'play';

	PlayList.playlists[this.playlist_id] = this;	// register

	return this;
}

PlayList.prototype.add_player = function(e,
		fade_in_duration, fade_in_speed, before_show_trigger,show_trigger,
		fade_out_duration,fade_out_speed,before_hide_trigger,hide_trigger) {

	var p = new Player(e,
		fade_in_duration,fade_in_speed,before_show_trigger,show_trigger,
		fade_out_duration,fade_out_speed,before_hide_trigger,hide_trigger);

	// Save playlist_id in document element for use by trigger functions.
	p.e.playlist_id = this.playlist_id;

	this.player_list[this.player_list.length] = p;	// save player in play list
}

PlayList.prototype.start = function(delay) {

	clearTimeout(this.play_timer);	// safety

	this.player_index = this.direction > 0 ? -1 : this.player_list.length;

	this.loop_count  = 0;
	this.firsttime   = true;	
	this.is_started  = true;
	this.is_finished = false;
	this.delay       = delay;

	// Store command in object to inform triggers.
	this.cmd = 'play';
	this.is_playing = true;
	this.status();

	// Clear menu visitation flags.
	var p,m;
	for (p = 0; p < this.player_list.length; p++) {
		if (m = this.player_list[p].menu) m.is_visited = false;
	}

	// Schedule first iteration.
	if (delay) {
		this.play_time = (new Date).getTime();	// record time started
		this.play_timer = setTimeout("PlayList.now_play('"+this.playlist_id+"')",delay);
	}
	else {
		PlayList.now_play(this.playlist_id);
	}
}

PlayList.prototype.stop = function(hide_player) {

	clearTimeout(this.play_timer);
	if (hide_player && this.player_list[this.player_index])
		this.player_list[this.player_index].hide();
	this.stop_time = 0;		// ensure stop time cleared
	this.is_started = false;
	this.is_playing = false;
	this.status();
}

PlayList.prototype.pause = function(hide_player) {

	clearTimeout(this.play_timer);
	if (hide_player) this.player_list[this.player_index].hide();
	this.stop_time = (new Date).getTime();	// record time stopped
	this.is_playing = false;
	this.status();
}

PlayList.prototype.resume = function() {
	
	clearTimeout(this.play_timer);

	if (! this.is_started) return;

	// Store command in object to inform triggers.
	this.cmd = 'play';
	this.is_playing  = true;
	this.status();

	var interval = 0;
	if (this.stop_time) { interval = this.interval - (this.stop_time - this.play_time); }
	if (this.player_index <= 0 && this.loop_count == 0 && this.delay > 0) {
		// first time
		if (interval > this.delay) interval = this.delay;
	}
	else {
		if (interval > this.interval) interval = this.interval;
	}
	if (interval < 1000) interval = 1000;	// 1 second minimum
	this.play_timer = setTimeout("PlayList.now_play('"+this.playlist_id+"')", interval);
}

PlayList.prototype.play = function() {
	
	clearTimeout(this.play_timer);

	if (! this.is_started) return;

	// Store command in object to inform triggers.
	this.cmd = 'play';
	this.is_playing  = true;
	this.is_finished = false;
	this.status();
	PlayList.now_play(this.playlist_id);
}

// Class Method
PlayList.now_play = function(playlist_id) {

	// This function is called by setTimeout().

	debug("PlayList.now_play("+playlist_id+")");

	var pl = PlayList.playlists[playlist_id];	// get autoplay control object
	if (! pl) return;				// error

	pl.next_play();

	if (pl.is_finished) return;

	// Schedule next iteration of play.
	pl.play_timer = setTimeout("PlayList.now_play('"+playlist_id+"')", pl.interval);
}

PlayList.prototype.next_play = function() {

	var old_player_index = this.player_index;

	// Get next element to play.
	this.set_next_player_index();

	if (this.firsttime) {
		this.firsttime = false;

		// Start play on next element (show it).
		this.player_list[this.player_index].show(1);
	}
	else {
		if (this.loop == 0
		&& this.player_index == (this.direction > 0 ? 0 : this.player_list.length - 1)) {
			this.finish();
			return;
		}

		var do_fade = true;

		// Stop play on current element (hide it).
		this.player_list[old_player_index].hide(do_fade);

		// Start play on next element (show it).
		this.player_list[this.player_index].show(do_fade);

		if (this.loop > 0 && this.loop_count == this.loop) {
			this.finish();
			return;
		}
	}

	this.play_time = (new Date).getTime();	// record time started
}

PlayList.prototype.finish = function() {

	this.is_playing  = false;
	this.is_finished = true;
}

PlayList.prototype.first = function() {

	this.pause();

	// Store command in object to inform triggers.
	this.cmd = 'first';

	// Stop play on current element (hide it).
	this.player_list[this.player_index].hide();

	// Set player_index to be first, depending upon direction.
	this.player_index = this.direction > 0 ? 0 : this.player_list.length - 1;

	// Start play on first element (show it).
	this.player_list[this.player_index].show();
}
	
PlayList.prototype.last = function() {

	this.pause();

	// Store command in object to inform triggers.
	this.cmd = 'last';

	// Stop play on current element (hide it).
	this.player_list[this.player_index].hide();

	// Set player_index to be last, depending upon direction.
	this.player_index = this.direction > 0 ? this.player_list.length - 1 : 0;

	// Start play on last element (show it).
	this.player_list[this.player_index].show();
}
	
PlayList.prototype.next = function() {

	this.pause();

	// Store command in object to inform triggers.
	this.cmd = 'next';

	// Stop play on current element (hide it).
	this.player_list[this.player_index].hide();

	this.player_index = this.get_player_index(this.player_index + this.direction);

	// Start play on next element (show it).
	this.player_list[this.player_index].show();
}

PlayList.prototype.prev = function() {

	this.pause();

	// Store command in object to inform triggers.
	this.cmd = 'prev';

	// Stop play on current element (hide it).
	this.player_list[this.player_index].hide();

	this.player_index = this.get_player_index(this.player_index - this.direction);

	// Start play on next element (show it).
	this.player_list[this.player_index].show();
}

PlayList.prototype.go = function(index) {

	this.pause();

	// Store command in object to inform triggers.
	this.cmd = 'go';

	// Stop play on current element (hide it).
	this.player_list[this.player_index].hide();

	this.player_index = this.get_player_index(index);

	// Start play on next element (show it).
	this.player_list[this.player_index].show();
}

PlayList.prototype.get_player_index = function(index) {

	// Keep index within bounds.
	index %= this.player_list.length;
	if (index < 0) index += this.player_list.length;

	return index;
}
	
PlayList.prototype.set_next_player_index = function() {

	// Set player_index to the next player. Count the loops.

	var m;
	while (1) {
		this.player_index += this.direction;

		if (this.player_index >= this.player_list.length) {
			this.player_index = 0;				 // reset
			this.loop_count++;	// count number of loops

			if (this.loop >= 0 && this.loop_count >= this.loop) {
				this.finish();
				return;
			}
		}
		else if (this.player_index < 0) {
			this.player_index = this.player_list.length - 1; // reset
			this.loop_count++;	// count number of loops

			if (this.loop >= 0 && this.loop_count >= this.loop) {
				this.finish();
				return;
			}
		}

		// Check if this is a menu and if it has already been visited.
		if ((this.loop == 0 || this.loop == 1)
		&& (m = this.player_list[this.player_index].menu) && m.is_visited) {
			continue;	// go to next item in play list
		}

		break;			// found next player_index
	}
}

// The following is a stub. The method is intended to be defined elsewhere.
PlayList.prototype.status = function() { }

/*
 * Player object.
 */

function Player(e, fade_in_duration,fade_in_speed,before_show_trigger,show_trigger,
		   fade_out_duration,fade_out_speed,before_hide_trigger,hide_trigger) {

	// Arguments:
	//	document element object or element id or menu id

	var m;
	if (typeof e == 'string') {
		this.id = e;
		if ( (m = Menu.menus[e]) ) {
			this.menu = m;
			this.e    = m.e;
		}
		else {
			this.e = document.getElementById(e);
		}
	}
	else {
		this.e = e;

		if (e.id) this.id = e.id;

		if ((m = Menu.menus[this.id])) this.menu = m;
	}

	this.fade_in_duration	= fade_in_duration;
	this.fade_in_speed	= fade_in_speed;
	this.before_show_trigger= before_show_trigger;
	this.show_trigger	= show_trigger;

	this.fade_out_duration	= fade_out_duration;
	this.fade_out_speed	= fade_out_speed;
	this.before_hide_trigger= before_hide_trigger;
	this.hide_trigger	= hide_trigger;
}

Player.prototype.show = function(do_fade) {
	
	if (this.before_show_trigger) {
		var rc = Fade.trigger(this.e,this.before_show_trigger);
		if (rc != null && ! rc) return;
	}

	// Start (show) element or menu.
	if (this.menu) {

		// Override menu fade parameters with autoplay fade parameters.
		this.menu.override = {  duration: this.fade_in_duration,
					speed:    this.fade_in_speed,
					trigger:  this.show_trigger };
		this.menu.show_menu();

		return;
	}

	if (do_fade) fadeIn(this.e,this.fade_in_duration,this.fade_in_speed,this.show_trigger);
	else         fadeReset(this.e,100,this.show_trigger);
}

Player.prototype.hide = function(do_fade) {
	
	if (this.before_hide_trigger) {
		var rc = Fade.trigger(this.e,this.before_hide_trigger);
		if (rc != null && ! rc) return;
	}

	// Stop (hide) element or menu.
	if (this.menu) {

		// Override menu fade parameters with autoplay fade parameters.
		this.menu.override = {  duration: this.fade_out_duration,
					speed:    this.fade_out_speed,
					trigger:  this.hide_trigger };
		this.menu.hide_menu();

		return;
	}

	if (do_fade) fadeOut(this.e,this.fade_out_duration,this.fade_out_speed,this.hide_trigger);
	else         fadeReset(this.e,0,this.hide_trigger);
}
