/**
 *	SlideFlow 1.0
 *
 *	This code is based on Michael L. Perrys Cover flow in Javascript.
 *	For he wrote that "You can take this code and use it as your own" [1]
 *
 *	[1] http://www.adventuresinsoftware.com/blog/?p=104#comment-1981
 */

/* Paramètres visuels */
/* 
	Nombre d'images affichage max. autour de l'image courante.
 */
var depth 			= 3;
/*
	Mode de répartition des images sur l'axe horizontal.
	Valeurs possibles : 
		line 	répartition linéaire (par défaut) 
		arctan 	répartition arc-tangentielle
	Paramètres connexes :
		x_max	amplitude horizontale max, en % de la largeur de la plus grande des images
		x_coef	coefficient de répartition (son influence dépend du mode)
*/
var x_function  	= 'arctan';
var x_max			= 180 / 100;
var x_coef			= 1;
/*
	Mode de répartition des images sur l'axe vertical.
	Valeurs possibles :
		circle	répartition circulaire (par défaut) 
		arctan	répartition parabolique
	Paramètres connexes :
		y_coef	coefficient de répartition (son influence dépend du mode choisi)
*/
var y_function  	= 'parabol';
var y_coef			= 0.5;
/* 
	Profil de transformation de la taille et de la transparence des images périphériques à l'image courante.
	Valeurs possibles : 
		line 	transformation linéaire (par défaut) 
		gauss 	transformation suivant une courbe de gauss
		agnesi	transformation suivant une courbe d'agnesi (profil similaire à gauss mais moins brutale pour les images éloignées)
		flat	transformation constante
	Paramètres connexes :
		r_coef		coefficient de transformation de la taille (son influence dépend du profil choisi)
		a_coef		coefficient de transformation de la transparence (son influence dépend du profil choisi)
		layer_mode	mode de transparence choisi
						true 	applique la transparence sur une couche superposée à l'image (div)
						false 	applique la transparence directement sur l'image (img)
		
		
*/	
var r_function	= 'agnesi';
var r_coef 		= 1;
var a_function	= 'agnesi';
var a_coef 		= 0.8;
var layer_mode  = true;

/*	Paramètres d'animation */
/*
	Période d'affichage des étapes (en millisecondes).
	Une courte période (< 50) rend l'animation plus fluide et rapide, mais consomme plus de ressources CPU.
*/
var period		= 50;
/*
	Tolérance pour la finalisation de l'animation.
	Une petite tolérance (< 10 / 100) rend l'animation plus fluide, mais augmente le nombre d'étapes pour finaliser l'animation.
	Une grande tolérance (> 10 / 100) provoque un effet "magnétique".
*/
var closure		= 5 / 1000;
var dichotomie	= 5;

/* Autres paramètres */

var cursor_images 	= 'pointer';
var cursor_slider 	= 'default';

/* Identifiants des div utilisés par le coverflow */

var DIV_IMAGE_FLOW 	= 'imageflow';
var DIV_LOADING 	= 'loading';
var DIV_IMAGES 		= 'images';
var DIV_CAPTION 	= 'captions';
var DIV_SCROLLBAR 	= 'scrollbar';
var DIV_SLIDER 		= 'slider';

/* Variables globales */

var current_id = 0;
var target_id = 0;
var max_id = 0;
var images_index = new Array();
var layers_index = new Array();
var	image_max_width = 0;
var image_max_height = 0;
var	images_width = 0;
var images_height = 0;
var slider = null;
var slider_step = 0;
var running = false;
var dragging = false;

/* Fonctions utilisées pour décaler le référentiel dans le centre inférieur de DIV_IMAGES */

function x2left(x) {
	return Math.round(x + images_width / 2);
}

function y2top(y) {
	return Math.round(images_height - y);
}

/* Transformations mathématiques de base */

function line(x, coef, yo, xo) {
	return coef * (x - xo) + yo;
}

function bline(x, coef, yo, xo) {
	return x <= xo ? line(x, coef, yo, xo) : line(x, -coef, yo, xo);
}

function parabol(x, coef, yo, xo) {
	return coef * Math.pow((x - xo), 2) + yo;
}

function circle(x, coef, yo, xo, amp) {
	x = (x - xo) > amp ? x = amp + xo : x;
	return - coef * Math.sqrt(Math.pow(amp, 2) - Math.pow((x - xo), 2)) + yo;
}

function arctan(x, coef, yo, xo, amp) {
	return amp * Math.atan(coef * (x - xo)) + yo;
}

function gauss(x, coef, yo, xo, amp) {
	return amp * Math.exp(- Math.pow((x - xo), 2) / (2 * Math.pow(coef, 2))) + yo;
}

function agnesi(x, coef, yo, xo, amp) {
	return amp * Math.pow(amp, 3) / (Math.pow((x - xo) / coef, 2) + Math.pow(amp, 2)) + yo;
}

/* Transformations des paramètres de l'image */

function fx(i, offset) {
	if (x_function == 'arctan') {
		return arctan(i, x_coef, 0, offset, x_max * image_max_width / Math.PI);
	}
	return line(i, x_coef * image_max_width, 0, offset);
}

function fy(x) {
	if (y_function == 'parabol') {
		return parabol(x, y_coef / 1000, 0, 0);
	}
	var r = x_max * image_max_width / 2;
	return circle(x, y_coef, y_coef * r, 0, r);
}

function fz(i, offset) {
	return bline(i, 2, max_id, offset);
}

function fr(i, offset) {
	if (r_function == 'flat') {
		return r_coef;
	}
	if (r_function == 'gauss') {
		return gauss(i, r_coef, 0, offset, 1);
	}
	if (r_function == 'agnesi') {
		return agnesi(i, r_coef, 0, offset, 1);
	}
	var r = bline(i, r_coef, 1, offset); 
	return (r < 0 ? 0 : r);
}

function fa(i, offset) {
	if (a_function == 'flat') {
		return a_coef;
	}
	if (a_function == 'gauss') {
		return gauss(i, a_coef, 0, offset, 1);
	}
	if (a_function == 'agnesi') {
		return agnesi(i, a_coef, 0, offset, 1);
	}
	var a = bline(i, a_coef, 1, offset); 
	return (a < 0 ? 0 : a);
}

function testFunc(offset)
{
	var x = '', y = '', z = '', r = '', a = '';
	
	for (i = 0; i <= max_id; i++)
	{
		x += Math.round(fx(i, offset)) + ', ';
		y += Math.round(fy(fx(i, offset))) + ', ';
		z += fz(i, offset) + ', ';
		r += Math.round(1000 * fr(i, offset)) / 1000 + ', ';
		a += Math.round(1000 * fa(i, offset)) / 1000 + ', ';
	}
	
	alert('x: ' + x + '\r\ny: ' + y + '\r\nz: ' + z + '\r\nr: ' + r + '\r\na: ' + a);
}

/* Fonctions d'animation */

function next(start, stop, current) {
	return current + (stop - current) / dichotomie;
}

function step(value) {
	value = 1 * value;
	
	if (value == target_id) {
		running = false;
	}
	else {
		if (false) alert('step: target_id = ' + target_id + '; value = ' + value); /* debug */
		value = next(current_id, target_id, value);
		if (Math.abs(target_id - value) <= closure) { 
			value = target_id;
		}
		moveTo(value);
		window.setTimeout('step(' + value + ')', period);
		running = true;
	}
}

function glideTo(id)
{	
	id = 1 * id; /* 'id' est passé en string après un clic sur l'image (à creuser) */
	target_id = id;

	if (!running) {
		window.setTimeout('step(' + current_id + ')', period);
		running = true;
	}
	
	current_id = target_id;
	caption = div_images.childNodes.item(images_index[id]).getAttribute('alt');
	if (caption == '') caption = '&nbsp;';
	div_caption.innerHTML = caption;

	if (!dragging) 	{
		slider_x = id * slider_step;
		div_slider.style.left = slider_x + 'px';
	}
}

function moveTo(value) {
	for (var id = 0; id < max_id; id++)	{
		var image = div_images.childNodes.item(images_index[id]);
		var layer = div_images.childNodes.item(layers_index[id]);

		if (Math.abs(id - target_id) > depth)	{
			image.style.visibility = 'hidden';
			image.style.display = 'none';
			if (layer_mode) {
				layer.style.visibility = 'hidden';
				layer.style.display = 'none';
			}
		}
		else {
			var x = fx(id, value);
			var y = fy(x);
			var z = fz(id, Math.round(value));
			var r = fr(id, value);
			var w = Math.round(r * image.w);
			var h = Math.round(r * image.h);
			var a = (z == max_id ? 1 : fa(id, value));
		
			if (false) alert('moveTo/for: id = ' + id + '; x = ' + x + '; y = ' + y + '; z = ' + z + '; r = ' + r + '; w = ' + w + '; h = ' + h + '; a = ' + a); /* debug */

			var a_image = (layer_mode ? 1 : a);
			var a_layer = 1 - a;
						
			image.style.width = w + 'px';
			image.style.left = x2left(x - w / 2) + 'px';
			image.style.height = h + 'px';
			image.style.top = y2top(y + h) + 'px';
			image.style.zIndex = z;
			image.style.opacity = a_image;
			image.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=' + a_image * 100 + ')';
			image.style.display = 'block';
			image.style.visibility = 'visible';
			
			if (layer_mode) {
				layer.style.width = w + 'px';
				layer.style.left = x2left(x - w / 2) + 'px';
				layer.style.height = h + 'px';
				layer.style.top = y2top(y + h) + 'px';
				layer.style.zIndex = z + 1;
				layer.style.opacity = a_layer;
				layer.style.filter = 'progid:DXImageTransform.Microsoft.alpha(opacity=' + a_layer * 100 + ')';
				layer.style.display = 'block';
				layer.style.visibility = 'visible';
			}
		}
	}
}

/* Fonctions principales */

function init() {
	/* mémorisation des blocs principaux */

	div_imageFlow 	= document.getElementById(DIV_IMAGE_FLOW);
	div_images 		= document.getElementById(DIV_IMAGES);
	div_scrollbar	= document.getElementById(DIV_SCROLLBAR);
	div_slider 		= document.getElementById(DIV_SLIDER);
	div_caption 	= document.getElementById(DIV_CAPTION);

	/* propriétés du slider */

	div_slider.onmousedown = function () { startDrag(this); };
	div_slider.style.cursor = cursor_slider;

	/* propriétés des images */
	
	var img_id = 0; 
	var div_id = 0; 
	for (var index = 0; index < div_images.childNodes.length; index++)	{ 
		var element = div_images.childNodes.item(index);
		if (element.nodeType == 1) {
			if (/img/i.test(element.tagName)) {
				var image = element;
				images_index[img_id] = index;
				
				image.onclick = function() { glideTo(this.id); }
				image.onmouseover = function() { glideTo(this.id); }
				image.id = img_id;
				image.style.cursor = cursor_images;
				image.w = image.width;
				image.h = image.height;
				image_max_height = Math.max(image_max_height, image.h);
				image_max_width = Math.max(image_max_width, image.w);

				img_id++;
			}
			if (layer_mode) {
				if (/div/i.test(element.tagName)) {
					var layer = element;
					layers_index[div_id] = index;
					
					layer.onclick = function() { glideTo(this.id); }
					layer.onmouseover = function() { glideTo(this.id); }
					layer.id = div_id;
					layer.style.backgroundColor = '#2f2e2e';
					layer.style.cursor = cursor_images;

					div_id++;
				}
			}
		}
	}
	
	max_id = images_index.length;
	
	images_height = Math.round(1.1 * image_max_height);
	div_images.style.height = images_height + 'px';
}

function refresh(onload) {
	images_width 	= div_images.offsetWidth;
	images_height 	= div_images.offsetHeight;
	scrollbar_width = div_scrollbar.offsetWidth;
	slider_step = Math.floor(scrollbar_width / (max_id - 1))

	moveTo(current_id);
	glideTo(current_id);
}

/* Show/hide element functions */

function show(id) {
	var element = document.getElementById(id);
	element.style.visibility = 'visible';
}

function hide(id) {
	var element = document.getElementById(id);
	element.style.visibility = 'hidden';
	element.style.display = 'none';
}

/* Hide loading bar, show content and initialize mouse event listening after loading */

window.onload = function() {
	if(document.getElementById(DIV_IMAGE_FLOW))	{
		if (false) alert('Test du bloc de chargement.');
		init();
		hide(DIV_LOADING);
		refresh();
		show(DIV_IMAGES);
		show(DIV_SCROLLBAR);
		initMouseWheel();
		initMouseDrag();
	}
}

/* Refresh ImageFlow on window resize */

window.onresize = function() {
	if (document.getElementById(DIV_IMAGE_FLOW)) refresh();
}

/* Handle the wheel angle change (delta) of the mouse wheel */

function handle(delta) {
	var change = false;
	
	if (delta > 0)	{
		if (current_id > 0) {
			new_image_id = current_id - 1;
			change = true;
		}
	}
	else {
		if (current_id < max_id - 1) {
			new_image_id = current_id + 1;
			change = true;
		}
	}

	if (change) {
		glideTo(new_image_id);
	}
}

function wheel(event) {
	var delta = 0;
	if (!event) event = window.event;
	if (event.wheelDelta) {
		delta = event.wheelDelta / 120;
	}
	else if (event.detail) {
		delta = -event.detail / 3;
	}
	if (delta) handle(delta);
	if (event.preventDefault) event.preventDefault();
	event.returnValue = false;
}

function initMouseWheel() {
	if (window.addEventListener) div_imageFlow.addEventListener('DOMMouseScroll', wheel, false);
	div_imageFlow.onmousewheel = wheel;
}

function startDrag(element) {
	slider = element;
	init_slider_x = mouse_x - slider.offsetLeft;
}

function stopDrag() {
	slider = null;
	dragging = false;
}

function drag(event) {
	mouse_x = document.all ? window.event.clientX : event.pageX;

	if (slider != null) {
		dragging = true;
		slider_x = mouse_x - init_slider_x;
		if (false) div_caption.innerHTML = 'drag: mouse_x = ' +mouse_x + '; init_slider_x = ' + init_slider_x + '; slider_x = ' + slider_x
		
		if (slider_x < 0) slider_x = 0;
		if (slider_x > scrollbar_width) slider_x = scrollbar_width;

		slider.style.left = slider_x + "px";
		
		var new_image_id = Math.round(slider_x / slider_step);
		glideTo(new_image_id);
	}
}

function initMouseDrag() {
	document.onmousemove = drag;
	document.onmouseup = stopDrag;
}

function getKeyCode(event) {
	event = event || window.event;
	return event.keyCode;
}

document.onkeydown = function(event) {
	var charCode  = getKeyCode(event);
	switch (charCode) {
		/* > */
		case 39:
			handle(-1);
			break;
		
		/* < */
		case 37:
			handle(1);
			break;
	}
}