function implementsInterface() {
	var prototypeObj=new Object();
	for (var i=0; i<arguments.length; i++) {
		for (var method in arguments[i]) {
			prototypeObj[method]=arguments[i][method];
		}	
	}
	return prototypeObj;
}

//////////////////////////
// Interface Observable //
//////////////////////////

Observable = {
	addListener : function(listenerObj) {
		if (typeof this.listeners == 'undefined') {
			this.listeners=new Array();
		}
		for (var i=0; i<this.listeners.length; i++) {
			if (this.listeners[i]==listenerObj) return;
		}
		this.listeners[this.listeners.length]=listenerObj;
	},

	removeListener : function(listenerObj) {
		for (var i=0; i<this.listeners.length; i++) {
			if (this.listeners[i]==listenerObj) {
				this.listeners.splice(i,1);
				return;
			}
		}
	},
	
	notifyListeners : function (eventName,eventObj) {
		if (typeof this.listeners == 'undefined') return;
		for (var i=0; i< this.listeners.length; i++) {
			if (typeof this.listeners[i][eventName]!="undefined") {
				this.listeners[i][eventName](eventObj);
			}
		}
	}
}

//////////////////////
// Class IconButton //
//////////////////////

/* parameters: container is ul element */

function IconButton(container, iconFileUrlNormal, iconFileUrlRollover, iconText, onClickHandler) {
	this.container=container;
	this.buttonElement=null;
	this.iconImageElement=null;
	this.iconImage=new Image();
	this.iconImage.src=iconFileUrlNormal;
	this.iconImageRollover=new Image();
	this.iconImageRollover.src=iconFileUrlRollover;
	this.onMouseOverHandler=null;
	this.onMouseOutHandler=null;
	this.onClickHandler=null;
	this.create(iconText);
	this.initHandlers(onClickHandler);
}

IconButton.prototype.create=function (iconText) {
	this.buttonElement=document.createElement("LI");
	this.iconImageElement=document.createElement("IMG");
	this.iconImageElement.src=this.iconImage.src;
	this.buttonElement.appendChild(this.iconImageElement);
	this.buttonElement.appendChild(document.createTextNode(" "+iconText));
	this.container.appendChild(this.buttonElement);
}

IconButton.prototype.initHandlers=function (onClickHandler) {
	var obj=this;
	this.onMouseOverHandler=DOMEvent.addDomListener(this.buttonElement, "mouseover", function () {
		obj.iconImageElement.src=obj.iconImageRollover.src;
	});
	
	this.onMouseOutHandler=DOMEvent.addDomListener(this.buttonElement, "mouseout", function () {
		obj.iconImageElement.src=obj.iconImage.src;
	});
	
	this.onClickHandler=DOMEvent.addDomListener(this.buttonElement, "click", onClickHandler);
}

IconButton.prototype.show=function() {
	DHTMLApi.CSS.setProperties(this.buttonElement,{display: "block"});
}

IconButton.prototype.hide=function() {
	DHTMLApi.CSS.setProperties(this.buttonElement,{display: "none"});
}

IconButton.prototype.remove=function() {
	DOMEvent.removeListener(this.onMouseOverHandler);
	DOMEvent.removeListener(this.onMouseOutHandler);
	DOMEvent.removeListener(this.onClickHandler);
	this.onMouseOverHandler=null;
	this.onMouseOutHandler=null;
	this.onClickHandler=null;
	this.container.removeChild(this.buttonElement);	
}


///////////////////////
// Package Animation //
///////////////////////

Animation= new Object();

Animation.FRAME_RATE=50; // miliseconds

//////////////////////////
// Class Animation.Fade //
//////////////////////////

Animation.Fade=function (fadeObject, currentOpacityPercentage) {
	this.fadeObject=fadeObject;
	this.currentOpacityPercentage=currentOpacityPercentage;
	this.interval=null;
	this.targetOpacityPercentage=null;
	this.currentAnimationStep=null;
	this.numOfAnimationSteps=null;
	DHTMLApi.Visibility.setOpacity(this.fadeObject,Math.round(this.currentOpacityPercentage));
}

Animation.Fade.prototype=implementsInterface(Observable);

Animation.Fade.prototype.setFade=function (targetOpacityPercentage, numOfSteps) {
	this.stop();
	var animationObject=this;
	this.targetOpacityPercentage=targetOpacityPercentage;
	this.opacityStep=1.0*(targetOpacityPercentage-this.currentOpacityPercentage)/numOfSteps;
	this.numOfAnimationSteps=numOfSteps;
	this.currentAnimationStep=0;
	this.notifyListeners("onAnimationStart",null);
	this.animate();
	this.interval=window.setInterval(function() {animationObject.animate()},Animation.FRAME_RATE);
}

Animation.Fade.prototype.animate=function () {
	if (this.currentAnimationStep<this.numOfAnimationSteps) {
		this.currentOpacityPercentage+=this.opacityStep;
		DHTMLApi.Visibility.setOpacity(this.fadeObject,Math.round(this.currentOpacityPercentage));
		this.notifyListeners("onAnimationStep",this.currentAnimationStep);
		++this.currentAnimationStep;
		return this.numOfAnimationStep;
	} else {
		this.currentOpacityPercentage=this.targetOpacityPercentage;
		DHTMLApi.Visibility.setOpacity(this.fadeObject,Math.round(this.currentOpacityPercentage));
		this.notifyListeners("onAnimationStep",this.currentAnimationStep);
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
		this.numOfAnimationStep=null;
		return false;
	}
}

Animation.Fade.prototype.stop=function() {
	if (this.interval!==null) {
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
	}
}

/////////////////////////////////
// Class Animation.SmoothHMove //
/////////////////////////////////

Animation.SmoothHMove=function(movingObject, relativeToObject) {
	this.movingObject=movingObject;
	this.relativeToObject=relativeToObject;
	this.currentXPos=DHTMLApi.Position.getXPosInElement(movingObject,relativeToObject);
	this.targetXPos=null;
	this.interval=null;
	this.numOfAnimationStep=null;
}

Animation.SmoothHMove.prototype=implementsInterface(Observable);

Animation.SmoothHMove.prototype.setPosition=function (targetPosition) {
	if (this.interval!==null) {
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
	}
	var animationObject=this;
	this.targetXPos=targetPosition;
	this.numOfAnimationStep=0;
	this.notifyListeners("onAnimationStart",null);
	this.animate();
	this.interval=window.setInterval(function() {animationObject.animate()},Animation.FRAME_RATE);
}

Animation.SmoothHMove.prototype.animate=function () {
	var stepDistance=(this.targetXPos-this.currentXPos)/3;
	if (Math.abs(stepDistance)>0.3) {
		this.currentXPos+=stepDistance;
		DHTMLApi.Position.setXPos(this.movingObject, this.currentXPos, this.relativeToObject);
		++this.numOfAnimationStep;
		this.notifyListeners("onAnimationStep",this.numOfAnimationSteps);
		return this.numOfAnimationStep;
	} else {
		this.currentXPos=this.targetXPos;
		DHTMLApi.Position.setXPos(this.movingObject, this.currentXPos, this.relativeToObject);
		this.notifyListeners("onAnimationStep",++this.numOfAnimationSteps);
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
		this.numOfAnimationStep=null;
		return false;
	}
}

Animation.SmoothHMove.prototype.getAnimationStep=function () {
	return this.numOfAnimationStep;
}

/////////////////////////////////
// Class Animation.SmoothVMove //
/////////////////////////////////

Animation.SmoothVMove=function(movingObject, relativeToObject) {
	this.movingObject=movingObject;
	this.relativeToObject=relativeToObject;
	this.currentYPos=DHTMLApi.Position.getYPosInElement(movingObject,relativeToObject);
	this.targetYPos=null;
	this.interval=null;
	this.numOfAnimationStep=null;
}

Animation.SmoothVMove.prototype=implementsInterface(Observable);

Animation.SmoothVMove.prototype.setPosition=function (targetPosition) {
	if (this.interval!==null) {
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
	}
	var animationObject=this;
	this.targetYPos=targetPosition;
	this.numOfAnimationStep=0;
	this.notifyListeners("onAnimationStart",null);
	this.animate();
	this.interval=window.setInterval(function() {animationObject.animate()},Animation.FRAME_RATE);
}

Animation.SmoothVMove.prototype.animate=function () {
	var stepDistance=(this.targetYPos-this.currentYPos)/3;
	if (Math.abs(stepDistance)>0.3) {
		this.currentYPos+=stepDistance;
		DHTMLApi.Position.setYPos(this.movingObject, this.currentYPos, this.relativeToObject);
		++this.numOfAnimationStep;
		this.notifyListeners("onAnimationStep",this.numOfAnimationSteps);
		return this.numOfAnimationStep;
	} else {
		this.currentYPos=this.targetYPos;
		DHTMLApi.Position.setYPos(this.movingObject, this.currentYPos, this.relativeToObject);
		this.notifyListeners("onAnimationStep",++this.numOfAnimationSteps);
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
		this.numOfAnimationStep=null;
		return false;
	}
}

Animation.SmoothVMove.prototype.getAnimationStep=function () {
	return this.numOfAnimationStep;
}

//////////////////////////////////
// Class Animation.SmoothResize //
//////////////////////////////////

Animation.SmoothResize=function(resizingObject) {
	this.resizingObject=resizingObject;
	this.currentWidth=DHTMLApi.Size.getElementWidth(this.resizingObject);
	this.currentHeight=DHTMLApi.Size.getElementHeight(this.resizingObject);
	this.proportionsRatio=1.0*this.currentWidth/this.currentHeight;
	this.maintainProportions=true;
	this.targetWidth=null;
	this.targetHeight=null;
	this.interval=null;
	this.currentAnimationStep=null;
}

Animation.SmoothResize.prototype=implementsInterface(Observable);

Animation.SmoothResize.prototype.setSize=function (targetWidth,targetHeight,maintainProportions) {
	if (this.interval!==null) {
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
	}
	var animationObject=this;
	this.targetWidth=targetWidth;
	this.targetHeight=targetHeight;
	this.maintainProportions=maintainProportions;
	this.currentAnimationStep=0;
	this.notifyListeners("onAnimationStart",null);
	this.animate();
	this.interval=window.setInterval(function() {animationObject.animate()},Animation.FRAME_RATE);
}

Animation.SmoothResize.prototype.animate=function () {
	var stepWidth=1.0*(this.targetWidth-this.currentWidth)/1.5;
	var stepHeight=1.0*(this.targetHeight-this.currentHeight)/1.5;
	if (((Math.abs(stepWidth)>0.3 || Math.abs(stepHeight)>0.3) && !this.maintainProportions) || (this.maintainProportions && Math.abs(stepWidth)>0.3)) {
		if (this.maintainProportions) {
			this.currentWidth+=stepWidth;
			this.currentHeight=this.currentWidth/this.proportionsRatio;
		} else {
			this.currentWidth+=stepWidth;
			this.currentHeight+=stepHeight;			
		}		
		DHTMLApi.CSS.setProperties(this.resizingObject, {width: Math.round(this.currentWidth)+"px", height:Math.round(this.currentHeight)+"px"});
		++this.currentAnimationStep;
		this.notifyListeners("onAnimationStep",this.currentAnimationStep);
		return this.currentAnimationStep;
	} else {
		if (this.maintainProportions) {
			this.currentWidth=this.targetWidth;
			this.currentHeight=Math.round(this.currentWidth/this.proportionsRatio);
		} else {
			this.currentWidth=this.targetWidth;
			this.currentHeight=this.targetHeight;			
		}			
		DHTMLApi.CSS.setProperties(this.resizingObject, {width: this.currentWidth+"px", height:this.currentHeight+"px"});
		this.notifyListeners("onAnimationStep",++this.currentAnimationStep);
		window.clearInterval(this.interval);
		this.notifyListeners("onAnimationEnd",null);
		this.currentAnimationStep=null;
		return false;
	}
}


/////////////////////
// Class SlideShow //
/////////////////////

function SlideShow(imagesUrlArray, containerElement, slideDurationInSec, numOfTransitionIterations, numOfTransitionMixIterations) {
	this.container=containerElement;
	this.imagesUrlArray=imagesUrlArray;
	this.numOfImagesPreloaded=1;
	this.preloadIsOn=false;
	this.currentSlide=1;
	this.slideDuration=Math.round(slideDurationInSec*1000);
	this.numOfTransitionIterations=numOfTransitionIterations;
	this.playInterval=null;
	this.foregroundSlide=document.createElement("IMG");
	this.backgroundSlide=document.createElement("IMG");
	this.fadeOutAnimation=null;
	this.fadeInAnimation=null;
	this.slideShowIsPlaying=false;
	this.numOfStartFadeInIteration=numOfTransitionIterations-numOfTransitionMixIterations;
	if (this.numOfStartFadeInIteration<1) {
		this.numOfStartFadeInIteration=1;
	}
	if (this.numOfStartFadeInIteration>=this.numOfTransitionIterations) {
		this.numOfStartFadeInIteration=this.numOfTransitionIterations-1;
	}
	this.init();
}

SlideShow.prototype.init=function() {
	var obj=this;
	DHTMLApi.CSS.setProperties(this.container, {position:"relative", overflow:"hidden"});
	this.container.appendChild(this.foregroundSlide);
	this.container.appendChild(this.backgroundSlide);
	DHTMLApi.CSS.setProperties(this.foregroundSlide, {position:"absolute", zIndex:2});
	DHTMLApi.CSS.setProperties(this.backgroundSlide, {position:"absolute", zIndex:1});
	DHTMLApi.Visibility.hide(this.foregroundSlide);
	DHTMLApi.Visibility.hide(this.backgroundSlide);
	this.displaySlide(this.foregroundSlide,this.currentSlide, 100);
}

SlideShow.prototype.preload=function() {
	var obj=this;
	if (this.numOfImagesPreloaded>=this.imagesUrlArray.length) return; 
	this.preloadIsOn=true;
	var img=new Image();
	img.onload=function () {
		obj.numOfImagesPreloaded++;
		if (obj.preloadIsOn) {
			obj.preload();
		}
	}
	img.src=this.imagesUrlArray[this.numOfImagesPreloaded];
}

SlideShow.prototype.stopPreload=function() {
	this.preloadIsOn=false;
}

SlideShow.prototype.displaySlide=function(slide, slideNum, opacity) {
	var obj=this;
	var img=new Image();
	img.onload=function () {
		DHTMLApi.Visibility.show(slide);
		slide.setAttribute("src",obj.imagesUrlArray[slideNum-1]);
		obj.setOpacity(slide,1);
		obj.centerSlide(slide,this.width,this.height);
		obj.setOpacity(slide,opacity);
	}
	img.src=this.imagesUrlArray[slideNum-1];
}

SlideShow.prototype.centerSlide=function (slide,slideWidth,slideHeight) {
	var posX,posY;
	posX=Math.round((DHTMLApi.Size.getElementWidth(this.container)-slideWidth)/2);
	posY=Math.round((DHTMLApi.Size.getElementHeight(this.container)-slideHeight)/2);
	DHTMLApi.CSS.setProperties(slide, {position:"absolute", left: posX+"px", top: posY+"px"});
}

SlideShow.prototype.setOpacity=function(slide,opacity) {
	DHTMLApi.Visibility.setOpacity(slide,opacity);
}

SlideShow.prototype.display=function(slideNum) {
	this.stop();
	this.currentSlide=slideNum;
	this.displaySlide(this.foregroundSlide, this.currentSlide,100);
}

SlideShow.prototype.displayNextSlide=function() {
	this.stop();
	this.currentSlide=this.getNextSlide();
	this.displaySlide(this.foregroundSlide, this.currentSlide,100);
}

SlideShow.prototype.displayPreviousSlide=function() {
	this.stop();
	this.currentSlide=this.getPreviousSlide();
	this.displaySlide(this.foregroundSlide, this.currentSlide,100);
}

SlideShow.prototype.getNextSlide=function() {
	if (this.currentSlide==this.imagesUrlArray.length) {
		return 1;
	} else {
		return this.currentSlide+1;
	}
}

SlideShow.prototype.getPreviousSlide=function() {
	if (this.currentSlide==1) {
		return this.imagesUrlArray.length;
	} else {
		return this.currentSlide-1;
	}
}

SlideShow.prototype.play=function () {
	var obj=this;
	if (this.slideShowIsPlaying) return;
	this.preload();
	this.slideShowIsPlaying=true;
	this.playInterval=window.setInterval(function () {obj.playTransition();}, this.slideDuration);
}

SlideShow.prototype.playTransition=function() {
	var fadeOutListener, fadeInListener, fadeOutChannel, fadeInChannel;
	if (this.currentSlide>=this.numOfImagesPreloaded && this.numOfImagesPreloaded!=this.imagesUrlArray.length) return;
	var obj=this;
	this.fadeInAnimation=null;
	fadeOutListener=new Object();
	fadeOutListener.onAnimationEnd=function () {
		obj.fadeOutAnimation.removeListener(this);
	}
	fadeOutListener.onAnimationStart=function() {
	}
	
	fadeOutListener.onAnimationStep=function (eventObj) {
		if (eventObj==obj.numOfStartFadeInIteration) {
			obj.currentSlide=obj.getNextSlide();
			obj.displaySlide(obj.foregroundSlide, obj.currentSlide, 1);
			obj.fadeInAnimation=new Animation.Fade(obj.foregroundSlide, 1);
			obj.fadeInAnimation.setFade(100,obj.numOfTransitionIterations);
		}
	}
	
	this.displaySlide(this.backgroundSlide,this.currentSlide,100);
	this.fadeOutAnimation=new Animation.Fade(this.backgroundSlide, 100);
	this.fadeOutAnimation.addListener(fadeOutListener);
	this.fadeOutAnimation.setFade(0,this.numOfTransitionIterations);
}

SlideShow.prototype.stop=function() {
	this.stopPreload();
	if (this.fadeOutAnimation!=null) this.fadeOutAnimation.stop();
	if (this.fadeInAnimation!=null) this.fadeInAnimation.stop();
	this.slideShowIsPlaying=false;
	DHTMLApi.Visibility.hide(this.backgroundSlide);
	this.displaySlide(this.foregroundSlide, this.currentSlide, 100);
	
	window.clearInterval(this.playInterval);
}

///////////////////////////////////
// Class PhotoGalleryThumbScroll //
///////////////////////////////////

/* parameters:
GUIelements= {photoContainer: , thumbScroll: , thumbContainer: , nextPhotoSetButton: , previousPhotoSetButton: , nextPhotoButton: , previousPhotoButton:}
*/

function PhotoGalleryThumbScroll(GUIelements,imagesUrlArray,thumbsUrlArray,thumbHeight,numOfThumbsPerPage) {
	this.photoContainerDiv=GUIelements.photoContainer;
	this.thumbContainer=GUIelements.thumbContainer;
	this.scrollContainer=GUIelements.thumbScroll;
	this.numOfThumbsPerPage=numOfThumbsPerPage;
	this.thumbForwardButton=GUIelements.nextPhotoSetButton;
	this.thumbBackwardButton=GUIelements.previousPhotoSetButton;
	this.picForwardButton=GUIelements.nextPhotoButton;
	this.picBackwardButton=GUIelements.previousPhotoButton;
	this.thumbnailHeight=thumbHeight;
	this.thumbnailUrl=thumbsUrlArray;
	this.thumbnailPictureObjects= new Array();
	this.currentThumbSet=null;
	this.numOfThumbSets=null;
	this.currentPhoto=null;
	this.numberOfPhotos=imagesUrlArray.length;
	this.thumbScroller=new Animation.SmoothVMove(this.thumbContainer, this.scrollContainer);
	this.slideShow=new SlideShow(imagesUrlArray, this.photoContainerDiv, 5, 25, 20);
}

PhotoGalleryThumbScroll.prototype.init= function () {
	var obj=this;
	this.initThumbs();
	
	DOMEvent.addDomListener(this.photoContainerDiv,"click", function (event) {
		if (obj.slideShow.slideShowIsPlaying) {
			obj.slideShow.stop();
			obj.currentPhoto=obj.slideShow.currentSlide;
			obj.setThumbSet(obj.getThumbSetNum(obj.currentPhoto));
		} else {
			obj.slideShow.play();
		}
	});
	
	DOMEvent.addDomListener(this.picForwardButton,"click", function (event) {
		obj.slideShow.displayNextSlide();
		obj.currentPhoto=obj.slideShow.currentSlide;
		obj.setThumbSet(obj.getThumbSetNum(obj.currentPhoto));
	});
	
	DOMEvent.addDomListener(this.picBackwardButton,"click", function (event) {
		obj.slideShow.displayPreviousSlide();
		obj.currentPhoto=obj.slideShow.currentSlide;
		obj.setThumbSet(obj.getThumbSetNum(obj.currentPhoto));
	});

	this.currentThumbSet=1;
	this.numOfThumbSets=Math.ceil(this.numberOfPhotos/this.numOfThumbsPerPage);
	this.currentPhoto=1;
}

PhotoGalleryThumbScroll.prototype.getThumbSetNum= function (photoNum) {
	return Math.ceil(photoNum/this.numOfThumbsPerPage); 
}

PhotoGalleryThumbScroll.prototype.getCurrentPhoto= function () {
	return this.currentPhoto; 
}

PhotoGalleryThumbScroll.prototype.calculateScrollPosition=function (targetThumbSet) {
	var numOfPhotosInLastSet;
	numOfPhotosInLastSet=this.numberOfPhotos % this.numOfThumbsPerPage;
	
	if (this.numOfThumbSets==targetThumbSet && this.numOfThumbSets>1 && numOfPhotosInLastSet!=0) {
		return -1*((targetThumbSet-2)*this.numOfThumbsPerPage+numOfPhotosInLastSet)*this.thumbnailHeight;
	}	else {
		return -1*(targetThumbSet-1)*this.thumbnailHeight*this.numOfThumbsPerPage;
	}
}

PhotoGalleryThumbScroll.prototype.setThumbSet=function (numOfThumbSet) {
	this.currentThumbSet=numOfThumbSet;
	this.thumbScroller.setPosition(this.calculateScrollPosition(numOfThumbSet));
}

PhotoGalleryThumbScroll.prototype.initThumbs= function () {
	var photoGalleryThumbScrollObj;
	photoGalleryThumbScrollObj=this;
	while (this.thumbContainer.hasChildNodes()) this.thumbContainer.removeChild(this.thumbContainer.firstChild);
	for (var i=0;i<this.numberOfPhotos;i++) {
		this.thumbnailPictureObjects[i]=document.createElement("IMG");
		this.thumbnailPictureObjects[i].num=i+1;
		this.thumbnailPictureObjects[i].setAttribute("src",this.thumbnailUrl[i]);
		this.thumbnailPictureObjects[i].style.cursor="pointer";
		this.thumbContainer.appendChild(this.thumbnailPictureObjects[i]);
		this.thumbnailPictureObjects[i].onclick=function () {
			photoGalleryThumbScrollObj.displayPhoto(this.num);
		}
	}
	this.thumbForwardButton.onclick= function () {
		if (photoGalleryThumbScrollObj.currentThumbSet!=photoGalleryThumbScrollObj.numOfThumbSets) {
			photoGalleryThumbScrollObj.setThumbSet(photoGalleryThumbScrollObj.currentThumbSet+1);
		} else {
			photoGalleryThumbScrollObj.setThumbSet(1);
		}
		
	}
	
	this.thumbBackwardButton.onclick= function () {
		if (photoGalleryThumbScrollObj.currentThumbSet>1) {
			photoGalleryThumbScrollObj.setThumbSet(photoGalleryThumbScrollObj.currentThumbSet-1);
		} else {
			photoGalleryThumbScrollObj.setThumbSet(photoGalleryThumbScrollObj.numOfThumbSets);
		}
	}
	
	this.thumbContainer.style.height=this.numberOfPhotos*this.thumbnailHeight+"px";
}

PhotoGalleryThumbScroll.prototype.displayPhoto=function(photoNum) {
	this.slideShow.display(photoNum);
	this.currentPhoto=photoNum;
}