//JAVASCRIPT OPACITY AND FADING IMAGE GALLERY CLASSES
// @author Alec Hill

// NB - must include Base.js

/*
// must include placeholder class in stylesheet

.placeholder {
  position:absolute;
  top:0px;
  left:0px;
}

*/

/*****************************************************************************************/
//Opacity class

// required arguments:
//						el - a string id or object reference to element to be made into opacity object
//						initialOpacity - 0 to 100 value, initial opacity to set object to
// optional arguments:
//						object with optional properties:
//										steps - number of steps in fade
//										interval - time of interval in fade (ms)
									

var Opacity = ah.Class.extend({}, {
	
	initialize: function(el,initialOpacity,options){
		this.el	= this.getElement(el); //string id or object reference to element to be Opacity - required
		this.setOptions(options); //sets defaults then optional properties
		this.setOpacity(initialOpacity);//sets to initial opacity - initialOpacity required
	},
	
	// sets the defualts and then overides them if they are properties of the options object
	setOptions: function(options){
		this.options = {
						steps: 20, //default steps in fade
						interval: 30 //defualt interval in fade
						};
		ah.extend(this.options, options || {});
	},
	
	//returns a reference to an element when passed string id, or reference, defualting to the opacity object element itself if no argument passed
	getElement: function(el){
		var element = this.el;
		if(el) element = el;
		if(el && typeof el == 'string') element = document.getElementById(el);
		return element;
	},
	
	//gets the opacity of an element, (el not required - defualts to this)
	getOpacity: function(el){
		var element = this.getElement(el);
		if(element.style.opacity){
			var currentOpacity = Math.round(element.style.opacity*100);
		}else if(element.style.MozOpacity){
			var currentOpacity = Math.round(element.style.MozOpacity*100);
		}else if(element.style.KhtmlOpacity){
			var currentOpacity = Math.round(element.style.KhtmlOpacity*100);
		}else if(element.filters){
			currentOpacity = element.filters.alpha.opacity;
		}
		return currentOpacity;
	},
	
	//set the opacity of an element, (el not required - defualts to this)
	setOpacity: function(opacity,el){
		var element = this.getElement(el);
		element.style.opacity = (opacity / 100);
		element.style.MozOpacity = (opacity / 100);
		element.style.KhtmlOpacity = (opacity / 100);
		element.style.filter = "alpha(opacity=" + opacity + ")";
	},

	//calculates the ease in and out
	easeInOut: function(opacStart, opacEnd, totalSteps, currentStep, degree) {
		var delta = opacEnd - opacStart; 
		var step = opacStart+(Math.pow(((1 / totalSteps) * currentStep), degree) * delta); 
		return Math.ceil(step) 
	},
	
	//Changes the opacity at set intervals.
	changeOpacity: function(opacStart, opacEnd, totalSteps, intervals, degree, element){
		if(element.opacChange) window.clearInterval(element.opacChange);
		var currentStep = 0;
		element.opacChange = window.setInterval(
			function(){
				element.currentOpacity = this.easeInOut(opacStart, opacEnd, totalSteps, currentStep, degree);
				this.setOpacity(element.currentOpacity, element);
				currentStep++;
				if(currentStep > totalSteps) window.clearInterval(element.opacChange);
			}.bind(this)
			,intervals);
	},
	
	//fades element to a specified opacity, (el not required - defualts to this)
	fadeTo: function(to,el){	
		var element = this.getElement(el);
		var currentOpacity = this.getOpacity(element);
		this.changeOpacity(currentOpacity,to,this.options.steps,this.options.interval,1.5, element);			 
	},	
	
	//Fades element in to 100% opacity, (el not required - defualts to this)
	fadeIn: function(el) { 
		var element = this.getElement(el);
		this.fadeTo(100,element);
	},
	
	//Fades element out to 0% opacity, (el not required - defualts to this)
	fadeOut: function(el) { 
		var element = this.getElement(el);
		this.fadeTo(0,element); 
	},
	
	//Determines if opacity closer to 0 or 100 and fades the opposite way, (el not required - defualts to this)
	toggle: function(el){
		var element = this.el;
		if(el) element = el;
		var state = Math.round(this.getOpacity(element)/100);
		if(state == 0){
			this.fadeIn(element);
		}else if(state == 1){
			this.fadeOut(element);
		}
	}
	
});

/*****************************************************************************************/
// FadeGallery class

// required arguments:
//						srcArray - an array of the source paths to images
//						altArray - an array of alt text for images
//						img_el - the string id or object reference to of the div element holding main image

// optional arguments:
//						object with optional properties:
//										steps - number of steps in fade
//										interval - time of interval in fade (ms)
//										duration - time duration of main image rotation (ms)
//										li_el - string id or object reference to li element holding thumbnails 


var FadeGallery = ah.Class.extend(Opacity, {
	
	initialize: function(srcArray,altArray,img_el,options){
		this.srcArray = srcArray; //an array of image source strings - required
		this.altArray = altArray; //an array of alt text strings - required
		this.img_el = this.getElement(img_el); //string id or object reference to div element holding main image - required
		this.imageArray = new Array(); //initialized for holding preloaded image objects
		this.delay; //initialized for holding the timeout for rotating images
		this.setOptions(options); //sets defaults then optional properties
		this.duration = 4000; //default image duration
		if(this.options.duration) this.duration = this.options.duration; //if duration option is set overide defualt duration	
		this.current = (this.options.random == true) ? this.randomNumber() : 0; //if random option is set to true,create a random start number  
		//calls to set up functions
		
		this.preload();
		this.changeImage();
		if(this.options.li_el) this.prepareThumbLinks();
	},

	randomNumber: function(){
		return Math.round((this.srcArray.length-1) * Math.random()); 
	},

	preload: function(){
		for(var i=0; i<this.srcArray.length; i++){
		  	var imageObj = new Image();
			imageObj.src=this.srcArray[i];
			this.imageArray.push(imageObj);
		}
	}, 
	
	insertAfter: function(newElement,targetElement){
		var parent = targetElement.parentNode;
		if(parent.lastChild == targetElement){
			parent.appendChild(newElement);
		}else{
			parent.insertBefore(newElement,targetElement.nextSibling);
		}
	},

	prepareThumbLinks: function(){	  
		var imageList = this.getElement(this.options.li_el);
		links = imageList.getElementsByTagName("img");
		for(var i=0; i<links.length; i++){
			links[i].imageValue=i;
			links[i].onclick = function(e){
				if (!e) var e = window.event;
				if (e.target){ 
					var targ = e.target;
				}else if(e.srcElement){ 
					var targ = e.srcElement;
				}
				if (targ.nodeType == 3) targ = targ.parentNode;
				var value = targ.imageValue;
				this.changeImage(value);
				return false;
			}.bind(this);
			links[i].onkeypress = links[i].onclick;
		}
	},
	
	changeImage: function(value){
		if(undefined != this.delay) clearTimeout(this.delay);
		this.addImage(value);
	},
	
	addImage: function(user_selection){
		if(undefined != user_selection) this.current = user_selection;
		if(this.img_el.childNodes.length > 1) this.img_el.removeChild(this.img_el.firstChild);
		var lastElement = this.img_el.lastChild;
		var newElement = document.createElement("img"); 
		newElement.id = "image_" + this.current;
		newElement.src = this.srcArray[this.current];
		newElement.alt = this.altArray[this.current];
		newElement.className = "placeholder";
		this.setOpacity(0,newElement);
		this.insertAfter(newElement,lastElement);
		this.fadeIn(newElement);		
		if(this.current == this.srcArray.length-1){
			this.current = 0;
		}else{
			this.current++;
		}	
		if(user_selection){
			this.delay = setTimeout(function(){this.changeImage()}.bind(this),this.duration *2);
		}else{
			this.delay = setTimeout(function(){this.changeImage()}.bind(this),this.duration);
		}
	}
	
});

	
	
/*****************************************************************************************/
// FadeGalleryDescriptions class

//extends FadeGallery - takes a further required argument for the string id /object reference to element that will show description in. this description is taken from the alt text array. In this alt text a '-' will be replaced by a <br /> to suit my needs for this project. 

//changeDescription() method added to do the description
//initialize() method overwritten to reflect new argument
//changeImage() method overwritten to add a call to changeDescription()

var FadeGalleryDescriptions = ah.Class.extend(FadeGallery, {
	
	initialize: function(srcArray,altArray,img_el,desc_el,options){
		this.srcArray = srcArray; //an array of image source strings - required
		this.altArray = altArray; //an array of alt text strings - required
		this.img_el = this.getElement(img_el); //string id or object reference to div element holding main image - required
		this.desc_el = this.getElement(desc_el); //string id or object reference to element you want description text displayed in		
		this.imageArray = new Array(); //initialized for holding preloaded image objects
		this.delay; //initialized for holding the timeout for rotating images
		this.current = this.randomNumber(); //the current image number - set start heren
		this.setOptions(options); //sets defaults then optional properties
		this.duration = 4000; //default image duration
		if(this.options.duration) this.duration = this.options.duration;  //if duration option is set overide defualt duration		
		//calls to set up functions
		this.preload();
		this.changeImage();
		if(this.options.li_el) this.prepareThumbLinks();	
	},
	
	changeImage: function(value){
		if(undefined != this.delay) clearTimeout(this.delay);
		this.changeDescription(value);
		this.addImage(value);
	},
	
	changeDescription: function(value){
		var number = this.current;
		if(value) number = value;
		var text = this.altArray[number];
		var separatedtext = text.split('-');
		if(this.desc_el.firstChild) this.desc_el.removeChild(this.desc_el.firstChild);
		var nodes = new Array();
		for(var i=0;i<separatedtext.length;i++){
			var node = document.createTextNode(separatedtext[i]);
			nodes.push(node);
			if(i != text.length-1){
				var br = document.createElement('br');
				nodes.push(br);
			}
		}
		var span = document.createElement('span');
		for(var i=0;i<nodes.length;i++){
			span.appendChild(nodes[i]);
		}
		span.id = 'thespan';
		span.style.display = 'none';
		this.desc_el.appendChild(span);
		var thespan = new Opacity('thespan',0,{steps: 30});
		var spanel = document.getElementById('thespan');
		spanel.style.display = 'block';
		thespan.fadeIn();
	}
	
});





/*******************************************************************************/


//Generic onload event handler.
function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			oldonload();
			func();
		}
	}
}
/***************************************************************************************/	
// set up when page has loaded

/*
function setUp(){
	var srcArr = new Array('images/gallery/pic_0.jpg','images/gallery/pic_1.jpg','images/gallery/pic_2.jpg','images/gallery/pic_3.jpg','images/gallery/pic_4.jpg','images/gallery/pic_5.jpg','images/gallery/pic_6.jpg','images/gallery/pic_7.jpg','images/gallery/pic_8.jpg');
	var altArr = new Array('A close-up of some roses','Miniature roses','White flowers','Oriental vase','Pink flowers','More white flowers','More roses','Lavender','Pink flowers');
	var gal = new FadeGallery(srcArr,altArr,"dtimg",{li_el: "dtimglist"});	
}
addLoadEvent(setUp);
*/
