// ----------------------------------------------------------------------------------
//
//	slideshow.js
//
// Copyright 2005 Rob Laddish. All rights reserved.
// Version: 2.6, Jul 18, 2009
//
// This script creates a running slideshow of pictures on a web page.
//
// Method #1:
// This method uses the get_intro_picture perl script to figure out your file listings.
// - Add to your <head> section: <script type="text/javascript" language="javascript1.2" src="/admin/js/slideshow.js"></script>
// - Add to your <head> section: <!--#include virtual="/cgi-bin/get_intro_picture.pl?format=header" -->
// - Change <body> to allow <body onkeypress="slideshow_process_keypress(event);">
//   (if you want to control the slideshow via the keyboard as well; note: add user help somewhere)
// - Add somewhere in a table in your body: <td id="slideshow_area" width=200>Loading Slideshow ...</td>
//
// Method #2:
// This method is manual, you must define your files in your <head> section by hand.
// - Add to your <head> section: <script type="text/javascript" language="javascript1.2" src="/admin/js/slideshow.js"></script>

//
// Options and arguments:
//
//
// TODO: (new)
// - Perhaps pre-load next image if not already loaded, then start time & discount image load time.
// - Fix caller to allow caching of filesystem searches & re-use if <1 hour. Add flush button here?
// - Hitting buttons multiple times forces multiple timers to try and start, make sure they don't overlap.
// - Add status icon so we can see if timer is active, images loading, etc. animated gifs good.
// - Put number in display for # seconds. left click drop down box to change values.
// - Allow images in all_intros to be selected & return upwards, use for selecting pic for hikes.
// - Establish max width & height, set by passed in args from above. Downsize images by ar to fit.
// - Fix error routine, infinite loops, and when #images=0 or #images>0, but all bad.
// - Change flow of code to load image, then wait to display it - time it took to load. Smooths
//   out jerkiness, but need to allow final loaded image to be cancelled.
// - Find way to let person jump to trip or image dir it came from? perhaps link or file in home_pages.
// - Make array for slideshow last image so R can continue going back?
// - i = more info about the image?
// - fix hard coded references to ms so we can allow any name for mini_slideshow
//   * onclick callback (this undefined at that level)
//   * keypress callback (this also undefined)
// - keep track of which random images have been called
//   * make sure next random image isn't same as current (unles num_valid<=1)
//   * make unique array & force us to go through all unseen images first
//   * it could be simpler if we form a unique list first, then walk it.
// - Find way to print width/height for each all pics photo and highlight errors in red!!
//   Perhaps with a custom javascript function? that way we don't read in ahead of time and
//   slow things down more.
//
// TODO: (old)
// - Update docs for this file.
// - Allow multiple slideshows to be defined for the same page. User will need to specify an id.
//   problem cause onclick and onkeypress for entire window, needs to be for small area too.
// - Clean up and remove unused code.
// - Define other table formats.
// - Create an options button that uses cookies. Inside options:
//   * set start slideshow when page is loaded (on/off)
//   * set image flip time (1-60 seconds)
//   * set table format type
//   * save button saves cookies. don't rely on hiking cookies
// - Can we force the left/right buttons to keep the same places? How about if we capture the
//   max width of each picture and adjust it for additional pics?
//
// Callback info:
// - http://www.webreference.com/programming/javascript/gr/column3/
// ----------------------------------------------------------------------------------

// Capture current time
var ms_start = new Date();
var ms_start2 = ms_start.getTime(); // num millisecs since 1Jan70.

// define objects
function mini_slideshow_image(url,caption) {
    this.url=url;
    this.caption=caption;
    this.status = "undefined";
    //this.image = new Image();
}


function mini_slideshow(name) {

    // User overridable variables, change in your html header if you wish
    this.active = 1;		// should slideshow start off running when page loads?
    this.debug=0;		// enable debug mode. Set =2 to show debug in caption fields.
    this.desired_aspect_ratio = "portrait"; // used when looking up files
    this.preload_images=0;
    this.all_pics_url="skip";
    this.all_intro_url="http://laddish.net";
    this.delay=4000;		// number of millisecs to wait between each picture
    this.use_setinterval=0;	// set=1 SetInterval, or set=0 to use SetTimeout
    this.display_mode=1;	// set=1 when using id="slideshow_area" in your body.
    this.table_format="table2";
    this.version = "2.9 [2009, July 18]";
    this.get_intro_version = "unknown";
    this.images = new Array();
    this.num_images = 0;
    this.alert_debug = 0;
    this.name = name;		// have to refer to var name in callbacks. pass it here!
    this.num_defined = 0;
    this.cookie_domain = "";
    this.cookie_expires = "600";
    this.cookie_path = "/";
    this.cookie_secure = "";
    if (this.name == "" || this.name == undefined) {
        this.name = "ms";
    }

    // Buttons, set to blank in your html header to keep them from showing.
    // Warning: had to force it to call "ms" as this.xx is unknown!
    this.prev_html='<img title="view previous image" src="/intro_photos/left_arrow.gif" height=20 onclick="' + this.name + '.prev_image()">';
    this.stop_html='<input TYPE="submit" name="submit" VALUE="Stop Slideshow" onclick="' + this.name + '.stop();">';
    this.start_html='<input title="start slideshow" TYPE="submit" name="submit" VALUE="Start Slideshow" onclick="' + this.name + '.toggle_slideshow()">';
    this.all_pics_html='<input TYPE="submit" name="submit" VALUE="All Pics" onclick="' + this.name + '.show_all_pics();">';
    if (this.all_pics_url == "ignore" || this.all_pics_url == "skip") {
        this.all_pics_html = "";
    }
    this.all_intro_html='<input title="See all intro pictures for this event" TYPE="submit" name="submit" VALUE="See Intros" onclick="'+this.name+'.show_all_intro();">';
    this.next_html='<img title="view next image" src="/intro_photos/right_arrow.gif" height=20 onclick="'+this.name+'.next_image()">';
    this.help_html='<img src="/intro_photos/help.gif" title="press this button for help" height=20 onclick="' + this.name + '.help_window()">';
    //this.all_pics_html = ""; // temporarily remove button, let's resize to small windows work better

    // Our globals, please don't change
    this.max_width = 200;			// used to resize image area
    this.width_pad = 50;
    this.max_height = 450;
    this.redraw_table = 1;
    this.redraw_controls = 1;
    this.num_valid = 0;
    this.num_loaded = 0;
    this.num_defined = 0;
    this.interval;
    this.max_loops = 100; // abort if we can't find a valid image after this many attempts.
    this.browser_name=navigator.appName; 
    this.browser_version=parseInt(navigator.appVersion);
    this.user_browser = "firefox";
    this.load_index = -1;
    this.big_setting = 0;
    this.cur_image = -1;
    this.last_image = -1;
    this.last_image2 = -1;

    this.image_max_height = 450;
    this.image_min_height = 450;
    this.image_max_width = 380;
    this.image_min_width = 330;

    this.error_msg = "";
    this.last_error_msg = "";
    this.status_msg = "";
    this.last_status_msg = "";
    this.debug_msg = "";
    this.startup_time = 0;
    this.get_intro_time = 0;

    this.num_preloaded=0;
    this.num_preloaded_bad=0;
    this.num_preloaded_ugly=0;
    this.tot_preloaded=0;
    this.default_active=this.active;
    this.default_delay=this.delay;
    this.default_big_setting=this.big_setting;

    this.debug_list = new Array();
    this.debug_size = 15;
    this.debug_index = 0;
    this.debug_digits = 3;


    this.add_image = function(url,caption) {
	this.images[this.num_images] = new mini_slideshow_image(url,caption);
        this.num_images++;
    }

    // ----------------------------------------------------------------
    this.update_debug_string = function(msg) {
        var pos=0;
	this.debug_msg = "";
	var start = this.debug_list.length;
	var index_string = this.debug_index+"";
	if (index_string.length<this.debug_digits) { index_string = "0" + index_string;}
	if (index_string.length<this.debug_digits) { index_string = "0" + index_string;}
	if (index_string.length<this.debug_digits) { index_string = "0" + index_string;}
	if (start>=this.debug_size) {start=this.debug_size-1;}
	for (pos=start; pos>=0; pos--) {
	    if (this.debug_list[pos] == undefined) {continue;}
	    this.debug_list[pos+1] = this.debug_list[pos];
	    this.debug_msg = this.debug_msg + "<br>" + "" + this.debug_list[pos+1];
	}
	this.debug_list[0] = "[" + index_string + "] " + msg;
	this.debug_msg = this.debug_msg + "<br>" + "" + this.debug_list[0];
	this.debug_index++;
    }

    // ----------------------------------------------------------------
    // slideshow_msg
    //
    // display message or status information to the user
    // TODO: may want to try a queue of messages, let them scroll off
    // the top. means we don't have to figure out debug123 order.
    // ----------------------------------------------------------------
    this.msg = function(type,msg) {
	if (0 || this.alert_debug) {alert("msg("+type+","+msg+") called");}
	// first check the type and set the appropriate string
	if (type == "error") {
	    if (msg=="") {
	    	this.error_msg="";
	    } else {
		this.error_msg = "<font color='#FF8888'>Error: " + msg+"</font>";
	    }
	} else if (type == "warning") {
	    if (msg=="") {
	    	this.error_msg="";
	    } else {
		this.error_msg = "Warning: " + msg;
	    }
	} else if (type == "status") {
	    this.status_msg = msg;
	} else if (type == "debug") {
	    this.update_debug_string(msg);
	} else {
	    if (msg=="") {
	    	this.error_msg="";
	    } else {
		this.error_msg = "Error2: " + msg;
	    }
	}
	
	// construct string to display
	var msg = "";
	if (this.status_msg != "") {
	    if (msg != "") {msg = msg + "<br>";}
	    msg = msg + this.status_msg;
	}
	if (this.error_msg != "") {
	    if (msg != "") {msg = msg + "<br>";}
	    msg = msg + this.error_msg;
	}
	if (this.debug>0 && this.debug_msg != "") {
	    if (msg != "") {msg = msg + "<br>";}
	    msg = msg + this.debug_msg;
	}

	// write combined html string to display
	if (this.def_force_small==1) {
	    // do nothing, this area does not exist
	} else if (this.browser_name == "Microsoft Internet Explorer") {
	    if (document.all.slideshow_error) {
		document.all.slideshow_error.innerHTML = msg;
	    } else {
	    	// alert("warning: could not write msg, as tag wasn't found.");
	    }
	} else {
	    if (document.getElementById("slideshow_error")) {
		document.getElementById("slideshow_error").innerHTML = msg;
	    } else {
	    	// alert("warning: could not write msg, as tag wasn't found.");
	    }
	}
    }


    // ----------------------------------------------------------------
    // slideshow_load_image_complete
    //
    // Called when slidwshow_image_array[place].src finishes
    // ----------------------------------------------------------------
    this.load_image_complete = function () {
	var valid=0;
	var dmesg="";
	    
	if (this.ms.alert_debug) {alert("load_image_complete() called");}
	if (this.ms.debug>1) {
	    this.ms.msg("debug","load_image_complete() called. pos="+this.place+", action="+this.action);
	}
	if (this.ms.startup_time==0) {
	    var ms_end = new Date();
	    var ms_end2 = ms_end.getTime(); // num millisecs since 1Jan70.
	    this.ms.startup_time = ms_end2 - ms_start2;
	}

	window.clearTimeout(this.ms.interval); // just in case it is set
	if (this.ms.images[this.place].status!="loaded") {
	    this.ms.num_loaded=this.ms.num_loaded+1;
	}
	if (this.ms.alert_debug) {
	    // useful to debug when it look like multiple timers exist
	    alert(unescape("slideshow_load_image_complete called%0A%0A"
		+ "this.place = "+this.place + "%0A"
		+ "this.action = "+this.action + "%0A"
	    ));
	}
	    
	this.ms.images[this.place].status = "loaded";
	if (this.ms.images[this.place].image.width >= this.ms.images[this.place].image.height) {
	    this.ms.images[this.place].aspect_ratio = "landscape";
	} else {
	    this.ms.images[this.place].aspect_ratio = "portrait";
	}
	
	// below used to use slideshow_current_slide
	// todo: if valid==0, asign status="ugly"
	if (this.ms.desired_aspect_ratio == "all" || this.ms.desired_aspect_ratio == this.ms.images[this.place].aspect_ratio) {
	    valid=1;
	} else {
	    this.ms.images[this.place].status = "ugly";
	    this.ms.msg("warning","skipping ugly image at pos "+this.place); // +"desired_ar="+this.ms.desired_aspect_ratio+",our ar="+this.ms.images[this.place].aspect_ratio);
	}

	// adjust maximum values if needed, only for valid images
	if (0 && this.ms.images[this.place].status == "loaded") {
	    if (this.ms.images[this.place].image.width > this.ms.max_width) {
		this.ms.max_width = this.ms.images[this.place].image.width;
	    }
	    if (this.ms.images[this.place].image.width > this.ms.max_height) {
		this.ms.max_height = this.ms.images[this.place].image.height;
	    }
	}

	//if (this.ms.images.length<=1) { valid=1; }
	dmesg = dmesg + "valid="+valid+",iw="+this.width+",ih="+this.height+",ar="+this.ms.images[this.place].aspect_ratio + "\n";
	
	// stretch to avoid major issues
	this.height_mult=1;
	this.width_mult=1;
	
	// adjust to ideal height (most important), keep aspect ratio intact
	if (this.height<this.ms.image_min_height) {
	    this.height_mult = this.ms.image_min_height/this.height;
	    this.width_mult = this.height_mult;
	} else if (this.height>this.ms.image_max_height) {
	    this.height_mult = this.ms.image_max_height/this.height;
	    this.width_mult = this.height_mult;
	}
	dmesg = dmesg + "mult1: w="+this.width_mult+" ("+Math.round(this.width*this.width_mult)+"),h="+this.height_mult+" ("+Math.round(this.height*this.height_mult)+")"+"\n";
	    
	// adjust to ideal width (less important), change aspect ratio as needed
	if (this.width*this.width_mult<this.ms.image_min_width) {
	    this.width_mult = this.width_mult*this.ms.image_min_width/(this.width*this.width_mult);
	} else if (this.width*this.width_mult>this.ms.image_max_width) {
	    this.width_mult = this.width_mult*this.ms.image_max_width/(this.width*this.width_mult);
	}
	
	// abort now if just preloaded, especially if ugly or invalid image
	if (this.action == "preload") {
	    this.ms.num_preloaded++;
	    if (this.ms.images[this.place].status == "ugly") {
	        this.ms.num_preloaded_ugly++;
	    }
	    if (this.ms.num_preloaded==this.ms.tot_preloaded && this.ms.tot_preloaded>0) {
		this.ms.msg("status","Preloading has finished loading "+this.ms.num_preloaded+" images. "+this.ms.num_preloaded_bad+" bad, "+this.ms.num_preloaded_ugly+" ugly.");
	    } else {
		this.ms.msg("status","Preloaded " + this.ms.num_preloaded + "/" + this.ms.tot_preloaded+" images, "+this.ms.num_preloaded_bad+" bad, "+this.ms.num_preloaded_ugly+" ugly.");
	    }
	}
	if (this.action == "preload" || this.action == "ignore") {return;}
	
	
	// adjust to correct size based on 
	dmesg = dmesg + "mult2: w="+this.width_mult+" ("+Math.round(this.width*this.width_mult)+"),h="+this.height_mult+" ("+Math.round(this.height*this.height_mult)+")"+"\n";
	
	// finaly bail if image is not valid
	if (valid==0||this.ms.images[this.place].status=="ugly") {
	    this.ms.msg("warning","skipping invalid or ugly image at pos "+this.place);
	    this.ms.msg("debug","load_image got an invalid or ugly image at "+this.place);
	    var newplace = this.ms.find_good_pos(this.place,this.action,"load_image_complete");
	    this.ms.msg("debug","find_good_pos suggested pos="+newplace);
	    if (newplace>=0) {
		this.ms.load_image(newplace,this.action);
	    } else {
		this.ms.msg("error","load_image could not find valid image. Stopping slideshow.");
		this.ms.stop();
	    }
	    return;
	}
	    
	// do after valid image loads
	this.ms.cur_image = this.place;
	this.ms.update_display(this.place);

	// todo: set a different flag than action and check at top
	this.action = "ignore"; // ignore future simultaneous calls
	    
	if (0) {
	    // write debug info to see what's working and what is not
	    if (this.ms.browser_name == "Microsoft Internet Explorer") {
		document.all.debug_td1.innerHTML = dmesg;
	    } else {
		document.getElementById("debug_td1").innerHTML = dmesg;
	    }
	}


	// tell it to load another one if delay is set
	// don't use interval, as loading can be slow and they will clog the pipe
	if (this.ms.active) {
	    if (this.ms.use_setinterval==0) {
		// todo: look at slideshow_load_action: preload, next, prev, random
		if (this.ms.delay<1000) {this.ms.delay=1000;}
		this.ms.interval = setTimeout("this."+this.ms.name+".random_image()", this.ms.delay);
	    }
	}
    }

    // ----------------------------------------------------------------
    // slideshow_load_image_error
    //
    // An error occured, image probably not found. mark invalid so we
    // skip next time, then look at action to see if we try the next one.
    // Check for infinite loops, abort after 10 or so tries of constant
    // errors.
    //
    // Find a way to handle the passed event or object
    // http://docs.sun.com/source/816-6408-10/handlers.htm#1120097
    // implies that we are passed the image object, and if we define
    // it's name in the html code, we can use arg1.name and print it.
    //
    // it doesn't look like we can drill down to the error reason,
    // such as a dropped network connection.
    // ----------------------------------------------------------------
    this.load_image_error = function (e) {
	if (this.ms.alert_debug) {alert("load_image_error() called");}
	if (this.ms.debug) {
	    this.ms.msg("debug","slideshow_load_image_error() called. pos="+this.place+", action="+this.action);
	}
	if (this.browser_name == "Microsoft Internet Explorer") { e = event; }
	this.ms.images[this.place].event = e; // save for later
	if (0) {
	    // useful to debug when it look like multiple timers exist
	    alert(unescape("slideshow_load_image_error called%0A%0A"
		+ "this.place = "+this.place + "%0A"
		+ "this.action = "+this.action + "%0A"
	    ));
	}
	if (this.ms.startup_time==0) {
	    var ms_end = new Date();
	    var ms_end2 = ms_end.getTime(); // num millisecs since 1Jan70.
	    this.ms.startup_time = ms_end2 - ms_start2;
	}
	this.ms.msg("error","load image had an error pos="+this.place);
	this.ms.images[this.place].status = "error";
	if (this.ms.images[this.place].action == "preload") {
	    this.ms.num_preloaded++;
	    this.ms.num_preloaded_bad++;
	}
	if (this.ms.num_preloaded==this.ms.tot_preloaded && this.ms.tot_preloaded>0) {
	    this.ms.msg("status","Preloading has finished loading "+this.ms.num_preloaded+" images. "+this.ms.num_preloaded_bad+" bad, "+this.ms.num_preloaded_ugly+" ugly.");
	} else {
	    this.ms.msg("status","Preloaded " + this.ms.num_preloaded + "/" + this.ms.tot_preloaded+" images, "+this.ms.num_preloaded_bad+" bad, "+this.ms.num_preloaded_ugly+" ugly.");
	}
	if (this.ms.images[this.place].action == "preload" || this.ms.images[this.place].action == "ignore") {return;}
	    
	// todo: look for another image, if not found, stop slideshow
	var place = this.ms.find_good_pos(this.place,this.action,"load_image_error");
	if (place<0) {
	    this.ms.msg("error","image_error could not find another valid image. stopping slideshow. pos="+this.place);
	    this.ms.stop();
	    return true;
	}
	return true;

    }

    // ----------------------------------------------------------------
    // slideshow_load_image_abort
    //
    // User hit stop button. turn off slideshow. don't process image.
    // ----------------------------------------------------------------
    this.load_image_abort = function() {
	if (this.ms.alert_debug) {alert("load_image_abort() called");}
	if (this.ms.debug) {
	    this.ms.msg("debug","slideshow_load_image_abort() called. pos="+this.place+", action="+this.action);
	}
	if (1) {
	    // useful to debug when it look like multiple timers exist
	    alert(unescape("slideshow_load_image_abort called%0A%0A"
		    + "this.place = "+this.place + "%0A"
		    + "this.action = "+this.action + "%0A"
	    ));
	}
	if (this.ms.startup_time==0) {
	    var ms_end = new Date();
	    var ms_end2 = ms_end.getTime(); // num millisecs since 1Jan70.
	    this.ms.startup_time = ms_end2 - ms_start2;
	}
	this.ms.num_preloaded=0; // forget about any possible preload action
	this.ms.tot_preloaded=0;
	this.ms.msg("error","user stopped download. Stopping slideshow. pos="+this.place);
	this.ms.images[this.place].status = "undefined";
	this.ms.stop();
	return true;

    }

    // ----------------------------------------------------------------
    // slideshow_load_image(place)
    // ----------------------------------------------------------------
    this.find_good_pos = function(place,action,caller) {
	if (this.alert_debug) {alert("find_good_pos("+place+","+action+") called");}
	if (this.debug>1) { this.msg("debug","find_good_pos("+place+","+action+") called from "+caller); }
	var max_tries=500;
	var num_tries=0;
	while (num_tries<max_tries) {
	    if (this.images[place].status == "loaded") {
		return(place);
	    } else if (this.images[place].status=="undefined") {
		return(place);
	    } else if (0 && this.images[place].status=="error") {
	        // continue to next one using action below
	    } else if (0 && this.images[place].status=="ugly") {
	        // continue to next one using action below
	    } else if (action=="next") {
		place = place + 1;
		if( place == this.images.length ) {
		    place = 0;
		}
	    } else if (action=="prev") {
		place = place - 1;
		if( place == -1 ) {
		    place = this.images.length-1;
		}
	    } else if (action=="random") {
		place = Math.round(Math.random()*(this.images.length - 1));
	    } else if (action=="preload" || action=="ignore") {
		return(-2); // called on bad image. don't halt, just stop this one.
	    } else {
		return(-3); // error! halt entire slideshow
	    }
	    num_tries=num_tries+1;
	}
	return(-1); // all errors!
    }

    // ----------------------------------------------------------------
    // slideshow_load_image(place)
    //
    // starts loading image into array. rely on callbacks to figure
    // out what to do when this finishes. action tells us what we should
    // retry if there is an error loading the image.
    //
    // Note: sometimes this=image object, sometimes this=mini_slideshow
    // For this reason, we have to figure out proper ms parent to call
    // methods or we'll get an error. This is true for any of our calls
    // that come from image callback routines.
    //
    // TODO: check for poor status types!
    // ----------------------------------------------------------------
    this.load_image = function(place,action) {
	if (0 || this.alert_debug) {alert("load_image("+place+","+action+") called");}
	if (this.debug>1) { this.msg("debug","load_image("+place+","+action+") called"); }

	// sometimes called from image object [ when already exists]
	var ms = this;
	if (this.ms) {ms = this.ms;}

	window.clearTimeout(ms.interval); // just in case it is set
	    
	// advance until we find an image that is loaded and valid or one that is not loaded yet
	var orig_place = place;
	place = ms.find_good_pos(place,action,"load_image");
	if (place<0) {
	    ms.msg("error","load image could not find valid image starting at pos="+orig_place+" and action="+action+". ret="+place);
	    return;
	}
	    
	if (ms.images[place].status=="loaded") {
	    if (action=="preload"||action=="ignore") {
		if (this.alert_debug) {alert("load_image called on image with action==preload or ignore");}
	    	return(-2);
	    }
	    if (this.alert_debug) {alert("load_image calling update_display as image already loaded and valid.");}
	    ms.update_display(place);
	    // todo: restart timer here!
	    if (ms.active) {
		if (ms.use_setinterval==0) {
		    // todo: look at ms.load_action: preload, next, prev, random
		    if (ms.delay<1000) {ms.delay=1000;}
		    // note: this is rarely called & results in error if we use this.random_image
		    // TODO: figure out how this works otherwise!
		    //alert("setting timeout to this.random_image(0)");
		    ms.interval = setTimeout(ms.name+".random_image(0)", ms.delay);
		}
	    }
	    return;
	}
	// warning: don't retry if status==ugly? error?
	ms.images[place].image = new Image();
	ms.images[place].image.ms = ms;
	ms.images[place].image.place = place;
	ms.images[place].image.action = action;
	ms.images[place].image.onload = ms.load_image_complete;
	ms.images[place].image.onerror = ms.load_image_error;
	ms.images[place].image.onabort = ms.load_image_abort;

	if (ms.alert_debug) {alert("setting ms.images["+place+"].image.src = "+ms.images[place].url);}
	ms.images[place].image.src = ms.images[place].url;
	return(0);
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.show_all_pics = function () {
	if (this.alert_debug) {alert("show_all_pics() called");}
	if (this.debug) { this.msg("debug","show_all_pics() called"); }
	this.stop();
	top.location.href=this.all_pics_url;
    }
    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.show_all_intro = function () {
	if (this.alert_debug) {alert("show_all_intro() called");}
	if (this.debug) { this.msg("debug","show_all_intro() called"); }
	this.stop();
	this.show_all_intros_page();
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.process_keypress = function(e) {
	if (this.alert_debug) {alert("process_keypress() called");}
	var keynum
	var keychar
	    
	if (this.browser_name == "Microsoft Internet Explorer") {
	    e = event;
	}
	if (!e) {
	    alert("cannot process key events, event structure is null");
	    return;
	}
	keynum = (e.which) ? e.which : e.keyCode
	keychar = String.fromCharCode(keynum)
	    
	if (this.debug) {
	    this.msg("debug","processing keypress. char='"+keychar+"' num="+keynum+".");
	}
	    
	// advance to the correct locaiton based on the key pressed
	if(keynum==39||keynum==32||keynum==13||keychar=='d'||keychar=='+'||keychar=='=') {
	    // advance to the right on: right arrow, space, return, enter
	    this.next_image(1);
	} else if (keynum==37||keynum==46||keychar=='a'||keychar=='-'||keychar=='_') {
	    this.prev_image(1);
	} else if (keychar=='q') {
	    this.close();
	} else if (keychar=='h') {
	    this.help_window();
	} else if (keychar=='v') {
	    if (this.debug==2) {
		this.msg("status","Note: debug mode is now off.");
		this.debug=0;
	    } else if (this.debug==1) {
		this.msg("status","Note: debug mode is now on maximum.");
		this.debug=2;
	    } else {
		this.msg("status","Note: debug mode is now on.");
		this.debug=1;
	    }
	} else if (keychar=='t') {
	    this.test_window();
	} else if (keychar=='r') {
	    this.random_image(1);
	} else if (keychar=='l') {
	    this.list_images(1);
	} else if (keychar=='o') {
	    this.options();
	} else if (keychar=='R') {
	    this.cur_image = this.last_image;
	    this.load_image(this.cur_image,"next");
	} else if (keychar=='s') {
	    this.toggle_slideshow();
	} else if (keychar=='b') {
	    this.big_setting = this.big_setting+1;
	    if (this.big_setting>1) {
		this.big_setting=-1;
	    }
	    this.display_image(this.cur_image);
	} else if (keychar=='p') {
	    this.preload_images_now();
	} else if (keychar=='0') {
	    this.stop();
	} else if (keychar=='1'||keychar=='2'||keychar=='3'||keychar=='4'||keychar=='5'||keychar=='6'||keychar=='7'||keychar=='8'||keychar=='9') {
	    this.delay = (parseInt(keychar)+0)*1000;
	    this.active=1;
	    this.random_image(0);
	} else {
	    this.msg("error","Sorry, key '"+keychar+"' is undefined.");
	}
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.display_caption = function(index) {
	if (this.alert_debug) {alert("display_caption("+index+") called");}
	if (this.debug>1) {this.msg("debug","display_caption("+index+") called");}
	var controls = "";
	var new_caption = this.images[index].caption;
	//debug: new_caption = new_caption + "; index=" + index + "; this.desired_aspect_ratio=" + this.image_aspect_ratio[index];
	    
	if (this.debug>1) {
	    new_caption = new_caption + "; debug=1; delay=" + this.delay + "; index=" + index + "; max_width=" + this.max_width;
	}
	
	// Can be "Netscape". What about Opera or others?
	if (this.def_force_small==1) {
	    // do nothing, this area does not exist
	} else if (this.browser_name == "Microsoft Internet Explorer") {
	    document.all.caption.innerHTML = new_caption;
	} else {
	    document.getElementById("caption").innerHTML = new_caption;
	}
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.options = function() {
	var header="<html><head>\n";
	var body="";
	var footer = "</body></html>\n";
	var code="";

	// javascript to save form values into cookies
	code += "function save_values(form) {\n";
	code += "  var minislideshow_autostart = form.autostart.value;\n";
	code += "  var minislideshow_delay = form.delay.value*1000;\n";
	code += "  var minislideshow_size = form.size.value;\n";
	code += "  Set_Cookie('minislideshow_autostart',minislideshow_autostart,'" + this.cookie_expires + "','"+this.cookie_path+"','"+this.cookie_domain+"','"+this.cookie_secure+"');\n";
	code += "  Set_Cookie('minislideshow_delay',minislideshow_delay,'" + this.cookie_expires + "','"+this.cookie_path+"','"+this.cookie_domain+"','"+this.cookie_secure+"');\n";
	code += "  Set_Cookie('minislideshow_size',minislideshow_size,'" + this.cookie_expires + "','"+this.cookie_path+"','"+this.cookie_domain+"','"+this.cookie_secure+"');\n";
	//code += "  alert('values: autostart='+minislideshow_autostart+', delay='+minislideshow_delay+', size='+minislideshow_size);\n"
	code += "history.go(-1);\n";
	code += "}\n";

	header += '<title>Mini-slideshow options</title>\n';
	header += '<script type="text/javascript" language="javascript1.2" src="/admin/js/cookies.js"></script>';
	header += '<script type="text/javascript" language="javascript1.2">\n';
	header += code;
	header += '</script>\n';
	header += "</head><body>\n";
	header += "<h1>Slideshow options</h1>\n";

	
	// figure out autostart default value
	var autostart_yes="";
	var autostart_no="";
	if (this.default_active == "yes" || this.default_active == 1) {
	    autostart_yes = "selected";
	} else if (this.default_active == "no" || this.default_active == "0") {
	    autostart_no = "selected";
	} else {
	    autostart_yes = "selected";
	}

	// figure out delay default value
	var delay_1="";
	var delay_2="";
	var delay_3="";
	var delay_4="";
	var delay_5="";
	var delay_6="";
	var delay_7="";
	var delay_8="";
	var delay_9="";
	var delay_10="";
	if (this.delay == 1000) {
	    delay_1 = "selected";
	} else if (this.delay == 2000) {
	    delay_2 = "selected";
	} else if (this.delay == 3000) {
	    delay_3 = "selected";
	} else if (this.delay == 4000) {
	    delay_4 = "selected";
	} else if (this.delay == 5000) {
	    delay_5 = "selected";
	} else if (this.delay == 6000) {
	    delay_6 = "selected";
	} else if (this.delay == 7000) {
	    delay_7 = "selected";
	} else if (this.delay == 8000) {
	    delay_8 = "selected";
	} else if (this.delay == 9000) {
	    delay_9 = "selected";
	} else if (this.delay == 10000) {
	    delay_10 = "selected";
	} else {
	    delay_4 = "selected";
	}

	// add 'selected' to correct option
	var size_small="";
	var size_medium="";
	var size_large="";
	if (this.big_setting == 1) {
	    size_large = "selected";
	} else if (this.big_setting == 0) {
	    size_medium = "selected";
	} else if (this.big_setting == -1) {
	    size_small = "selected";
	} else {
	    size_medium = "selected";
	}

	body += "<p>Options are saved as cookies and are only valid for this site. They won't work if you have cookies disabled.\n";
	body += "<form name='options'>\n\n";
	body += "<table border=1>\n";
	body += "<tr bgcolor='#CCCCCC'><th>Value</th><th>Description</th></tr>\n";
	body += "<tr><td>\n  <select name='autostart'>\n";
	body += "    <option value='yes' " + autostart_yes + " >yes</option>\n";
	body += "    <option value='no' " + autostart_no + " >no</option>\n";
	body += "  </select>\n";
	body += "</td>\n<td>Automatically start when page loads</td></tr>\n";
	body += "<tr><td>\n  <select name='delay'>\n";
	body += "    <option value='1' " + delay_1 + " >1</option>\n";
	body += "    <option value='2' " + delay_2 + " >2</option>\n";
	body += "    <option value='3' " + delay_3 + " >3</option>\n";
	body += "    <option value='4' " + delay_4 + " >4</option>\n";
	body += "    <option value='5' " + delay_5 + " >5</option>\n";
	body += "    <option value='6' " + delay_6 + " >6</option>\n";
	body += "    <option value='7' " + delay_7 + " >7</option>\n";
	body += "    <option value='8' " + delay_8 + " >8</option>\n";
	body += "    <option value='9' " + delay_9 + " >9</option>\n";
	body += "    <option value='10' " + delay_10 + " >10</option>\n";
	body += "  </select>\n";
	body += "</td>\n<td>Default delay (in seconds) between images</td></tr>\n";
	body += "<tr><td>\n  <select name='size'>\n";
	body += "    <option value='small' "+ size_small + " >small</option>\n";
	body += "    <option value='medium' "+size_medium+" >medium</option>\n";
	body += "    <option value='large' "+size_large+" >large</option>\n";
	body += "  </select>\n";
	body += "</td>\n<td>Size of image windows</td></tr>\n";
	body += "</table>\n\n";
	body += "<p>\n\n";
	body += '<button type=button onClick="save_values(this.form)">Save Settings</button>\n';
	body += "</form>\n";


	document.open();
	document.write(header+body+footer);
	document.close();

    }
    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.list_images = function() {
	var pos=0;
	var num_loaded=0;
	var num_ugly=0;
	var num_error=0;
	var num_undefined=0;
	var num_other=0;
	var num_wide=0;
	var num_wide2=0;
	var num_skinny=0;
	var num_skinny2=0;
	var num_tall=0;
	var num_tall2=0;
	var num_short=0;
	var num_short2=0;
	var tot=this.images.length;
	var header = "<html><body>";
	var body="";
	var debug_digits = 3;
	var sep = "\n";
	//var sep = "<br>";
	if (this.debug) { this.msg("debug","list_images() called."); }
	header = header + "<title>Mini-slideshow image listing</title>\n";
	header = header + "</head><body>\n";
	header = header + "<h1>Image Listing</h1>\n";
	header = header + "<ul><li>" + this.images.length + " images are defined</h1>";
	body = body + '<table border=1><tr bgcolor="#CCCCCC"><th>num</th><th>status</th><th>thumb</th><th>dimensions</th><th>ar</th><th>caption</th></tr>';
	while (pos<this.images.length) {
	    var pos_string = pos + "";
	    var pic="";
	    var status_html = this.images[pos].status;
	    var image_dimensions = "";
	    var dim_error = "";
	    var status_error = "";
	    var big_width_warning = 60;
	    var big_height_warning = 0;

	    // force num to have width of 3
	    if (pos_string.length<debug_digits) { pos_string = "0" + pos_string;}
	    if (pos_string.length<debug_digits) { pos_string = "0" + pos_string;}
	    if (pos_string.length<debug_digits) { pos_string = "0" + pos_string;}
	    // take status count and set fields
	    if (this.images[pos].status=="loaded" || this.images[pos].status=="ugly") {
		if (this.images[pos].status=="loaded") { num_loaded++; }
		if (this.images[pos].status=="ugly") {
		    num_ugly++;
		    status_html = '<font color="red">UGLY</font>';
		}
		pic = '<a href="'+this.images[pos].image.src + '"><img height=20 src="'+this.images[pos].image.src+'"></a>';
		image_dimensions = this.images[pos].image.width + "x" + this.images[pos].image.height;
		if (this.images[pos].image.width>this.image_max_width+big_width_warning) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "VERY&nbsp;WIDE&nbsp;(>" + this.image_max_width+")";
		    num_wide2++;
		} else if (this.images[pos].image.width>this.image_max_width) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "wide&nbsp;(>" + this.image_max_width+")";
		    num_wide++;
		}
		if (this.images[pos].image.width<this.image_min_width-big_width_warning) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "VERY&nbsp;SKINNY&nbsp;(<" + this.image_min_width+")";
		    num_skinny2++;
		} else if (this.images[pos].image.width<this.image_min_width) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "skinny&nbsp;(<" + this.image_min_width+")";
		    num_skinny++;
		}
		if (this.images[pos].image.height>this.image_max_height+big_height_warning) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "VERY&nbsp;TALL&nbsp;(>" + this.image_max_height+")";
		    num_tall2++;
		} else if (this.images[pos].image.height>this.image_max_height) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "tall&nbsp;(>" + this.image_max_height+")";
		    num_tall++;
		}
		if (this.images[pos].image.height<this.image_min_height-big_height_warning) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "VERY&nbsp;SHORT&nbsp;(<" + this.image_min_height+")";
		    num_short2++;
		} else if (this.images[pos].image.height<this.image_min_height) {
		    if (dim_error!="") {dim_error+="<br>";}
		    dim_error = dim_error + "short&nbsp;(<" + this.image_min_height+")";
		    num_short++;
		}
	    } else if (this.images[pos].status=="undefined") {
	    	num_undefined++;
	    } else if (this.images[pos].status=="error") {
	    	num_error++;
		status_html = '<font color="red">ERROR</font>';
		if (this.images[pos].event) {
		    status_error = "Event=" + this.images[pos].event + ",t="+getEventType(this.images[pos].event);
		} else {
		    status_error = "No_Event";
		}
	    } else {
		status_html = '<font color="red">Unknown</font>';
	    	num_other++;
	    }
	    if (dim_error!="") {
	        dim_error = "<br>" + dim_error;
	    }
	    var ar_html = this.images[pos].aspect_ratio;
	    if (ar_html!="" && ar_html>1) {ar_html += "<br>Error";}
	    body = body + '<tr><td><a href="'+this.images[pos].url + '">' + pos_string + '</a></td><td><center>' + status_html + status_error+ '</center></td><td><center>'+pic+'</center></td><td>' + image_dimensions + '<font color="red">'+dim_error+"</font></td><td>" +ar_html + "</td><td>" + this.images[pos].caption+'</td></tr>\n';

	    pos++;
	}
	header = header + "<li> Totals: loaded="+num_loaded+", ugly="+num_ugly+", error="+num_error + ", undefined=" + num_undefined + ", other=" + num_other;
	header = header + "</ul><h3>Image size tests</h3><ul><li> Note we only check image dimensions for loaded images. You may wish to press p to preload all images before showing the listing.";
	header = header + "<li> Totals Errors: skinny="+num_skinny+", very skinny="+num_skinny2+"; wide="+num_wide+", very wide="+num_wide2+"; tall="+num_tall+", very tall="+num_tall2+"; short="+num_short+", very short="+num_short2;
	header = header + "<li> Ideal images sizes: width "+this.image_min_width+"-"+this.image_max_width+", height " + +this.image_min_height+"-"+this.image_max_height + ", images outside these bounds will show an error below";
	header = header + "<li> If width is too large/small but difference is < " + big_width_warning + ", error will be in lower case, worst cases capitalized";
	header = header + "<li> Same reporting method for height, if difference is < " + big_height_warning;
	header = header + "</ul>\n";
	var footer = "</body></html>";
	document.open();
	document.write(header+body+footer);
	document.close();
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.display_controls= function(index) {
	if (this.alert_debug) {alert("display_controls("+index+") called");}
	if (this.debug>1) {this.msg("debug","display_controls("+index+") called");}
	var controls = "";
	var center=1;
	    
	// define controls html
	// TODO: table made spacing of help show up well, but added vertical white space. remove this extra space, it looks bad
	controls = "";
	controls = controls + "<table width='100%' cellpadding=0 cellspacing=0 height='10'><tr height=10>";
	//controls = controls + "<td width=20></td>";
	controls = controls + "<td width=10>" + this.prev_html +"</td>";
	controls = controls + "<td><center>";
	if (this.active) {
	    controls = controls + this.stop_html;
	} else {
	    controls = controls + this.start_html;
	}
	if (0) {
	    controls = controls + this.all_pics_html;
	}
	controls = controls + this.all_intro_html;
	controls = controls + "</center></td>";
	controls = controls + "<td width=20>" + this.help_html + "</td>";
	controls = controls + "<td width=10>" + this.next_html+"</td>";
	    
	controls = controls + "</tr></table>";
	    
	// Can be "Netscape". What about Opera or others?
	if (this.def_force_small==1) {
	    // do nothing, this area does not exist
	} else if (this.browser_name == "Microsoft Internet Explorer") {
	    document.all.controls.innerHTML = controls; 
	} else {
	    document.getElementById("controls").innerHTML = controls;
	}
    }

    // ----------------------------------------------------------------
    // now change the td element to the correct width of the picture to reflow content
    // note: setting it too small forces it to picture width so we don't have to look it up.
    // - TODO: if we shrink window, need to shrink font for caption and buttons
    // ----------------------------------------------------------------
    this.display_image = function(index) {

    	// debug alert + check to make sure image defined and has properties
	if (this.alert_debug) {alert("display_image("+index+") called");}
	if (this.debug>1) {this.msg("debug","display_image("+index+") called");}
	if(!this.images[index]) {
	    alert("display_image error. this.images["+index+"] does not exist");
	    return;
	}
	if(!this.images[index].image) {
	    alert("display_image error. this.images["+index+"].image does not exist");
	    return;
	}
	if(!this.images[index].image.width) {
	    alert("display_image error. this.images["+index+"].image.width does not exist");
	    return;
	}

	// figure out new width/height for picture based on window size
	var new_width = this.images[index].image.width;
	var new_height = this.images[index].image.height;
	var ratio1 = 1;
	var ratio2 = 1;
	var dmesg = "";
	var window_width=document.body.offsetWidth;
	var window_height=document.body.offsetHeight;

	// scale: 50% max for wide windows, 40% for narrow, 30% for tiny
	// function must be continuous (no jumps) to avoid jerky behavior
	// TODO: may want to add onresize callback (ie sucks) to do it
	//       proactively and not have to wait for next image change
	var max_window_pct = 0.40;
	if (window_width>3*this.image_max_width) {
	    max_window_pct=0.50;
	} else if (window_width<1.5*this.image_max_width) {
	    max_window_pct=0.30;
	} else {
	    max_window_pct=0.30+(0.50-0.30)*(window_width-1.5*this.image_max_width)/((3-1.5)*this.image_max_width);
	}

	// adjust max_width if needed
	if (this.max_width<new_width*this.images[index].image.width_mult) {
	    this.max_width = new_width*this.images[index].image.width_mult;
	    if (this.debug>1) {this.msg("debug","reset max_width="+this.max_width);}
	}
	    
	// adjust ratio1 to scale with maximum window width
	var ideal_width = max_window_pct*window_width;
	if (ideal_width> this.image_max_width) {ideal_width=this.image_max_width;}
	ratio1 = ideal_width/this.max_width; // sometimes>1, it's a bug, fix it
	if (ratio1>1) {ratio1=1;}

	// adjust by "big setting"
	if (this.big_setting== -1) {
	    ratio2 = 0.70;
	} else if (this.big_setting== -2) {
	    ratio2 = 0.50;
	} else if (this.big_setting== 1) {
	    ratio2 = 1.50;
	} else if (this.big_setting== 2) {
	    ratio2 = 2.50;
	}

	// now adjust maximum sizes and scale images
	// note: if window width grows and shrinks lots, then pad is too
	// small. You can test assumption by setting to huge value below
	//this.width_pad=200; // debug
	var slide_width = Math.round(ratio1*ratio2*new_width*this.images[index].image.width_mult);
	var slide_height = Math.round(ratio1*ratio2*new_height*this.images[index].image.height_mult);
	var table_width = Math.round(ratio1*ratio2*this.max_width+this.width_pad);

	// this is complex, view settings on debug to fix problems
	if (this.debug>1) {
	    var ratio1a=Math.round(100*ratio1)/100;
	    var ratio2a=Math.round(100*ratio2)/100;
	    var wm=Math.round(100*this.images[index].image.width_mult)/100;
	    var hm=Math.round(100*this.images[index].image.height_mult)/100;
	    this.msg("debug", "max_win%="+max_window_pct+", window w="+window_width+",h="+window_height+", r1="+ratio1a+", r2="+ratio2a);
	    this.msg("debug", "image w="+new_width+",h="+new_height+"; imult w="+wm+",h="+hm);
	    this.msg("debug", "html slide w="+slide_width+", h="+slide_height+"; table w="+table_width+"; pdwidth="+this.max_width);
	}
	    
	    
	// picture_row, picture_data, slide
	if (this.browser_name == "Microsoft Internet Explorer") {
	    // if window is wide, ie will put extra space in this cell. Bummer.
	    document.slide.src=this.images[index].image.src;
	    document.slide.width=slide_width;
	    document.slide.height=slide_height;
	    document.all.picture_table.width = table_width; // broken in ie?
	    //document.all.picture_data.width = this.max_width;
	} else {
	    // bad: document.getElementById("slide").src=this.images[index].src;
	    if (this.alert_debug) {alert("display_image setting src="+this.images[index].image.src);}
	    document.slide.src=this.images[index].image.src;
	    document.slide.width=slide_width;
	    document.slide.height=slide_height;
	    document.getElementById("picture_table").width = table_width;
	    document.getElementById("picture_data").width = this.max_width;
	}
	this.last_image = this.last_image2;
	this.cur_image = index;
	this.last_image2 = index;
	    
	// clear status and error messages if they have not recently changed
	if (this.error_msg == this.last_error_msg && this.error_msg != "") {
	    this.msg("error","");
	}
	this.last_error_msg = this.error_msg;
	if (this.status_msg == this.last_status_msg && this.status_msg != "") {
	    this.msg("status","");
	}
	this.last_status_msg = this.status_msg;
	    
	if (0) {
	    // write debug info to see what's working and what is not
	    if (this.browser_name == "Microsoft Internet Explorer") {
		document.all.debug_td2.innerHTML = dmesg;
	    } else {
		document.getElementById("debug_td2").innerHTML = dmesg;
	    }
	}
    }

    this.help_window = function() {
	if (this.alert_debug) {alert("help_window() called");}
	if (this.debug) { this.msg("debug","help_window() called"); }
	alert(unescape("In addition to the control buttons, you can also use your mouse and keyboard.%0A%0A"
	    + "Mouse shortcuts:%0A"
	    +"%0A"
	    +"left click = left click on image to advance to the next one%0A"
	    +"%0A"
	    + "Keyboard shortcuts:%0A"
	    +"%0A"
	    +"1-9 = start slideshow with # seconds delay%0A"
	    +"0   = stop slideshow;%0A"
	    +"+   = advance to next picture (or left click on picture)%0A"
	    +"-   = go to previous picture%0A"
	    +"s   = toggle start/stop slideshow%0A"
	    +"%0A"
	    +"For firefox, you can use left/right arrows as well.%0A"
	    +"%0A"
	    +"You can also use the left part of your keyboard:%0A"
	    +"a   = prev image%0A"
	    +"d   = next image%0A"
	    +"q   = stop slideshow and remove window from the screen.%0A"
	    +"%0A"
	    + "Advanced commands:%0A"
	    +"%0A"
	    +"r   = go to random picture%0A" 
	    +"R   = return to the last image we displayed%0A"
	    +"b   = make image bigger; keep pressing to cycle between .7x, 1x, and 1.5x sizes%0A"
	    +"o   = set default options for next time%0A" 
	    +"h   = show this help information%0A"
	    +"t   = display testing/debug info%0A"
	    +"v   = enable verbose/debug mode while running%0A"
	    +"p   = preload remaining images (may take a while)%0A"
	    +"l   = list defined images and their status and problems%0A"
	    +"%0A"
	    +"Slideshow version " + this.version + "%0A"
	    +"Get_intro_picture version " + this.get_intro_version
	   )
	);
    }

    this.test_window = function() {
	if (this.alert_debug) {alert("test_window() called");}
	if (this.debug) { this.msg("debug","test_window() called"); }
	alert(unescape( ""
	    +"Debug / test information:%0A"
	    +"%0A"
	    +"Startup / seach values:%0A"
	    +"site                      = " +this.def_site+"%0A"
	    +"preload                   = " +this.def_preload+"%0A"
	    +"delay                     = " +this.def_delay+"%0A"
	    +"auto_slideshow            = " +this.def_auto_slideshow+"%0A"
	    +"caption_prefix            = " +this.def_caption_prefix+"%0A"
	    +"suburl                    = " +this.def_suburl+"%0A"
	    +"format                    = " +this.def_format+"%0A"
	    +"all_format                = " +this.def_all_format+"%0A"
	    +"force_width               = " +this.def_force_width+"%0A"
	    +"force_height              = " +this.def_force_height+"%0A"
	    +"image_max_height          = " +this.image_max_height+"%0A"
	    +"image_max_width           = " +this.image_max_width+"%0A"
	    +"dir_match                 = " +this.def_dirmatch+"%0A"
	    +"file_match                = " +this.def_filematch+"%0A"
	    +"skip                      = " +this.def_skip+"%0A"
	    +"valid_extensions          = " +this.def_fileext+"%0A"
	    +"get_intro_time (ms)       = " +this.get_intro_time+"%0A"
	    +"startup_time (ms)         = " +this.startup_time+"%0A"
	    +"%0A"
	    +"Current running values:%0A"
	    +"active          = " +this.active+"%0A"
	    +"num_valid       = " +this.num_valid+"%0A"
	    +"num_loaded      = " +this.num_loaded+"%0A"
	    +"num_defined     = " +this.num_defined+"%0A"
	    +"use_setinterval = " +this.use_setinterval+"%0A"
	    +"delay           = " +this.delay+"%0A"
	    +"user_browser    = " +this.user_browser+"%0A"
	    +"browser_version = " +this.browser_version+"%0A"
	    +"table_format    = " +this.table_format+"%0A"
	    +"all_pics_url    = " +this.all_pics_url+"%0A"
	    +"all_intro_url   = " +this.all_intro_url+"%0A"
	    +"big_setting     = " +this.big_setting+"%0A"
	    +"%0A"
	    +"cur_image       = " +this.cur_image+"%0A"
	    +"last_image      = " +this.last_image+"%0A"
	   )
	);
    }


    // ----------------------------------------------------------------
    // Replaces the table area of the page with a table using the specified format
    // ----------------------------------------------------------------
    this.display_table = function(index) {
	if (this.alert_debug) {alert("display_table("+index+") called");}
	if (this.debug>1) {this.msg("debug","display_table("+index+") called");}
	var table_html = '';
	var warning;
	var image_src;
	var caption;
	var controls;
	var close_button_src = "/admin/js/close_box.gif";
	    
	// Validate the table format settings
	if (this.table_format != "normal" && this.table_format != "table2") {
	    this.table_format = "normal";
	    warning = "<br>Warning: this.table_format set to unknown value.";
	}
	    
	// Define default values.
	if (index>-1) {
	    image_src = 'src="' + this.images[index].url + '"';
	    caption = this.images[index].caption;
	} else {
	    image_src = "";
	    caption = "loading first image";
	}
	controls = "";
	    
	// Create the table html
	if (this.table_format == "normal") {
	    table_html = table_html + '<table id="picture_table"><tr id="picture_row"><td id="picture_data">';
	    table_html = table_html + '<img  height='+this.def_force_height+' onClick="'+ this.name + '".left_click()" name="slide" ';
	    table_html = table_html + image_src + '></td>';
	    //table_html = table_html + '<img  height=450 onClick="ms.next_image()" name="slide"></td>';
	    table_html = table_html + '</tr><tr height="50" valign="top"><td bgcolor="#C0C0C0"><div id="caption">';
	    table_html = table_html + caption + '</div></td></tr>';
	    table_html = table_html + '<tr><td><div id="controls"><nobr>';
	    table_html = table_html + controls;
	    table_html = table_html + '</nobr></div></td></tr></table>';
	} else if (this.table_format == "table2") {
	    // TODO: define mouseover for close button to highlight
	    // TODO: overlay close box on TOP of picture if possible. Remove padding between rows.
	    table_html = table_html + '<table id="picture_table" border=1 bgcolor="#CCCCCC">'
	    if (0) {
		table_html = table_html + '<tr id="top_row"><td nowrap valign="top" align="right">';
		table_html = table_html + '<img onClick="ms.close();" height=10 name="close" src="';
		table_html = table_html + close_button_src
		table_html = table_html + '">';
	    }
	    table_html = table_html + '</td></tr>';
	    table_html = table_html + '<tr id="picture_row"><td><table border=0 width="100%"><tr><td width=5></td><td id="picture_data" align="center">';
	    table_html = table_html + '<img title="left click to go to next image" height='+this.def_force_height+' onClick="ms.next_image()" name="slide" ';
	    table_html = table_html + image_src + '></td>'
	    if (1) {
		// TODO: find some way of overlapping the main image in the upper right hand corner.
		// table_html = table_html + '<img valign="top" align="top" style="position: absolute; top:0; right: 0;" onClick="this.close();" height=10 name="close" src="';
		table_html = table_html + '<td valign="top" width=5>'
		table_html = table_html + '<img title="Stop slideshow, close image window" valign="top" align="top" style="position: relative; top:0; right: 0;" onClick="ms.close();" width=15 height=15 name="close" src="';
		table_html = table_html + close_button_src
		table_html = table_html + '">';
		table_html = table_html + '</td>';
	    }
	    table_html = table_html + '</tr></table></td>';
	    //table_html = table_html + '<img  height=450 onClick="ms.next_image()" name="slide"></td>';
	    table_html = table_html + '</tr>';
	    if (this.def_force_small!=1) {
		table_html = table_html + '<tr><td><table border=0 width="100%" bgcolor="#C0C0C0"><tr height="50" valign="top"><td><div id="caption">';
		table_html = table_html + caption + '</div></td></tr>';
		table_html = table_html + '<tr><td id="slideshow_error">' + this.error_msg + '</td></tr>';
		table_html = table_html + '</table></td></tr>';
		table_html = table_html + '<tr><td><div id="controls">';
		table_html = table_html + controls;
		table_html = table_html + '</div></td></tr>';
	    }
	    table_html = table_html + '</table>';

		    
	} else {
	    table_html = table_html + '<table id="picture_table"><tr id="picture_row"><td id="picture_data">'
	    table_html = table_html + 'Error: invalid value for this.table_format.</td></tr></table>';
	}
	    
	    
	// picture_row, picture_data, slide
	if (this.browser_name == "Microsoft Internet Explorer") {
	    document.all.slideshow_area.innerHTML=table_html; 
	} else {
	    document.getElementById("slideshow_area").innerHTML = table_html;
	}
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.update_display = function(index) {
	if (this.alert_debug) {alert("update_display("+index+") called");}
	if (this.debug>1) { this.msg("debug","update_display() called"); }
	// Figure out if we need to write the table first
	if (this.display_mode && this.redraw_table) {
	    this.redraw_table=0;
	    this.table_loaded = 1;
	    this.display_table(index);
	}	
	if (this.redraw_controls) {
	    this.redraw_controls=0;
	    this.display_controls(index);
	}
	this.display_image(index);
	this.display_caption(index);
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.toggle_slideshow = function() {
	if (this.alert_debug) {alert("toggle_slideshow() called");}
	if (this.debug) {this.msg("debug","toggle_slideshow() called");}
	this.redraw_controls=1;
	if (this.active==0) {
	    this.active=1;
	    this.random_image(0);
	} else {
	    this.stop();
	}
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.close = function() {
	if (this.alert_debug) {alert("close() called");}
	if (this.debug) {this.msg("debug","close() called");}
	var table_html;
	var new_width=1;
	    
	table_html = '';
	    
	this.redraw_controls=1;
	if (this.active) {this.stop();}
	if (this.browser_name == "Microsoft Internet Explorer") {
	    document.all.slideshow_area.innerHTML=table_html;
	    document.all.slideshow_area.width = new_width;
	} else {
	    document.getElementById("slideshow_area").innerHTML = table_html;
	    document.getElementById("slideshow_area").width = new_width;
	}
    }

    // ----------------------------------------------------------------
    //
    // TODO:
    // - Port this function from the get_intro_picture.pl perl program
    //   to replace the current page with a list of all valid snapshots
    //   sorted by directory. Or get get_intro_picture.pl to create it
    //   as a string for us to load
    // ----------------------------------------------------------------
    this.show_all_intros_page = function () {
	if (this.alert_debug) {alert("show_all_intros_page() called");}
	if (this.debug) {this.msg("debug","show_all_intros_page() called");}

	// replace the entire file contents
	document.open();
	document.write(unescape(this.all_table));
	document.close();
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.prev_image = function() {
	if (this.alert_debug) {alert("prev_image() called");}
	if (this.debug) { this.msg("debug","prev_image() called. cur="+this.cur_image); }
	var num_loops=0;
	var valid=0;
	

	if (this.active) {this.stop();}
	this.cur_image = this.cur_image - 1;
	if( this.cur_image < 0 ) {
	    this.cur_image = this.images.length-1;
	}
	this.load_image(this.cur_image,"prev");
    }

    // ----------------------------------------------------------------
    // slideshow_next_image
    //
    // advance to the next valid image.
    //
    // This is called if the user clicks on the picture window. It will
    // turn off the slideshow (if it is on_ and advance to the next window.
    // ----------------------------------------------------------------
    this.next_image = function() {
	if (this.alert_debug) {alert("next_image() called");}
	var num_loops=0;
	valid=0;
	if (this.debug) { this.msg("debug","next_image() called. cur="+this.cur_image); }

	if (this.active) {this.stop();}
	this.cur_image = this.cur_image + 1;
	if( this.cur_image == this.images.length ) {
	    this.cur_image = 0;
	}
	this.load_image(this.cur_image,"next");
    }
    this.left_click = function() {
	if (this.alert_debug) {alert("left_click() called");}
	this.next_image();
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.random_image = function(stop_slideshow) {
	if (this.alert_debug) {alert("random_image("+stop_slideshow+") called");}
	if (this.debug) { this.msg("debug","random_image() called. cur="+this.cur_image); }
	if (stop_slideshow==1) {
	    if (this.active) {this.stop();}
	} else {
	    if (this.active==0 && stop_slideshow!=2) return 0; // program error
	}
	
	this.previous_slide = this.cur_image;
	this.cur_image = Math.round(Math.random()*(this.images.length - 1));
	this.load_image(this.cur_image,"random");
	if (this.alert_debug) {alert("random_image finished");}
    }


    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.preload_images_now = function () {
	var pos=0;
	var num_try=0;
	var num_bad=0;
	var tot=this.images.length;
	//alert("this="+this+", this.images="+this.images);
	if (this.debug) { this.msg("debug","preload_images_now() called."); }
	while (pos<this.images.length) {
	    if (this.images[pos].status == "loaded") {
		// already loaded, skip it!
	    } else if (this.images[pos].status == "ugly") {
		// already loaded, skip it!
	    } else if (this.images[pos].status=="error") {
		num_bad++;
	    } else {
		num_try++;
	    }
	    pos++;
	}
	this.num_preloaded=0;
	this.num_preloaded_bad=0;
	this.num_preloaded_ugly=0;
	if (num_try+num_bad==0) {
	    this.msg("status","All "+tot+" images are already loaded. Preloading action cancelled.");
	    this.msg("debug","All "+tot+" images are already loaded. Preloading action cancelled.");
	} else {
	    this.tot_preloaded=num_try+num_bad;
	    if (num_bad>0) {
		this.msg("status","Attempting to preload "+num_try+" images and retry "+num_bad+" images marked bad. This may take a bit.");
	    } else {
		this.msg("status","Attempting to preload "+num_try+" images. This may take a few moments.");
	    }
		    
	    pos=0;
	    while (pos<this.images.length) {
		if (this.images[pos].status == "loaded") {
		    // already loaded, skip it!
		} else if (this.images[pos].status == "ugly") {
		    // already loaded, skip it!
		} else {
		    this.load_image(pos,"preload"); // load unloaded or retry bad ones
		}
		pos++;
	    }
	    this.msg("debug","preload_images_now() finished. bad="+num_bad+",try="+num_try+",tot="+tot);
	    // error: cannot show here! show after finished/cancelled/etc has tried X number of attempts.
	}
		    
	    // tell next load to remove status msg.
    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.startup = function() {
	if (this.alert_debug) {alert("startup() called");}
	if (this.debug) { this.msg("debug","startup() called."); }

	// load user options and save as default values
	var cookie_autostart = Get_Cookie('minislideshow_autostart');
	var cookie_delay = Get_Cookie('minislideshow_delay');
	var cookie_size = Get_Cookie('minislideshow_size');
	//alert("cookies: autostart="+cookie_autostart+", delay="+cookie_delay+", size="+cookie_size);

	if (cookie_autostart == "yes") {
	    this.active = 1;
	} else if (cookie_autostart == "no") {
	    this.active=0;
	} else {
	    // stick with predefined default ( usually on)
	}
	cookie_delay=cookie_delay*1; // convert to a number if needed
	if (cookie_delay && cookie_delay>0 && cookie_delay<20000) {
	    this.delay = (cookie_delay)*1;
	}
	if (cookie_size) {
	    if (cookie_size == "large") {this.big_setting=1;}
	    if (cookie_size == "medium") {this.big_setting=0;}
	    if (cookie_size == "small") {this.big_setting=-1;}
	}
	this.default_active=this.active;
	this.default_delay=this.delay;
	this.default_big_setting=this.big_setting;

	// capture current settings
	this.display_table(-1); // doesn't speed things up much. waah!
	if (this.preload_images) {
	    // don't do this anymore, takes too long with lots of images
	    //this.preload_images_now();
	}
	this.cur_image=-1;
	this.redraw_controls=1;
	this.random_image(2); // load one now before waiting

	// we're now over-ridding max_width and max_height to apply to images.
	if (this.def_force_image_width>0) {
	    this.image_max_width = this.def_force_image_width;
	    this.image_min_width = this.def_force_image_width;
	}
	if (this.def_force_image_height>0) {
	    this.image_max_height = this.def_force_image_height;
	    this.image_min_height = this.def_force_image_height;
	    //if (this.image_min_height > this.image_max_height) { this.image_min_height = this.image_max_height;}
	    //if (this.def_image_max_width <=0&&this.image_max_width>this.image_max_height) {
	    if (this.def_force_image_width <=0) {
		this.image_max_width = 0.9*this.image_max_height;
		this.image_min_width = 0.7*this.image_max_height;
	    }
	}

    }

    // ----------------------------------------------------------------
    // ----------------------------------------------------------------
    this.stop = function() {
	if (this.debug) { this.msg("debug","stop() called."); }
	if (this.active) {
	    if (this.use_setinterval) {
		clearInterval(this.interval);
	    } else {
		clearTimeout(this.interval);
	    }
	    this.active=0;
	    this.display_controls(this.cur_image); // forces buttons to go to start slideshow
	}
	// tell all in progress loads to stop doing anything & not replace current picture
	var pos=0;
	while (pos<this.images.length) {
	    if(this.images[pos].action=="next"||this.images[pos].action=="prev"||this.images[pos].action=="random"){
		this.images[pos].action="ignore";
	    }
	    pos++;
	}
    }

    // this.msg("error","test mdg"); // test: it works!

    // ensure old onload code is preserved.
    var slideshow_old_onload = window.onload;
    slideshow_active=1;
    window.onload = function(e){
       // execute old onload code...
       var not_found_msg = "Sorry, no slideshow pictures were found.";
       var dest_defined = 0;
       
       if (slideshow_old_onload) slideshow_old_onload(e);
       /* what ever you want to add to the onload function here */

       if (this.browser_name == "Microsoft Internet Explorer") {
	    if (document.all.slideshow_area) {
		dest_defined = 1;
	    }
	} else {
	    if (document.getElementById("slideshow_area")) {
		dest_defined = 1;
	    }
	}
	if (dest_defined) {
	    if (this.ms.num_defined>0) {
	    	// works! warning: assumes "var ms = new mini_slideshow(); already called"
		// todo: fix to make work in all cases. can't call this.startup due to scoping.
		// todo: why does document.onkeypress not give error below?
		this.ms.startup();
	    } else {
		if (this.slideshow_browser_name == "Microsoft Internet Explorer") {
		    document.all.slideshow_area.innerHTML= not_found_msg; 
		} else {
		    document.getElementById("slideshow_area").innerHTML = not_found_msg;
		}
	    }
	}
    }

    // insert keypress callback. ie uses document & not window, netscape works with both
    // note that passed event argument is only good for netscape, ie uses a global var
    var slideshow_old_onkeypress;
    if (document.onkeypress) {
	slideshow_old_onkeypress = document.onkeypress;
    }
    document.onkeypress = function(e) {
       // execute old onload code...
       if (slideshow_old_onload) slideshow_old_onkeypress(e);

       /* what ever you want to add to the onload function here */
       ms.process_keypress(e);
    }
}
