
/**************************************************************/
/*                    Slideshow functions                     */
/**************************************************************/

function Slideshow() {
}

var slideshow = new Slideshow();
var PHOTO_BORDER_SIZE=14;
var TINY_WIDTH=60;
var TINY_HEIGHT=40;
var TINY_EXTRA=6;
var DEFAULT_TIME_BETWEEN_TRANSITIONS = 5000;
var MIN_TIME_BETWEEN_TRANSITIONS = 1000;
var MAX_TIME_BETWEEN_TRANSITIONS = 10000;

Slideshow.prototype.initObjects = function() {
    this.photoDisplay = new PhotoDisplay();

    this.counter      = document.getElementById("counter");
    this.prev         = document.getElementById("prev");
    this.playPause    = document.getElementById("playPause");
    this.next         = document.getElementById("next");
    this.loop         = document.getElementById("loop");
    this.sectionTitle = document.getElementById("sectionTitle");
    this.link         = document.getElementById("link");
    
    this.exif         = document.getElementById("exif");
    this.exifContent  = document.getElementById("exifContent");
    this.exifTab      = document.getElementById("exifTab");

    this.tnails       = document.getElementById("tnails");
    var cnt=0;
    this.tiny = new Array();
    while (true) {
	var obj = document.getElementById("tiny_"+cnt);
	if (!obj) break;
	this.tiny[cnt++] = obj;
	if (is_ie && !is_ie6up) {
	    /* IE5 has a bug where padding on inline elements is ignored
	     * unless the element also has a dimension set, so let's set
	     * the dimension here */
	    obj.parentNode.style.width = TINY_WIDTH+TINY_EXTRA;
	}
    }
};


Slideshow.prototype.initSliders = function() {
    this.speedSlider = new Slider(document.getElementById("speedSlider"),
				  document.getElementById("speedSliderInput"));
    this.speedSlider.setMinimum(0);
    this.speedSlider.setMaximum(MAX_TIME_BETWEEN_TRANSITIONS-
				MIN_TIME_BETWEEN_TRANSITIONS);
    this.speedSlider.setBlockIncrement(1000);
    this.speedSlider.setUnitIncrement(500);

    this.timeBetweenTransitions = getCookie("timeBetweenTransitions");
    if (!this.timeBetweenTransitions || this.timeBetweenTransitions=="")
	this.timeBetweenTransitions = DEFAULT_TIME_BETWEEN_TRANSITIONS;
    this.speedSlider.setValue(timeToSpeedSlider(this.timeBetweenTransitions));
    this.speedSlider.onchange = function() { slideshow.speedSliderChanged(); }

    this.countSlider = new Slider(document.getElementById("countSlider"),
				  document.getElementById("countSliderInput"));
    this.countSlider.setMinimum(0);
    this.countSlider.setMaximum(this.photos.length-1);
    this.countSlider.setBlockIncrement(Math.floor(this.photos.length/10));
    this.countSlider.setValue(this.currentPhotoNum);

    this.countSlider.onchange   = function() { slideshow.countSliderChanged();}
    this.countSlider.onmousedown= function() { slideshow.countSliderDown(); }
    this.countSlider.onmouseup  = function() { slideshow.countSliderUp(); }
};


Slideshow.prototype.init = function(photos, photoNum, maxWidth, maxHeight) {
    /* check if we have a compatible browser */
    if (!isBrowserCompatible()) return;
    
    var i;
    for (i=0; i < photos.length; i++) photos[i].index = i;

    this.photos = photos;
    this.currentPhotoNum = photoNum;
    this.initSliders();
    this.initObjects();

    this.effect = new FadeEffect(this.photoDisplay);
    this.tnailEffect = new ThumbnailEffect(this);
    this.bothEffects = new BothEffects(this.effect, this.tnailEffect);

    /* get rid of the filename from playPauseObj.src to get the
     * theme images dir */
    this.imageDir = this.playPause.src.substring(0, this.playPause.src.
						 lastIndexOf('/'));

    if (this.sectionTitle) {
	this.indexPage = this.sectionTitle.getElementsByTagName('A')[0].href;
    }

    this.nextEnabled = this.currentPhotoNum==this.photos.length-1 ? false:true;
    this.prevEnabled = this.currentPhotoNum==0 ? false:true;

    /* store the max width and height across all photos */
    this.maxWidth = maxWidth;
    this.maxHeight= maxHeight;

    //    /* grab the isLooping value from the cookie */
    //    this.isLooping = getCookie("loop");
    //    if (this.isLooping) this.toggleLoop();

    /* hide the exif tab entirely if this page has set its display 
       attribute to "none"; otherwise display it in the proper position */
    this.repositionExif(this.exifTab && this.exifTab.style &&
			this.exifTab.style.display == 'none' ? 1 : 0);

    /* start the slideshow if the corresponding parameter exists in the URL */
    var params = new URLParameters(document.location.href);
    var photo;
    if (params['slideshow'] != null && params['slideshow'] == "1") {
	this.doPlayPause(true);
	this.continueSlideshow();
    }
};


Slideshow.prototype.continueSlideshow = function() {
    var next;
    if (this.currentPhotoNum >= this.photos.length-1) {
	if (!this.isLooping) {
	    this.slideshowDone();
	    return;
	}
	next = 0;
    } else {
	next = this.currentPhotoNum+1;
    }
    
    /* start preloading the next image */
    this.preloader = this.photos[next].preload();

    /* set a timer to initiate the transition */
    this.transitionTimeoutId = setTimeout("slideshow.doContinueSlideshow()",
					  this.timeBetweenTransitions);
};
Slideshow.prototype.doContinueSlideshow = function() {
    this.transitionTimeoutId = null;
    
    /* looping may have been turned off; check for that again here */
    if (this.currentPhotoNum >= this.photos.length-1 && !this.isLooping) {
	this.slideshowDone();
	return;
    }
    
    this.transitionTo(this.photos[this.currentPhotoNum==this.photos.length-1 ? 
				  0 : this.currentPhotoNum+1], 
		      "slideshow.continueSlideshow()");
};
Slideshow.prototype.slideshowDone = function() {
    this.doPlayPause(true);
    this.implicitPause = true;
};


Slideshow.prototype.transitionToNow = function(photo, callback) {
    this.transitionTo_(photo, callback, false);
};
Slideshow.prototype.transitionTo = function(photo, callback) {
    this.transitionTo_(photo, callback, true);
};
Slideshow.prototype.transitionTo_ = function(photo, callback, doEffect) {
    if (!isBrowserCompatible()) return;

    if (this.transitioningTo) this.abortTransition();

    this.transitioningTo = photo;
    this.transitionCallback = callback;
    this.doEffect = doEffect;

    /* create a preloading object for this photo if one doesn't 
       already exist */
    if (!this.preloader) {
	this.preloader = photo.preload();
    }
    /* check if the preload has completed */
    if (!this.preloader.isDone()) {
	this.preloader.setCallback("slideshow.transitionPhotoLoaded()");
	this.preloader.showLoading();
    } else {
	this.startTransition(photo);
    }
};
Slideshow.prototype.transitionPhotoLoaded = function() {
    this.startTransition(this.transitioningTo);
}



Slideshow.prototype.startTransition = function(photo) {
    if (!this.doEffect) {
	this.photoDisplay.update(this.preloader.get(1).result());
	this.updateThumbnails(photo);
	this.endTransition(false);
	return;
    } else {
	this.bothEffects.start(this.photos[this.currentPhotoNum], photo,
			       this.preloader, 
			       "slideshow.endTransition(false)");
    }
};
Slideshow.prototype.endTransition = function(isAbort) {
    var xml = this.preloader.get(1).result();
    this.currentPhotoNum = this.transitioningTo.index;
    this.transitioningTo = null;

    // update the exif information tab
    this.updateExif(xml);

    // update the section title
    this.indexPage = xml.page.src;
    if (this.sectionTitle) {
	var t = (xml.section.title != "" && xml.page.src && xml.page.src!="") ?
	    "<a href='"+xml.page.src+"'>" + xml.section.title + (xml.section.date && xml.section.date!="" ? " <span class=\"date\">("+nbsp(xml.section.date)+")</span>" : "") + "</a>" : "&nbsp;";
	this.sectionTitle.innerHTML = t;
    }
    document.title = "Photo Album: " + 
        (xml.title && xml.title != "" ? xml.title :
	 (xml.section && xml.section != "" ? xml.section :
	  (xml.album.title ? xml.album.title : "")));
	 
    // update the "link to this page"
    if (this.link) {
	this.link.href = this.photos[this.currentPhotoNum].pageURL();
    }
    // update the photoAnchor
    if (this.photoDisplay.photoAnchor) {
	this.photoDisplay.photoAnchor.href = this.nextLink();
    }

    // update the anchor tags for the prev/play/next buttons
    this.prev.parentNode.href = this.prevLink();
    this.next.parentNode.href = this.nextLink();
    this.playPause.parentNode.href=this.photos[this.currentPhotoNum].pageURL();

    // update the tiny thumbnails
    //this.updateThumbnails(this.photos[this.currentPhotoNum]);

    this.updateCounter(this.currentPhotoNum);
    this.adjustNextPrev();

    // reset the preloader
    this.preloader = null;

    var cb = this.transitionCallback;
    this.transitionCallback = null;
    if (cb && !isAbort) eval(cb);
};


Slideshow.prototype.abortTransition = function() {
    if (!this.transitioningTo) return;

    if (this.bothEffects.inProgress()) {
	this.bothEffects.abort(true);
	this.endTransition(true);
    }

    // clear callbacks, just in case they are set
    if (this.preloader) {
	this.preloader.abort();
	this.preloader = null;
    }

    this.transitionCallback = null;
    this.transitioningTo = null;
};


Slideshow.prototype.updateThumbnails = function(photo) {
    var preloadIndex = 2;
    var tinyIndex = 0;

    var left  = Math.floor(slideshow.tiny.length/2);
    var right = Math.floor(slideshow.tiny.length - left - 1);
    var spacer;
    if (photo.index - left < 0 || 
	photo.index+right >= slideshow.photos.length) {
	spacer = this.preloader.get(preloadIndex++).result();
    }
    for (i=photo.index - left; i < photo.index; i++) {
	if (i < 0) {
	    this.updateOneTiny(this.tiny[tinyIndex++], spacer, -1, photo);
	} else {
	    this.updateOneTiny(this.tiny[tinyIndex++], 
			       this.preloader.get(preloadIndex++).result(), i,
			       photo);
	}
    }
    this.updateOneTiny(this.tiny[tinyIndex++], 
		       this.preloader.get(preloadIndex++).result(), 
		       photo.index, photo);

    for (i=photo.index + 1; i <= photo.index+right; i++) {
	if (i >= slideshow.photos.length) {
	    this.updateOneTiny(this.tiny[tinyIndex++], spacer, -1, photo);
	} else {
	    this.updateOneTiny(this.tiny[tinyIndex++], 
			       this.preloader.get(preloadIndex++).result(), i,
			       photo);
	}
    }
};
Slideshow.prototype.updateOneTiny = function(obj, preloadResult, index, photo){
    if (index < 0) {
	obj.className = "blank";
	obj.src = preloadResult.src;
	obj.width  = TINY_WIDTH;
	obj.height = TINY_HEIGHT;
	obj.parentNode.style.padding = "0px";
	obj.parentNode.href = this.photos[photo.index].pageURL();
	obj.parentNode.onclick = function() { return false; }
    }
    else {
	var w = preloadResult.width;
	var pl = Math.floor((TINY_WIDTH - w)/2);
	var pr = TINY_WIDTH - w - pl;

	obj.src    = preloadResult.src;
	obj.width  = preloadResult.width;
	obj.height = preloadResult.height;
	obj.className = "tiny";
	obj.parentNode.style.padding = "0px "+pr+"px 0px "+pl+"px";
	obj.parentNode.href = this.photos[index].pageURL();
	obj.parentNode.onclick = function() { 
	    slideshow.gotoPhoto(slideshow.photos[index]); return false; 
	}
    }
};


Slideshow.prototype.doPlayPause = function(onlyUpdateDisplay) {
    /* check if we have a compatible browser and whether we have
     * initialized the slideshow or not */
    if (!isBrowserCompatible() || !this.photos) return;

    this.implicitPause = false;
    if (this.isPlaying) {
	/* stop playing */
	this.isPlaying = false;

	/* since we are stopping, we should change the button to show the
	 * play icon */
	this.playPause.src = this.imageDir + "/play.gif";
	this.playPause.alt = "Play";
	this.playPause.title = "Play";
	this.playPause.onmouseover = function() { this.src = slideshow.imageDir + "/play-active.gif"; }
	this.playPause.onmouseout  = function() { this.src = slideshow.imageDir + "/play.gif"; }

	/* cancel play timers */
	if (!onlyUpdateDisplay) {
	    if (this.transitioningTo) this.abortTransition();
	    if (this.transitionTimeoutId) {
		clearTimeout(this.transitionTimeoutId);
		this.transitionTimeoutId = null;
	    }
	}

    } else {
	/* start playing */
	this.isPlaying = true;

	/* since we are playing, we should change the button to show the
	 * pause icon */
	this.playPause.src = this.imageDir + "/pause.gif";
	this.playPause.alt = "Pause";
	this.playPause.title = "Pause";
	this.playPause.onmouseover = function() { this.src = slideshow.imageDir + "/pause-active.gif"; }
	this.playPause.onmouseout  = function() { this.src = slideshow.imageDir + "/pause.gif"; }

	/* we may have some transition in progress in case the user pressed
	 * the prev/next buttons not too long earlier */
	if (this.transitioningTo) this.abortTransition();

	/* initiate the play timers */
	if (!onlyUpdateDisplay) {
	    this.transitionTo(this.photos[this.currentPhotoNum == 
					  this.photos.length-1 ?
					  0 : this.currentPhotoNum+1],
			      "slideshow.continueSlideshow()");
	}
    }
};


Slideshow.prototype.doPrev = function() {
    /* check if we have a compatible browser and whether we have
     * initialized the slideshow or not */
    if (!isBrowserCompatible() || !this.photos || !this.prevEnabled) return;

    var prev;
    if (this.transitioningTo) {
	if (this.bothEffects.inProgress()) {
	    // we've already started fading in the new photo;
	    // let's go back to the old photo
	    prev = this.currentPhotoNum;
	} else {
	    if (!this.isLooping && this.currentPhotoNum==0) return;
	    prev = this.currentPhotoNum==0 ? this.photos.length-1 : 
		this.currentPhotoNum-1;
	}
	this.abortTransition();
    } else {
	if (!this.isLooping && this.currentPhotoNum==0) return;
	prev = this.currentPhotoNum==0 ? this.photos.length-1: 
	         this.currentPhotoNum-1;
    }

    this.implicitPause = false;
    this.jumpTo(this.photos[prev]);
};


Slideshow.prototype.doNext = function() {
    /* check if we have a compatible browser and whether we have
     * initialized the slideshow or not */
    if (!isBrowserCompatible() || !this.photos || !this.nextEnabled) return;

    var next;
    if (this.transitioningTo) {
	if (this.bothEffects.inProgress()) {
	    // we've already started fading in the new photo; let's transition
	    // fully to the new photo
	    next = this.transitioningTo.index;
	} else {
	    if (!this.isLooping && this.currentPhotoNum==this.photos.length-1) 
		return;
	    next = this.currentPhotoNum==this.photos.length-1
				   ? 0 : this.currentPhotoNum+1;
	}
	this.abortTransition();
    } else {
	if (!this.isLooping && this.currentPhotoNum==this.photos.length-1) 
	    return;
	next = this.currentPhotoNum==this.photos.length-1
				   ? 0 : this.currentPhotoNum+1;
    }

    this.implicitPause = false;
    this.jumpTo(this.photos[next]);
}


Slideshow.prototype.toggleLoop = function() {
    /* check if we have a compatible browser and whether we have
     * initialized the slideshow or not */
    if (!isBrowserCompatible() || !this.photos) return;

    /* reset the implicitPause here coz we don't really want the playback
     * to automatically restart after this point */
    this.implicitPause = false;

    if (this.isLooping) {
	/* stop looping */
	this.isLooping = false;

	this.loop.src = this.imageDir + "/loop.gif";
	this.loop.onmouseover = function() { this.src = slideshow.imageDir + "/loop-active.gif"; }
	this.loop.onmouseout  = function() { this.src = slideshow.imageDir + "/loop.gif"; }

	this.adjustNextPrev();

    } else {
	/* start looping */
	this.isLooping = true;

	/* since we are playing, we should change the button to show the
	 * pause icon */
	this.loop.src = this.imageDir + "/loop-on.gif";
	this.loop.onmouseover = function() { this.src = slideshow.imageDir + "/loop-on-active.gif"; }
	this.loop.onmouseout  = function() { this.src = slideshow.imageDir + "/loop-on.gif"; }


	this.enableNextPrev("next", true);
	this.enableNextPrev("prev", true);
    }
}


Slideshow.prototype.gotoIndexPage = function() {
    /* check if we have a compatible browser */
    if (!isBrowserCompatible()) return;

    if (this.indexPage) {
	document.location.href = this.indexPage;
    } else {
	document.location.href = "../";
    }
}

Slideshow.prototype.gotoPhoto = function(photo) {
    /* check if we have a compatible browser and whether we have
     * initialized the slideshow or not */
    if (!isBrowserCompatible() || !this.photos) return;
    this.implicitPause = false;
    this.jumpTo(photo);
}




Slideshow.prototype.jumpTo = function(photo, doEffect) {
    if (this.transitioningTo) this.abortTransition();
    if (this.preloader && this.preloader.getData() != photo) {
	this.preloader.abort();
	this.preloader = null;
    }

    if (this.implicitPause) {
	/* restart playing if it makes sense */
	if (photo.index < this.photos.length-1) {
	    this.doPlayPause(true);
	}
    }

    var cb=null;
    if (this.isPlaying) {
	if (this.transitionTimeoutId) {
	    clearTimeout(this.transitionTimeoutId);
	}
	cb = "slideshow.continueSlideshow()";
    }
    if (doEffect) {
	this.transitionTo(photo, cb);
    } else {
	this.transitionToNow(photo, cb);
    }
}


Slideshow.prototype.updateCounter = function(num, dontUpdateSlider) {
    this.counter.innerHTML = (num+1)+" of "+this.photos.length;
    if (!dontUpdateSlider) {
	this.countSlider.explicitModification = true;
	this.countSlider.setValue(num);
	this.countSlider.explicitModification = false;
    }
}


Slideshow.prototype.adjustNextPrev = function() {
    if (this.isLooping) return;

    if (this.currentPhotoNum == this.photos.length-1)
	this.enableNextPrev("next", false);
    else 
	this.enableNextPrev("next", true);
    
    if (this.currentPhotoNum == 0)
	this.enableNextPrev("prev", false);
    else 
	this.enableNextPrev("prev", true);
}
Slideshow.prototype.enableNextPrev = function(which, enable) {
    if (enable == (which=="next" ? this.nextEnabled : this.prevEnabled)) return;

    var obj;
    if (which == "next") { obj = this.next; this.nextEnabled = enable; }
    else { obj = this.prev; this.prevEnabled = enable; }
    
    if (enable) {
	obj.src = this.imageDir + "/" + which + ".gif";
	obj.onmouseover = function() { this.src = slideshow.imageDir + "/" + which + "-active.gif"; }
	obj.onmouseout  = function() { this.src = slideshow.imageDir + "/" + which + ".gif"; }
    } else {
	obj.src = this.imageDir + "/" + which + "-disabled.gif";
	obj.onmouseover = function() { this.src = slideshow.imageDir + "/" + which + "-disabled.gif"; }
	obj.onmouseout  = function() { this.src = slideshow.imageDir + "/" + which + "-disabled.gif"; }
    }
};


Slideshow.prototype.prevLink = function() {
    // always use looping for this; the actual doPrev/doNext functions
    // will prevent looping if that feature is turned off
    return this.photos[this.currentPhotoNum==0 ? 
		       this.photos.length-1 : this.currentPhotoNum-1].pageURL();
}
Slideshow.prototype.nextLink = function() {
    // always use looping for this; the actual doPrev/doNext functions
    // will prevent looping if that feature is turned off
    return this.photos[this.currentPhotoNum==this.photos.length-1 ? 
		       0 : this.currentPhotoNum+1].pageURL();
};


function keyVal(key, val) {
    return "<tr><td class=\"key\">"+key+":</td><td class=\"val\">"+val+
	"</td></tr>";
}
Slideshow.prototype.updateExif = function(xml) {
    if (xml.exif.iso == "" &&
	xml.exif.aperture == "" &&
	xml.exif.exposure == "" &&
	xml.exif.flash == "" &&
	xml.exif.model == "" &&
	xml.exif.lens  == "") {

	this.exifContent.innerHTML = "";
	this.repositionExif(true);
	return;
    }

    var html = "<table class=\"tight\">";
    var empty = "<tr><td class=\"key\">&nbsp;</td><td class=\"val\">&nbsp;</td></tr>";
    var plus = "";
    if (xml.exif.iso != "") 
	html += keyVal("ISO setting", xml.exif.iso);
    else plus += empty;
    if (xml.exif.aperture != "") 
	html += keyVal("Aperture", xml.exif.aperture);
    else plus += empty;
    if (xml.exif.exposure != "") 
	html += keyVal("Exposure", xml.exif.exposure);
    else plus += empty;
    if (xml.exif.flash != "") 
	html += keyVal("Flash", xml.exif.flash);
    else plus += empty;
    if (xml.exif.model != "") 
	html += keyVal("Camera", xml.exif.model);
    else plus += empty;
    if (xml.exif.lens != "") 
	html += keyVal("Lens", xml.exif.lens);
    else plus += empty;
    html += plus+"</table>";

    this.exifContent.innerHTML = html;
    this.repositionExif(false);
};


Slideshow.prototype.repositionExif = function(hide) {
    if (hide) {
	this.exif.style.display = 'none';
	this.exifTab.style.display = 'none';
	return;
    }
    var showExifCookie = getCookie("showExif");
    if (showExifCookie=="1") { 
	this.exif.style.left = (getPositionInDocument(this.photoDisplay.photo, 
						      "x") +
				this.photoDisplay.photo.offsetWidth) + "px";
	this.exif.style.top  = (getPositionInDocument(this.photoDisplay.photo, 
						      "y") +
				10) + "px";
	this.exif.style.display = '';

	this.exifTab.style.left=(getPositionInDocument(this.photoDisplay.photo,
						       "x") +
				   this.photoDisplay.photo.offsetWidth + 
				   this.exif.offsetWidth) + "px";
    } else {
	this.exifTab.style.left=(getPositionInDocument(this.photoDisplay.photo,
						       "x") +
				 this.photoDisplay.photo.offsetWidth) + "px";
	this.exif.style.display = 'none';
    }
    this.exifTab.style.top  = (getPositionInDocument(this.photoDisplay.photo, 
						     "y") + 20) + "px";
    this.exifTab.style.display = '';
    this.exifTab.style.visibility = 'visible';
};


Slideshow.prototype.toggleExif = function() {
    if (!isBrowserCompatible()) { return; }

    if (this.exif.style.display == 'none') {
	setCookie("showExif", 1, "", "/");
    } else {
	setCookie("showExif", 0, "", "/");
    }
    this.repositionExif();
};




Slideshow.prototype.countSliderChanged = function() {
    if (this.countSlider.explicitModification) return;

    if (this.countSlider.inUse) 
	this.updateCounter(this.countSlider.getValue());
    else {
	this.jumpTo(this.photos[this.countSlider.getValue()], this.isPlaying);
    }
};
Slideshow.prototype.countSliderDown = function() {
    if (this.isPlaying) {
	/* disable the playing */
	if (this.transitioningTo) this.abortTransition();
	if (this.transitionTimeoutId) {
	    clearTimeout(this.transitionTimeoutId);
	    this.transitionTimeoutId = null;
	}
    }
    
    this.countSlider.inUse = true;
};
Slideshow.prototype.countSliderUp = function() {
    if (this.transitioningTo) this.abortTransition();

    if (this.currentPhotoNum != this.countSlider.getValue()) {
	/* jumpTo will automatically re-enable the play timers */
	this.jumpTo(this.photos[this.countSlider.getValue()], this.isPlaying);
    } else {
	/* re-enable the play timer */
	this.continueSlideshow();
    }
    
    this.countSlider.inUse = false;
}



Slideshow.prototype.speedSliderChanged = function() {
    if (this.speedSlider.inUse) return;

    var t = speedSliderToTime(this.speedSlider.getValue());
    if (t==this.timeBetweenTransitions) return;

    this.timeBetweenTransitions = t;
    setCookie("timeBetweenTransitions", t, "", "/");
    if (this.transitionTimeoutId) {
	clearTimeout(this.transitionTimeoutId);
	this.transitionTimeoutId=setTimeout("slideshow.doContinueSlideshow()", 
					    this.timeBetweenTransitions);
    }
}
Slideshow.prototype.speedSliderDown = function() {
    this.speedSlider.inUse = true;
}
Slideshow.prototype.speedSliderUp = function() {
    this.speedSlider.inUse = false;

    var t = speedSliderToTime(this.speedSlider.getValue());
    if (t==this.timeBetweenTransitions) return;

    this.timeBetweenTransitions = t;
    setCookie("timeBetweenTransitions", t, "", "/");
    if (this.transitionTimeoutId) {
	clearTimeout(this.transitionTimeoutId);
	this.transitionTimeoutId=setTimeout("slideshow.doContinueSlideshow()", 
					    this.timeBetweenTransitions);
    }
}

function speedSliderToTime(val) {
    return (MAX_TIME_BETWEEN_TRANSITIONS - val);
}
function timeToSpeedSlider(val) {
    return (MAX_TIME_BETWEEN_TRANSITIONS - val);    
}




function prop(o) {
    var i;
    var s = "";
    for (i in o) {
	try {
	    s += "'"+i+"'='"+o[i]+"'\n";
	} catch(e) {}
    }
    return s;
}
/**************************************************************/
/*                    Photo XML parser                        */
/**************************************************************/
function PhotoXMLParser(xml) {
    if (!xml) { this.setNull(); return; }

    var elem;
    elem = xml.getElementsByTagName("main")[0];
    this.main = new Object();
    this.main.src    = elem.getAttribute("src");
    this.main.width  = Math.floor(elem.getAttribute("width"));
    this.main.height = Math.floor(elem.getAttribute("height"));

    elem = xml.getElementsByTagName("tiny")[0];
    this.tiny = new Object();
    this.tiny.src    = elem.getAttribute("src");
    this.tiny.width  = Math.floor(elem.getAttribute("width"));
    this.tiny.height = Math.floor(elem.getAttribute("height"));

    elem = xml.getElementsByTagName("tnail")[0];
    this.tnail = new Object();
    this.tnail.src    = elem.getAttribute("src");
    this.tnail.width  = Math.floor(elem.getAttribute("width"));
    this.tnail.height = Math.floor(elem.getAttribute("height"));

    this.title  = this.getText_(xml, "title");
    this.descr  = this.getText_(xml, "descr");
    this.date   = this.getText_(xml, "date");

    elem = xml.getElementsByTagName("section")[0];
    this.section       = new Object();
    this.section.date  = elem.getAttribute("date");
    this.section.title = this.getText_(xml, "section");

    elem = xml.getElementsByTagName("page")[0];
    this.page       = new Object();
    this.page.src   = elem.getAttribute("src");
    this.page.date  = elem.getAttribute("date");
    this.page.title = this.getText_(xml, "page");

    elem = xml.getElementsByTagName("album")[0];
    this.album       = new Object();
    this.album.src   = elem.getAttribute("src");
    this.album.date  = elem.getAttribute("date");
    this.album.title = this.getText_(xml, "album");

    this.exif = new Object();
    this.exif.iso     = this.getText_(xml, "iso");
    this.exif.aperture= this.getText_(xml, "aperture");
    this.exif.exposure= this.getText_(xml, "exposure");
    this.exif.flash   = this.getText_(xml, "flash");
    this.exif.model   = this.getText_(xml, "model");
    this.exif.lens    = this.getText_(xml, "lens");
}
PhotoXMLParser.prototype.setNull = function() {
    this.main = new Object();
    this.main.src = "";
    this.main.width = this.main.height = 0;

    this.tiny = new Object();
    this.tiny.src = "";
    this.tiny.width = this.tiny.height = 0;

    this.tnail = new Object();
    this.tnail.src = "";
    this.tnail.width = 0;
    this.tnail.height = 0;
    
    this.title = this.descr = this.date = this.section = "";

    this.page = new Object();
    this.page.src = this.page.title = "";

    this.album = new Object();
    this.album.src = this.album.title = "";

    this.exif = new Object();
    this.exif.iso   = this.exif.aperture = this.exif.exposure = "";
    this.exif.flash = this.exif.model    = this.exif.lens     = "";
};
PhotoXMLParser.prototype.getText_ = function(xml, tagName) {
    var elem = xml.getElementsByTagName(tagName);
    if (elem && elem[0] && elem[0].firstChild && elem[0].firstChild.data)
	return elem[0].firstChild.data;
    else return "";
}


/**************************************************************/
/*                    Preloader functions                     */
/**************************************************************/
function Preloader(callback) {
    this.callback = callback;
    this.objects = new Array();
}

Preloader.prototype.setCallback = function(callback) {
    if (!this.isDone()) this.callback = callback;
};

Preloader.prototype.setData = function(data) { this.data = data; };
Preloader.prototype.getData = function() { return this.data; };

Preloader.prototype.add = function(object) {
    this.objects[this.objects.length] = object;
    object.setPreloader(this);
    return object;
};
Preloader.prototype.start = function() {
    var i;
    for (i=0; i < this.objects.length; i++) {
	this.objects[i].start();
    }
};


Preloader.prototype.done = function(object) {
    if (!this.isDone()) return;

    if (this.mustHideLoading) this.hideLoading_();

    if (this.callback) eval(this.callback);
    this.callback = null;
}

Preloader.prototype.isDone = function() { 
    // check if all the objects are done
    var i;
    for (i=0; i < this.objects.length; i++) {
	if (!this.objects[i].isDone()) return false;
    }
    return true;
};

Preloader.prototype.get = function(index) { return this.objects[index]; }


Preloader.showPreloadCount=0;
Preloader.loading;
Preloader.prototype.showLoading = function() {
    if (this.isDone() || this.mustHideLoading) return;

    this.mustHideLoading = true;
    Preloader.showPreloadCount++;
    if (Preloader.showPreloadCount > 1) return;

    if (!Preloader.loading) {
	Preloader.loading = document.getElementsByTagName('body')[0].
	    appendChild(createElement('div'));
	Preloader.loading.style.display  = '';
	Preloader.loading.style.position = 'absolute';
	Preloader.loading.className = "loading";
	Preloader.loading.innerHTML = "<img src=\""+slideshow.imageDir+"/../loading-small.gif\" width=16 height=16 alt=\"\"> Loading...";
    }

    // set the dimensions of the object to overlap the "link" object
    Preloader.loading.style.left = getPositionInDocument(slideshow.link, "x")
				    + "px";
    Preloader.loading.style.top  = (getPositionInDocument(slideshow.link, "y")
				    + slideshow.link.offsetHeight) + "px";
    Preloader.loading.style.width = slideshow.link.offsetWidth + "px";

    Preloader.loading.style.display = '';
};
Preloader.prototype.hideLoading_ = function() {
    Preloader.showPreloadCount--;
    this.mustHideLoading = false;
    if (Preloader.showPreloadCount > 0) return;
    Preloader.loading.style.display = "none";
};
Preloader.prototype.abort = function() {
    // this function doesn't really abort the load operations; it just
    // cancels the callbacks for them
    if (this.isDone()) return;
    this.callback = null;
    if (this.mustHideLoading) this.hideLoading_();
};


Preloader.image = function(url) {
    this.url = url;
};
Preloader.image.prototype.setPreloader = function(preloader) { 
    this.preloader = preloader; 
};

Preloader.image.prototype.start = function() {
    /* Safari has some bugs with the way it handles "new Image()" objects.
     * The "this" element is not set correctly when it invokes event 
     * handlers such as "onload()".  Using document.createElement() fixes
     * this problem, but causes the image.width and image.height elements
     * to not get set.  Arrgh! */

    this.image = new Image();
    this.isLoading = true;

    var thisObject = this;
    this.image.onload  = function() { thisObject.done(true);  }
    this.image.onerror = function() { thisObject.done(false); }
    this.image.onabort = function() { thisObject.done(false); }

    this.image.src = this.url;
};
Preloader.image.prototype.done = function(success) {
    this.success = true;
    this.loaded = true;
    this.preloader.done(this);
};
Preloader.image.prototype.isDone = function() {
    return this.loaded ? true : false;
};
Preloader.image.prototype.result = function() {
    return this.image;
};



Preloader.xml = function(url) {
    this.url = url;
};
Preloader.xml.prototype.setPreloader = function(preloader) { 
    this.preloader = preloader; 
};

Preloader.xml.prototype.start = function() {
    var req;
    var object;
    if (window.XMLHttpRequest) {
	// branch for native XMLHttpRequest object
        req = new XMLHttpRequest();
	object = this;
        req.onreadystatechange = function() {object.xmlReqChange_(req);}
        req.open("GET", this.url, true);
        req.send(null);
    } else if (window.ActiveXObject) {
	// branch for IE/Windows ActiveX version
        req = new ActiveXObject("Microsoft.XMLHTTP");
        if (!req) return;
	object = this;
	req.onreadystatechange = function() {object.xmlReqChange_(req);}
	req.open("GET", this.url, true);
	req.send();
    }
};
Preloader.xml.prototype.xmlReqChange_ = function(req) {
    // only if req shows "complete"
    if (req.readyState == 4) {
        // only if "OK"
        if (req.status == 200) {
	    this.xml = new PhotoXMLParser(req.responseXML.documentElement);
            this.done(true);
        } else {
	    this.xml = new PhotoXMLParser(null);
	    this.done(false);
        }
    }
};
Preloader.xml.prototype.done = function(success) {
    this.success = true;
    this.loaded  = true;
    this.preloader.done(this);
};
Preloader.xml.prototype.isDone = function() {
    return this.loaded ? true : false;
};
Preloader.xml.prototype.result = function() {
    return this.xml;
};



/**************************************************************/
/*                    Photo display functions                 */
/**************************************************************/

function PhotoDisplay(obj) {
    if (!obj) this.init();
    else this.clone(obj);
}


PhotoDisplay.prototype.init = function() {
    this.container    = document.getElementById("slideshowContainer");
    this.photo        = document.getElementById("photo");
    this.photoAnchor  = document.getElementById("photoAnchor");
    this.photoPadding = document.getElementById("photoPadding");
    this.title        = document.getElementById("title");
    this.descr        = document.getElementById("descr");
};

PhotoDisplay.prototype.clone = function(obj) {
    if (is_ie && !is_ie6up) {
	/* we need to perform a kludge to get IE5 to report the right
	 * position in offsetLeft and offsetTop */
	
	var tmp = obj.container.insertBefore
	       (createElement('img'), obj.photoPadding);
	tmp.style.height = "1px";
	tmp.style.width  = "1px";
	tmp.style.padding= "0px";
	tmp.src = slideshow.imageDir + "/../spacer.gif";
    }

    this.container = document.getElementsByTagName('body')[0].
                     appendChild(createElement('div'));
    this.container.display = "none";
    this.photoPadding = this.container.appendChild(createElement('div'));
    this.photoAnchor  = this.photoPadding.appendChild(createElement('a'));
    this.photo        = this.photoAnchor.appendChild(createElement('img'));
    this.title        = this.photoPadding.appendChild(createElement('div'));
    this.descr        = this.container.appendChild(createElement('div'));

    if (is_ie && !is_ie6up) {
	/* duplicate the kludged element */
	
	var tmp = this.container.insertBefore
	       (createElement('img'), this.photoPadding);
	tmp.style.height = "1px";
	tmp.style.width  = "1px";
	tmp.style.padding= "0px";
	tmp.src = slideshow.imageDir + "/../spacer.gif";
    }

    this.container.id = this.container.className = "slideshowContainerEffect";
    this.photoPadding.id = this.photoPadding.className = "photoPaddingEffect";
    this.photo.id = this.photo.className = "photoEffect";
    this.title.id = this.title.className = "titleEffect";
    this.descr.id = this.descr.className = "descrEffect";

    this.photoAnchor.onclick = function() { return false; }

    this.container.style.position = "absolute";
    this.container.style.left = getPositionInDocument(obj.container, 'x')+'px';
    this.container.style.top  = getPositionInDocument(obj.container, 'y')+'px';
};


PhotoDisplay.prototype.update = function(xml) {
    /* set the photo source and dimensions */
    this.photo.src    = xml.main.src;
    this.photo.width  = xml.main.width;
    this.photo.height = xml.main.height;

    /* set the padding on the left */
    var p1 = slideshow.maxWidth - xml.main.width;
    var p2 = slideshow.maxHeight- xml.main.height;

    this.photoPadding.style.paddingLeft  = Math.floor(p1/2)+'px';
    this.photoPadding.style.paddingTop   = Math.floor(p2/2)+'px';
    this.photoPadding.style.paddingBottom= (p2-Math.floor(p2/2))+'px';

    /* set the title */
    var t;
    if (xml.date != null && xml.date  != "") {
	t = "<div class=\"date\">" + nbsp(xml.date) + "</div>";
    }
    if (xml.title != null && xml.title != "") {
	t = (t ? t + "<div>" + xml.title + "</div>" : xml.title);
    } else {
	t = (t ? t + "<div>&nbsp;</div>" : "&nbsp;");
    }
    this.title.innerHTML = t;
    this.title.style.width = (xml.main.width+PHOTO_BORDER_SIZE) + "px";

    /* set the descr */
    this.descr.innerHTML = xml.descr;
};

PhotoDisplay.prototype.getContainer = function() { return this.container; }



/**************************************************************/
/*                    Fade effect functions                   */
/**************************************************************/

var FADE_STEP = 10;
var FADE_DURATION = 500;

function FadeEffect(primary) {
    this.doingEffect = false;
    this.primaryDisplay = primary;
}


FadeEffect.prototype.start = function(xml, callback) {
    this.doingEffect = true;
    this.callback  = callback;
    if (!this.photoDisplay) {
	this.photoDisplay = new PhotoDisplay(this.primaryDisplay);
	this.container = this.photoDisplay.getContainer();
    }
    this.photoDisplay.update(xml);
    this.xml = xml;

    /* turn on the display of the fade object */
    setOpacity(this.container, 0);
    this.container.style.left = 
        getPositionInDocument(this.primaryDisplay.getContainer(), 'x')+'px';
    this.container.style.top  = 
        getPositionInDocument(this.primaryDisplay.getContainer(), 'y')+'px';
    this.container.style.display = '';
    this.container.style.visibility = 'visible';

    this.opacity = 0;
    this.timeoutId = setTimeout("slideshow.effect.timeout_()",
			       Math.floor(FADE_DURATION * FADE_STEP/100));
};


FadeEffect.prototype.timeout_ = function() {
    this.opacity += FADE_STEP;
    if (this.opacity > 100) this.opacity = 100;

    setOpacity(this.container, this.opacity);

    if (this.opacity >= 100) {
	this.opacity = null;
	this.timeoutId = null;
	this.end_(false, true);
    }
    else {
        this.timeoutId = setTimeout("slideshow.effect.timeout_()",
				    Math.floor(FADE_DURATION * FADE_STEP/100));
    }
};

FadeEffect.prototype.end_ = function(isAbort, bringPrimaryToFront) {
    if (bringPrimaryToFront) this.primaryDisplay.update(this.xml);
    this.xml = null;

    this.container.style.display = 'none';
    this.container.style.visibility = 'hidden';
    
    this.doingEffect = false;
    if (!isAbort && this.callback) eval(this.callback);
};


FadeEffect.prototype.abort = function(bringToFront) {
    if (!this.doingEffect) return;

    if (this.timeoutId) {
	clearTimeout(this.timeoutId);

	/* bring the faded object to full opacity */
	this.end_(true, bringToFront);
    }
};

FadeEffect.prototype.inProgress = function() {
    return this.doingEffect;
};




/**************************************************************/
/*                 Thumbnail effect functions                 */
/**************************************************************/

var TNAIL_EFFECT_STEP_TIME = 20;
var TNAIL_EFFECT_STEP = 4;

function ThumbnailEffect(slideshow) {
    this.doingEffect = false;
    this.slideshow = slideshow;
    ThumbnailEffect.effect = this;
}


ThumbnailEffect.prototype.createObjects = function() {
    this.container = document.getElementsByTagName('body')[0].
                     appendChild(createElement('div'));
    this.container.id = this.container.className = "tnailsEffect";

    this.tiny = new Array();
    var i;
    // we need twice as many images for the transition effect
    for (i=0; i < this.slideshow.tiny.length*2; i++) {
	var anchor = this.container.appendChild(createElement('a'));
	this.tiny[i] = anchor.appendChild(createElement('img'));
	this.tiny[i].id = "tinyEffect_" + i;
	this.tiny[i].className = "tiny";
	if (is_ie && !is_ie6up) {
	    /* IE5 has a bug where padding on inline elements is ignored
	     * unless the element also has a dimension set, so let's set
	     * the dimension here */
	    this.tiny[i].parentNode.style.width = TINY_WIDTH+TINY_EXTRA;
	}
    }

    this.container.style.display = "none";
    this.container.style.position = "absolute";
    this.container.style.left = getPositionInDocument(this.slideshow.tnails, 
						      'x')+'px';
    this.container.style.top  = getPositionInDocument(this.slideshow.tnails, 
						      'y')+'px';
};


ThumbnailEffect.prototype.initEffect = function(fromPhoto, toPhoto, preloader){
    var num = this.tiny.length/2;
    var left = Math.floor(num/2);
    var right = num - left - 1;
    var total;
    var preloadIndex=2;
    var spacer=null;

    if (toPhoto.index - left < 0 || 
	toPhoto.index + right >= this.slideshow.photos.length) {
	spacer = preloader.get(preloadIndex++).result();
    }

    var toStart = 0;
    var fromStart;
    var fromNum;
    var tinyIndex;
    if (fromPhoto.index < toPhoto.index) {
	var more = toPhoto.index - fromPhoto.index;
	if (more <= num) toStart = more;
	else toStart = num;
	fromStart = 0;
	fromNum = toStart;

	tinyIndex = 0
    } else {
	var more = fromPhoto.index - toPhoto.index;
	fromStart = num;
	fromNum = more <= num ? more : num;

	tinyIndex = num - fromNum;
    }

    var i=0;
    for (i=0; i < num; i++) {
	var ii = toStart + i;
	var index = toPhoto.index - left + i;
	var isBlank = index < 0 || index >=    this.slideshow.photos.length;
	var r = (isBlank ? spacer : preloader.get(preloadIndex++).result());
	this.setTinyPhoto(ii, r, isBlank, index == fromPhoto.index);
    }
    for (i=0; i < fromNum; i++) {
	var ii = fromStart + i;
	var ti = tinyIndex + i;
	var fi = fromPhoto.index - left + ti;
	
	this.setTinyPhoto(ii, this.slideshow.tiny[ti], 
			  fi < 0 || fi >= this.slideshow.photos.length, 
			  fi == fromPhoto.index);
    }

    this.totalThumbnails = num+fromNum;
    this.direction = (fromPhoto.index < toPhoto.index ? "left" : "right");
}

ThumbnailEffect.prototype.setTinyPhoto = function(index, preloadResult, 
						  isBlank, isSelected) 
{
    var obj = this.tiny[index];
    if (isBlank) {
	obj.className = "blank";
	obj.src = preloadResult.src;
	obj.width  = TINY_WIDTH;
	obj.height = TINY_HEIGHT;
	obj.parentNode.style.padding = "0px";
    }
    else {
	var w = preloadResult.width;
	var pl = Math.floor((TINY_WIDTH - w)/2);
	var pr = TINY_WIDTH - w - pl;

	obj.src    = preloadResult.src;
	obj.width  = preloadResult.width;
	obj.height = preloadResult.height;
	obj.className = "tinyEffect";
	obj.parentNode.style.padding = "0px "+pr+"px 0px "+pl+"px";
    }

    obj.parentNode.className = isSelected ? "selected" : "";
}


ThumbnailEffect.prototype.totalTravel = function() {
    return ((this.totalThumbnails - this.slideshow.tiny.length) * 
	    (TINY_WIDTH+TINY_EXTRA));
}

ThumbnailEffect.prototype.start = function(fromPhoto, toPhoto, preloader, 
					   callback) {
    this.doingEffect = true;
    this.callback  = callback;
    this.fromPhoto = fromPhoto;
    this.toPhoto   = toPhoto;
    if (!this.container) {
	this.createObjects();
    }
    this.initEffect(fromPhoto, toPhoto, preloader);
    this.container.style.display = '';
    this.container.style.visibility = 'visible';

    this.container.style.top = getPositionInDocument(this.slideshow.tnails,
						     "y") + "px";
    this.counter = 0;
    this.travel  = this.totalTravel();
    this.adjust(this.travel);
    this.timeoutId = setTimeout("ThumbnailEffect.effect.timeout_()",
				TNAIL_EFFECT_STEP_TIME);
};


ThumbnailEffect.prototype.adjust = function(travel) {
    var width = this.slideshow.tiny.length * (TINY_WIDTH+TINY_EXTRA);

    if (this.direction == "left") {
	position = travel - this.totalTravel();
    } else {
	position = -travel;
    }
    this.container.style.left = (getPositionInDocument(this.slideshow.tnails,
						       "x") + position) + "px";
    this.container.style.clip = "rect(0px " + (-position + width) + "px "+
        this.slideshow.tnails.offsetHeight + "px "+ (-position) + "px)";
}


ThumbnailEffect.prototype.timeout_ = function() {
    this.counter++;
    var t = this.travel/TNAIL_EFFECT_STEP;
    if (t < 1) t = 1;
    var travel = Math.floor(this.travel - t);
    if (travel <= 0) {
	this.end_(false, true);
    } else {
	this.travel = travel;
	this.adjust(travel);
	this.timeoutId = setTimeout("ThumbnailEffect.effect.timeout_()",
				    TNAIL_EFFECT_STEP_TIME);
    }
};

ThumbnailEffect.prototype.end_ = function(isAbort, updatePrimary) {
    if (updatePrimary) this.slideshow.updateThumbnails(this.toPhoto);

    this.container.style.display = 'none';
    this.doingEffect = false;
    this.timeoutId = null;

    if (!isAbort && this.callback) eval(this.callback);
};


ThumbnailEffect.prototype.abort = function(updatePrimary) {
    if (!this.doingEffect) return;

    if (this.timeoutId) {
	clearTimeout(this.timeoutId);
	this.end_(true, updatePrimary);
    }
};

ThumbnailEffect.prototype.inProgress = function() {
    return this.doingEffect;
};



/**************************************************************/
/*                    Both effect functions                   */
/**************************************************************/

function BothEffects(effect, tnailEffect) {
    this.doingEffect = false;
    this.effect = effect;
    this.tnailEffect = tnailEffect;
}


BothEffects.prototype.start = function(fromPhoto,toPhoto,preloader,callback) {
    this.doingEffect = true;
    this.eDone = this.tDone = false;
    this.callback = callback;
    this.tnailEffect.start(fromPhoto, toPhoto, preloader, 
			   "slideshow.bothEffects.effectDone()");
    this.effect.start(preloader.get(1).result(), 
		      "slideshow.bothEffects.tnailEffectDone()");
}


BothEffects.prototype.effectDone = function() {
    this.eDone = true;
    if (this.tDone) this.end_();
}
BothEffects.prototype.tnailEffectDone = function() {
    this.tDone = true;
    if (this.eDone) this.end_();
}

BothEffects.prototype.end_ = function() {
    this.doingEffect = false;
    if (this.callback) eval(this.callback);
};


BothEffects.prototype.abort = function() {
    if (!this.doingEffect) return;
    if (!this.eDone) this.effect.abort(true);
    if (!this.tDone) this.tnailEffect.abort(true);
}

BothEffects.prototype.inProgress = function() {
    return this.doingEffect;
};






/**************************************************************/
/*                    Photo functions                         */
/**************************************************************/

Photo.prototype.xmlURL = function() {
    var dot = this.filename.lastIndexOf(".");
    return this.filename.substring(0, dot) + ".xml";
};
Photo.prototype.imgURL = function() {
    return "../images/"+this.filename;
};
Photo.prototype.pageURL = function() {
    var dot = this.filename.lastIndexOf(".");
    return this.filename.substring(0, dot) + ".html";
};
Photo.prototype.tinyURL = function() {
    var dot = this.filename.lastIndexOf(".");
    return "../images/"+this.filename.substring(0, dot) + "-tiny.gif";
};

Photo.prototype.preload = function() {
    var preloader = new Preloader();
    preloader.setData(this);
    preloader.add(new Preloader.image(this.imgURL()));
    preloader.add(new Preloader.xml  (this.xmlURL()));

    var left  = Math.floor(slideshow.tiny.length/2);
    var right = slideshow.tiny.length - left - 1;
    if (this.index - left < 0 || this.index+right >= slideshow.photos.length) {
	preloader.add(new Preloader.image(slideshow.imageDir+"/../spacer.gif"));
    }
    for (i=this.index - left; i < this.index; i++) {
	if (i < 0) continue;
	preloader.add(new Preloader.image(slideshow.photos[i].tinyURL()));
    }
    preloader.add(new Preloader.image(this.tinyURL()));
    for (i=this.index + 1; i <= this.index+right; i++) {
	if (i >= slideshow.photos.length) continue;
	preloader.add(new Preloader.image(slideshow.photos[i].tinyURL()));
    }

    preloader.start();
    return preloader;
}


/**************************************************************/
/*                    Miscellaneous functions                 */
/**************************************************************/

// convert all characters to lowercase to simplify testing
var agt=navigator.userAgent.toLowerCase();
// *** BROWSER VERSION ***
// Note: On IE5, these return 4, so use is_ie5up to detect IE5.
var is_major = parseInt(navigator.appVersion);
var is_minor = parseFloat(navigator.appVersion);

var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
var is_ie3  = (is_ie && (is_major < 4));
var is_ie4  = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) );
var is_ie4up= (is_ie && (is_major >= 4));
var is_ie5  = (is_ie && (is_major == 4) && (agt.indexOf("msie 5.0")!=-1) );
var is_ie5_5= (is_ie && (is_major == 4) && (agt.indexOf("msie 5.5") !=-1));
var is_ie5up= (is_ie && !is_ie3 && !is_ie4);
var is_ie5_5up=(is_ie && !is_ie3 && !is_ie4 && !is_ie5);
var is_ie6    = (is_ie && (is_major == 4) && (agt.indexOf("msie 6.")!=-1));
var is_ie6up  = (is_ie && !is_ie3 && !is_ie4 && !is_ie5 && !is_ie5_5);

function widthNeedsAdjustment() {
    return (is_ie && !is_ie6up);
}


function URLParameters(url) {
    var t = url.split("?");
    if (t.length < 2) return;

    var params = t[1].split("&");
    var i;
    for (i=0; i < params.length; i++) {
	var kv = params[i].split("=");
	if (kv.length > 1) this[kv[0]] = kv[1];
	else this[kv[0]] = "";
    }
}

function createElement(type) {
    return ((typeof document.createElementNS != 'undefined') ? 
	    document.createElementNS('http://www.w3.org/1999/xhtml', type) : 
	    document.createElement(type));
}

function nbsp(s) {
    var regex = / /g;
    return s.replace(regex, "&nbsp;");
}
