/* Declaration des variables globales */
var loadPageTime = new Date(),	// heure du debut du chargement de la page. Utilisee pour calculer le temps d'initialisation du code et le prendre en compte dans le temps d'affichage de la premiere image du diaporama si besoin
	fluidite_MIN = 42,			// 42 correspond en gros a 24 animations par seconde (1000 / 24 = 41.7)
	fluidite_MAX = 100,			// au-dela de 100, ca a l'air vraiment moins fluide
	fluiditeAnimation = 62,		// duree en ms entre deux pas de transparence
	HundredTimesFluidite = fluiditeAnimation * 100,
	backTransition = 'brusque',	/* regle dans quel etat sera la page lorsque l'utilisateur clique sur un lien avec preview puis revient avec 'Back'
								** avec la valeur 'fade', l'utilisateur verra la transition comme s'il avait simplement enleve sa souris du lien
								** avec une autre valeur, il reviendra directement sur le diaporama generique ou le div sans animation s'il n'y a pas de diaporama generique */
	// Regex pour verifier la validite d'un reglage de couleur CSS. Il n'est pas 100% fiable pour la notation rgb(...) mais elle est peu utilisee de toute facon
	CSSRegex = /^(?:#(?:[\da-f]{3}){1,2}|rgb\((?:\d[\d\.]*%?,){2}\d[\d\.]*%?\)|aqua|bl(?:ack|ue)|fuchsia|gr(?:ay|een)|lime|marron|navy|o(?:live|range)|purple|red|silver|t(eal|ransparent)|yellow)$/,
	trimRegex = /^\s+|\s+$/g,	// Regex pour enlever les blancs en debut et fin de chaine de caracteres (trim())
	interPool_ImgSRC = 'images/00.gif',				// URL d'une image de 1x1px 100% transparente utilisee pour les inter-pools d'images
	load_ImgSRC = 'images/LoadingImageSmall.gif',	// URL du temoin de chargement
	diapID = 0,					// compteur d'ID pour les diaporamas
	myPreLoader,				// objet charge de gerer les pre-chargements des images
	diaporamas = new Array(),	// liste des diaporamas de la page
	debugging = false,			// activer ou non la fenetre de debuggage
	myDebugger = null,			// objet charge de gerer l'affichage des messages de debuggage
	myMutex = null,				// objet permettant de temporiser les demarrages et arrets de diaporamas pour eventuellement annuler des ordres conflictuels rapproches
	CPUOverUsedFactor = 1.4,	// Si l'intervalle entre deux executions de setInterval depasse de CPUOverUsedFactor fois le temps prevu, le processeur est trop utilise
	capH1Elmnt = null,			// Reference a l'element H1 qui peut recevoir les legendes
	capH1InnerHTML = '',		// Contenu initial de l'element H1 qui peut recevoir les legendes

/* Valeurs d'animation par defaut, utilisees si une valeur du fichier de configuration est absente ou n'est pas valide */
	picMode_default = 'list',				// mode d'animation de diaporama (batch ou list)
	poolMode_default = 'list',				// mode d'animation de diaporama (batch, random ou list)
	trans_default = 0,						// transparence de depart du fadein ou de fin de fadeout
	width_default = 824,					// largeur de la zone animee
	height_default = 547,					// hauteur de la zone animee
	bgColor_default = 'white',				// couleur de fond des fadings
	fade_speed_def = 1000,					// vitesse d'apparition/disparition
	pictime_def = 3000,						// temps d'affichage entre fadein et fadeout en millisecondes
	firstPictime_def = 3000,				// temps d'affichage entre fadein et fadeout en millisecondes pour les premieres images d'un batch
	target_def = null,						// ID du div cible par defaut
	waitStart_def = 0,						// attente avant demarrage des diaporamas generiques
	interpool_Wait_default = 0,				// temps d'attente entre deux pools d'images d'un meme diaporama
	interpool_Bg_default = 'transparent',	// couleur du div de fond lors de la transition entre deux pools
	captionText_Col_def = 'white',			// couleur du texte des legendes
	captionBg_Col_def = 'black',			// couleur du fond des legendes
	captionBg_Transp_def = 100,				// transparence des legendes
	captionTop_def = '85%',					// position top des legendes (en % ou en px)
	preloadBg_def = 'white',				// couleur du fond du temoin de chargement
	preloadBgInit_def = 'transparent',		// couleur du fond du temoin de chargement lorsqu'aucune image n'est encore affichee dans le diaporama
	preloadBgTransp_def = 50,				// transparence du fond du temoin de chargement lorsqu'aucune image n'est encore affichee dans le diaporama
	preloadBgInitTransp_def = 100,			// transparence du fond du temoin de chargement lorsqu'aucune image n'est encore affichee dans le diaporama

/* Objets par defaut, bases sur les valeurs par defaut ci-dessus + valeurs par defaut du fichier config */
	diap_FadeIn = null,	// FadeIn par default pour les diaporamas
	diap_FadeOut = null,// FadeOut par default pour les diaporamas
	img_FadeIn = null,	// FadeIn par default pour les images
	img_FadeOut = null,	// FadeOut par default pour les images
	pool_FadeIn = null,	// FadeIn par default pour les premieres images d'un pool
	pool_FadeOut = null;// FadeOut par default pour les premieres images d'un pool

/* Lancement du script des que la page est chargee */
if (window.addEventListener) window.addEventListener('load', initializePage, false);// Firefox
else if (window.attachEvent) window.attachEvent('onload', initializePage);			// Internet Explorer

/* Point d'entree du script */
function initializePage() {
	myDebugger = new debuggerObj();												// Initialisation de la fenetre de debuggage
	if (debugging) {
		myDebugger.logTimer('start initializePage()');
		myDebugger.log('script du 8 sept 2009 v1');
	}
	var pageURL = getPageURL();													// Determiner l'adresse de la page web actuelle. Si l'adresse est xxx.html, le fichier de configuration doit etre xxx_diapo.xml, dans le meme repertoire
	myDebugger.logTimer('getPageURL() done');
	if (pageURL != null) {
		myMutex = new Mutex();													// Initialiser le mutex
		myPreLoader = new preLoader();											// Initialiser le pre-loader
		/* importXML est une fonction qui se trouve dans importxml.js. Ce fichier js doit donc etre reference dans le code HTML egalement
		** importXML essaie de trouver le fichier de configuration, et s'il est trouve, lance la fonction Parse_ConfigFile */
		myDebugger.logTimer('start importXML');
	    importXML(pageURL.replace(/\.html?/, '_diapo.xml'), 'Parse_ConfigFile', false, 2000);
		pageURL = null;
		ReCentrer();															// 'Corrige' un bug de FF2-
		capH1Elmnt = document.getElementsByTagName('h1');						// Determine quel est le premier element <h1 /> du DOM. Il servira a afficher les legendes
		if (capH1Elmnt != null && capH1Elmnt.length > 0) {
			capH1Elmnt = capH1Elmnt[0];
			capH1InnerHTML = capH1Elmnt.innerHTML;								// Sauvegarder le contenu initial de ce H1 pour restauration entre 2 legendes
		}
		else capH1Elmnt = null;
	}
	myDebugger.logTimer('initializePage() done');
}

/* Fonction permettant d'interpreter le fichier de configuration
** Le contenu du fichier de configuration est passe comme argument par importXML */
function Parse_ConfigFile(oDocObj) {
	myDebugger.logTimer('start Parse_ConfigFile');
	oDocObj = oDocObj.lastChild;													// sous IE, firstChild correspond a <xml .../>, donc lastChild accede a <diaporamas /> chez tout le monde
	if (oDocObj) {																	// Verifier que le fichier de config contient bien quelque chose
	    var myDiaps = oDocObj.getElementsByTagName('diaporama');					// Selectionner les elements <diaporama /> du fichier
	    if (myDiaps != null) {														// Verifier que ces elements ont bien ete trouves
	    	extractDefVal_FromConfig(getFirstElementByTagName(oDocObj, 'defVal'));	// Extraire les valeurs par defaut
	    	myDebugger.logTimer('extractDefVal_FromConfig done');
	        var diapsCount = myDiaps.length;
	        for (var j = 0; j < diapsCount; j++) {	 								// Passer en revue chacun des elements <diaporama />
	            extractDiaporama_FromConfig(myDiaps[j]);							// Extraire les informations du fichier de configuration
	        }
	        myDiaps = null; diapsCount = null;
	        mode_default = null; poolMode_default = null; trans_default = null; width_default = null; height_default = null; bgColor_default = null; target_def = null; waitStart_def = null; interpool_Wait = null; interpool_bg = null; fade_speed_def = null; trans_default = null; 
	        diap_FadeIn = null; diap_FadeOut = null; pool_FadeIn = null; pool_FadeOut = null;
	        //img_FadeIn = null; img_FadeOut = null; pictime_def = null; captionText_Col_def = null;  captionBg_Col_def = null; captionBg_Transp_def = null; CSSRegex = null; trimRegex = null; // utilises lors du cloning des images
	        myDebugger.logTimer('Diaporama extraction done');
	    }
	}
	// Si la page a deja ete initialisee, inutile de recommencer. Cette situation se produit par exemple lorsque l'utilisateur utilise la fonction 'Back' du navigateur
	if (window.removeEventListener) window.removeEventListener('load', initializePage, false);	// Firefox
	else if (window.detachEvent) window.detachEvent('onload', initializePage);					// Internet Explorer
	myDebugger.logTimer('Parse_ConfigFile done');
}

/*****************************/
/*** Fonctions assistantes ***/
/*** pour l'interpretation ***/
/*** du fichier de config  ***/
/*****************************/
/* getPageURL retourne l'adresse de la page actuelle, ou null s'il n'est pas possible de la determiner */
function getPageURL() {
    var pageURL = null;																	// initialise la variable a retourner. La valeur par defaut est null
    if (window.location) pageURL = window.location.href;								// determine l'URL si celle-ci est accessible
    if (pageURL && pageURL.length > 7 && pageURL.toLowerCase().indexOf('.htm') < 0) {	// si l'URL ne contient pas '.htm', ajouter index.html a la fin (ex: http://www.laurenttheeten.com/)
    	if (pageURL.substring(pageURL.length - 1) == '/') pageURL += 'index.html';		// ajouter index.html si l'URL se termine par un slash
    	else pageURL += '/index.html';													// ajouter /index.html sinon
    }
    return pageURL;
}

/* Cette fonction extrait les valeurs par defaut */
function extractDefVal_FromConfig(defValObj) {
	if (defValObj != null) {
		poolMode_default = diapMode(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'poolMode')), 2, poolMode_default);		// le mode de choix des pools a l'interieur d'un diaporama
		width_default = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'width')), width_default, 1, 9999);		// la largeur des diaporamas
		height_default = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'height')), height_default, 1, 9999);	// la hauteur des diaporamas
		diap_FadeIn = extractFadeIn_FromConfig(getFirstElementByTagName(defValObj, 'fadein'), diap_FadeIn);								// le fadein initial des diaporamas
		diap_FadeOut = extractFadeOut_FromConfig(getFirstElementByTagName(defValObj, 'fadeout'), diap_FadeOut);							// le fadeout final des diaporamas
		waitStart_def = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'waitStart')), waitStart_def, 0, 9999);	// l'attente avant demarrage des diaporamas generiques
		var defVal = getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'targetID'));												// l'ID du div cible
		if (defVal.length > 0) target_def = document.getElementById(defVal);
		defVal = getFirstElementByTagName(defValObj, 'pool');
		if (defVal != null) {
			picMode_default = diapMode(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'picMode')), 2, picMode_default);			// le mode de choix des images a l'interieur d'un pool d'images
			pool_FadeIn = extractFadeIn_FromConfig(getFirstElementByTagName(defVal, 'fadein'), pool_FadeIn);							// le fadein initial du pool
			pool_FadeOut = extractFadeOut_FromConfig(getFirstElementByTagName(defVal, 'fadeout'), pool_FadeOut);						// le fadeout final du pool
			interpool_Wait_default = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'interpool_Wait')), interpool_Wait_default, 0, 9999);	// le temps d'attente entre deux pools
			bgColor_default = checkCSSColor(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'bgColor')), bgColor_default);								// la couleur de fond des fadings
			interpool_Bg_default = checkCSSColor(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'interpool_Bg')), interpool_Bg_default);					// la couleur de fond s'il y a un temps d'attente entre deux pools
			defVal = getFirstElementByTagName(defVal, 'img');
			if (defVal != null) {
				pictime_def = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'display_time')), pictime_def, 0, 9999);	// temps d'affichage d'une image
				img_FadeIn = extractFadeIn_FromConfig(getFirstElementByTagName(defVal, 'fadein'), img_FadeIn);								// fadin des images
				img_FadeOut = extractFadeOut_FromConfig(getFirstElementByTagName(defVal, 'fadeout'), img_FadeOut);							// fadeout des images
				defVal = getFirstElementByTagName(defVal, 'caption');
				if (defVal != null) {
					captionText_Col_def = checkCSSColor(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'textCol')), captionText_Col_def);			// couleur du texte des legendes
					captionBg_Col_def = checkCSSColor(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'bgCol')), captionBg_Col_def);					// couleur de fond des legendes
					captionBg_Transp_def = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defVal, 'bgTransp')), captionBg_Transp_def, 1, 100);	// transparence du fond des legendes
					defVal = getUnNestedTextNodes(getFirstElementByTagName(defVal, 'top'));
					if (defVal.length > 0) captionTop_def = defVal;																							// position des legendes
					defVal = null;
				}
			}
		}
		/* Couleurs et transparences du fond du logo de chargement */
		preloadBg_def = checkCSSColor(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'preloadBg')), preloadBg_def);
		preloadBgInit_def = checkCSSColor(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'preloadBgInit')), preloadBgInit_def);
		preloadBgTransp_def = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'preloadBgTransp')), preloadBgTransp_def, 0, 100);
		preloadBgInitTransp_def = CheckInteger(getUnNestedTextNodes(getFirstElementByTagName(defValObj, 'preloadBgInitTransp')), preloadBgInitTransp_def, 0, 100);
	}
}

/* Fonction de conversion d'un element <diaporama /> du fichier de configuration (passe en argument)
** en objet diaporama javascript */
function extractDiaporama_FromConfig(diapObj) {
	if (diapObj != null) {
		var poolList = extractPoolList_FromConfig(diapObj.getElementsByTagName('pool')),							// extraire la liste des pools
			targetID = getUnNestedTextNodes(getFirstElementByTagName(diapObj, 'targetID')),							// extraire l'ID du div cible
			targetObj = target_def,																					// div cible par defaut
			linkID = getUnNestedTextNodes(getFirstElementByTagName(diapObj, 'linkID')),								// extraire l'ID du lien declanchant la previsualisation
			linkObj = null;
		if (targetID.length > 0) targetObj = document.getElementById(targetID);										// trouver le div cible dans le DOM
		targetID = null;
		if (linkID.length > 0) linkObj = document.getElementById(linkID);											// trouver le lien declanchant la previsualisation dans le DOM
		if (poolList.length > 0 && targetObj != null && (linkID.length == 0 || linkObj != null))					// s'il n'y a pas d'images ou pas de div cible, ou pas de lien ayant l'ID donnee, renvoyer null, sinon...
			new diaporama(	getUnNestedTextNodes(getFirstElementByTagName(diapObj, 'poolMode')),					// extraire le mode de choix des pools
							extractFadeIn_FromConfig(getFirstElementByTagName(diapObj, 'fadein'), diap_FadeIn),		// extraire le fadein initial
							extractFadeOut_FromConfig(getFirstElementByTagName(diapObj, 'fadeout'), diap_FadeOut),	// extraire le fadeout final
							poolList,
							linkObj,
							targetObj,
							getUnNestedTextNodes(getFirstElementByTagName(diapObj, 'width')),						// extraire la largeur
							getUnNestedTextNodes(getFirstElementByTagName(diapObj, 'height')),						// extraire la hauteur
							getUnNestedTextNodes(getFirstElementByTagName(diapObj, 'waitStart'))					// extraire le temps d'attente avant demarrage
						);
		targetObj = null; poolList = null; linkID = null; linkObj = null;
	}
}

/* Fonction de conversion des elements <pool /> du fichier de configuration (passes en argument)
** en tableau d'objets pool javascript */
function extractPoolList_FromConfig(poolsObj) {
	var myPools = new Array();								// initialise la variable a retourner en un tableau vide
	if (poolsObj != null) {									// ne proceder que si la liste des pools est non null
		var poolsCount = poolsObj.length, myPool;
		for (var j = 0; j < poolsCount; j++) {
			myPool = extractPicPool_FromConfig(poolsObj[j]);// extraire chacun des pools
			if (myPool != null) myPools.push(myPool);		// si le pool extrait est non null, l'ajouter au tableau a retourner
		}
		poolsCount = null; myPool = null;
	}
	return myPools;
}

/* Fonction de conversion d'un element <pool /> du fichier de configuration (passe en argument)
** en un objet pool javascript */
function extractPicPool_FromConfig(poolObj) {
	var myPool = null;																										// initialise la variable a retourner
	if (poolObj != null) {																									// si l'argument est null, renvoyer null
		var myPicList = extractPicList_FromConfig(poolObj.getElementsByTagName('img'));										// extraire la liste des images
		if (myPicList != null) {																							// s'il n'y en a pas, renvoyer null
			myPool = new picPool(	getUnNestedTextNodes(getFirstElementByTagName(poolObj, 'picMode')),						// extraire le mode de choix des images
									extractFadeIn_FromConfig(getFirstElementByTagName(poolObj, 'fadein'), pool_FadeIn),		// extraire le fadein initial
									extractFadeOut_FromConfig(getFirstElementByTagName(poolObj, 'fadeout'), pool_FadeOut),	// extraire le fadeout final
									myPicList,
									getUnNestedTextNodes(getFirstElementByTagName(poolObj, 'interpool_Wait')),				// extraire le temps d'attente avant le pool suivant
									getUnNestedTextNodes(getFirstElementByTagName(poolObj, 'interpool_Bg')),				// extraire la couleur du fond pendant l'attente avant le pool suivante
									extractPic_FromConfig(getFirstElementByTagName(poolObj, 'firstImg')),					// extraire l'image d'en-tete
									getUnNestedTextNodes(getFirstElementByTagName(poolObj, 'bgColor')),						// extraire la couleur de fond pour le fading
									getUnNestedTextNodes(getFirstElementByTagName(poolObj, 'isFirst')),						// ce pool doit-il sortir en premier en mode batch?
									extractCaption_FromConfig(getFirstElementByTagName(poolObj, 'caption'))					// legende de pool
								);
			myPicList = null;
		}
	}
	return myPool;
}

/* Fonction de conversion d'une liste d'elements <img /> du fichier de configuration (passes en argument)
** en un tableau d'objets diaporamaPic javascript */
function extractPicList_FromConfig(imgsObj) {
	var myImgs = new Array();							// initialise la variable a retourner en un tableau vide
	if (imgsObj != null) {								// ne proceder que si la liste des images est non null
		var imgsCount = imgsObj.length, myImg;
		for (var j = 0; j < imgsCount; j++) {
			myImg = extractPic_FromConfig(imgsObj[j]);	// extraire chacune des images
			if (myImg != null) myImgs.push(myImg);		// si l'image extraite est non null, l'ajouter au tableau a retourner
		}
		imgsCount = null; myImg = null;
	}
	return myImgs;
}

/* Fonction de conversion d'un element <img /> du fichier de configuration (passe en argument)
** en un objet diaporamaPic javascript */
function extractPic_FromConfig(imgObj) {
	var myImg = null;															// initialise la variable a retourner. La valeur par defaut est null
	if (imgObj != null) {														// ne proceder que si l'image est non null
		var url = getUnNestedTextNodes(getFirstElementByTagName(imgObj, 'url'));// extraire l'url de  l'image
		if (url.length > 0) {													// ne proceder que si l'url est indiquee
			myImg = new diaporamaPic(	url,
										extractFadeIn_FromConfig(getFirstElementByTagName(imgObj, 'fadein'), img_FadeIn),	// extraire le fadein
										extractFadeOut_FromConfig(getFirstElementByTagName(imgObj, 'fadeout'), img_FadeOut),// extraire le fadeout
										getUnNestedTextNodes(getFirstElementByTagName(imgObj, 'display_time')),				// extraire le temps d'affichage
										extractCaption_FromConfig(getFirstElementByTagName(imgObj, 'caption'))				// extraire les parametres de la legende
									);
		}
		url = null;
	}
	return myImg;
}

/* Fonction de conversion d'un element <fadein /> du fichier de configuration (passe en argument)
** en un objet picFadein javascript */
function extractFadeIn_FromConfig(fadeObj, defaultFade) {
	return new picFadein(	getUnNestedTextNodes(getFirstElementByTagName(fadeObj, 'initial_trans')),	// extraire la transparence initiale
							getUnNestedTextNodes(getFirstElementByTagName(fadeObj, 'trans_speed')),		// extraire la vitesse de fading
							defaultFade
						);
}

/* Fonction de conversion d'un element <fadeout /> du fichier de configuration (passe en argument)
** en un objet picFadeout javascript */
function extractFadeOut_FromConfig(fadeObj, defaultFade) {
	return new picFadeout(	getUnNestedTextNodes(getFirstElementByTagName(fadeObj, 'final_trans')),	// extraire la transparence finale
							getUnNestedTextNodes(getFirstElementByTagName(fadeObj, 'trans_speed')),	// extraire la vitesse de fading
							defaultFade
						);
}

/* Fonction de conversion d'un element <caption /> du fichier de configuration (passe en argument)
** en un objet picCaption javascript */
function extractCaption_FromConfig(capObj, isPoolCap) {
	var myCap = null;
	if (capObj != null) {
		var myText = getUnNestedTextNodes(getFirstElementByTagName(capObj, 'text'));							// Si le texte de legende est vide, renvoyer null (pas de legende)
		if (myText.length > 0) {
			myCap = new picCaption(	myText,
									getUnNestedTextNodes(getFirstElementByTagName(capObj, 'textCol')),			// extraire la couleur du texte
									getUnNestedTextNodes(getFirstElementByTagName(capObj, 'bgCol')),			// extraire la couleur du fond
									getUnNestedTextNodes(getFirstElementByTagName(capObj, 'bgTransp')),			// extraire la transparence du fond
									getUnNestedTextNodes(getFirstElementByTagName(capObj, 'top')),				// extraire la position
									getUnNestedTextNodes(getFirstElementByTagName(capObj, 'toH1')).length > 0	// cette legende doit-elle etre affichee dans un bandeau sur l'image ou dans le H1
								);
		}
		myText = null;
	}
	return myCap;
}

/* Cette fonction extrait d'un objet le premier des elements fils ayant pour tag tagName */
function getFirstElementByTagName(oDocObj, tagName) {
	var myElmt = null;															// initialise la variable a retourner. La valeur par defaut est null
	if (oDocObj != null && tagName != null && tagName.toString().length > 0) {	// si oDocObj est null ou que tagName est vide, retourner null
		tagName = tagName.toLowerCase();										// comparer sur les tagnames en minuscule
		myElmt = oDocObj.firstChild;
		while (myElmt) {
			if (myElmt.nodeName.toLowerCase() == tagName) break;
			myElmt = myElmt.nextSibling;
		}
	}
	return myElmt;
}

/* Cette fonction extrait le texte inclus dans un objet */
function getUnNestedTextNodes(oFrom) {
    var oStr = '';															// initialise la variable a retourner. La valeur par defaut est une chaine vide
    if (oFrom != null && oFrom.childNodes) {								// verifier que oFrom a un contenu
        var childNodesLength = oFrom.childNodes.length, childNode, myType;	// extraire la liste de ses noeuds enfants
        for (var j = 0; j < childNodesLength; j++) {						// passer en revue les noeuds enfants
        	childNode = oFrom.childNodes[j];
        	myType = childNode.nodeType;
            if (((myType == 3) || (myType == 4)) && childNode.nodeValue) oStr += childNode.nodeValue;// concatener les contenus des noeuds texte
        }
        childNodesLength = null; childNode = null; myType = null;
    }
    return oStr;
}

/* CheckInteger verifie que le premier argument (myInt) est un nombre et renvoie l'entier le plus proche
** Si myInt n'est pas un entier, CheckInteger retourne le deuxieme argument (myDefault)
** Si myInt est un entier inferieur a myMin, CheckInteger retourne myMin
** Si myInt est un entier superieur a myMax, CheckInteger retourne myMax */
function CheckInteger(myInt, myDefault, myMin, myMax) {
    var myOutput = myDefault;									// initialise la variable a retourner. La valeur par defaut est myDefault
    if (myInt != null && myInt.length > 0 && !isNaN(myInt)) {	// verifier que myInt est bien un nombre
        myOutput = Math.round(myInt);							// arrondir ce nombre
        if (myOutput < myMin) myOutput = myMin;					// ne pas depasser la limite inferieure
        else if (myOutput > myMax) myOutput = myMax;			// ne pas depasser la limite superieure
    }
    return myOutput;
}

/* diapMode verifie la validite du mode du diaporama.
** les valeurs correctes sont 'random', 'batch' et 'list'.
** defMode est la valeur par defaut retournee si myMode n'est pas valide */
function diapMode(myMode, selCount, defMode) {
	var myModeStr = 'list';						// initialise la variable a retourner. La valeur par defaut est 'list'
	if (selCount > 1) {							// s'il n'y a qu'une image ou qu'un pool, on reste sur 'list' qui necessite le moins de calcul
		if (myMode != null) {
			myModeStr = myMode.toLowerCase();	// mettre myMode en minuscules
			switch (myModeStr)
			{
				case 'random':
				case 'batch':
				case 'list':
					break;						// si la valeur est correcte, ne pas la changer
				default:
					myModeStr = defMode;		// si la valeur est incorrecte, renvoyer defMode
					break
			}
		}
		else myModeStr = defMode;				// si l'argument etait null, renvoyer defMode
	}
	return myModeStr;
}

/* Cette fonction verifie la validite d'un parametre de couleur CSS */
function checkCSSColor(myColor, defaultColor) {
	var checkedCol = defaultColor;											// initialise la variable a retourner. La valeur par defaut est defaultColor
	if (myColor != null && myColor.length > 3) {							// si l'argument fait moins de 3 caracteres, il ne peut pas etre valide
		myColor = myColor.toString().replace(trimRegex, '').toLowerCase();	// sinon, convertir en minuscules et enlever les espaces de debut et de fin
		if (CSSRegex.test(myColor)) checkedCol = myColor;					// et verifier la validite a l'aide de l'expression reguliere definie dans le scope general
	}
	return checkedCol;
}

/**************/
/*** Objets ***/
/**************/
/*** Diaporamas ***/
function picFadein(initial_trans, trans_speed, defaultFade) {					// definit l'animation d'apparition d'une image
	var initial_transDef = trans_default, trans_speedDef = fade_speed_def;		// charger les valeurs par defaut
	if (defaultFade != null) {													// mettre a jour ces valeur si un fadein par defaut a ete passe en argument
		initial_transDef = defaultFade.initial_trans;
		trans_speedDef = defaultFade.trans_speed;
	}
    this.initial_trans = CheckInteger(initial_trans, initial_transDef, 0, 100);	// transparence de depart
    initial_transDef = null;
    this.trans_speed = CheckInteger(trans_speed, trans_speedDef, 1, 9000);		// vitesse d'apparition
    trans_speedDef = null;
}

function picFadeout(final_trans, trans_speed, defaultFade) {					// definit l'animation de disparition d'une image
	var final_transDef = trans_default, trans_speedDef = fade_speed_def;		// charger les valeurs par defaut
	if (defaultFade != null) {													// mettre a jour ces valeur si un fadeout par defaut a ete passe en argument
		initial_transDef = defaultFade.initial_trans;
		trans_speedDef = defaultFade.trans_speed;
	}
    this.final_trans = CheckInteger(final_trans, final_transDef, 0, 100);		// transparence de fin
    final_transDef = null;
    this.trans_speed = CheckInteger(trans_speed, trans_speedDef, 1, 9000);		// vitesse de disparition
    trans_speedDef = null;
}

function diaporamaPic(url, fadein, fadeout, time, caption) {	// definit une image d'un diaporama
    this.picsrc = url;											// URL de l'image
    this.fadein = fadein;										// animation d'apparition d'une image
    this.time = CheckInteger(time, pictime_def, 200, 30000);	// temps d'affichage de l'image (entre apparition et disparition)
    this.fadeout = fadeout;										// animation de disparition d'une image
    this.caption = caption;										// legende de l'image
    this.isLoaded = false;										// L'image est-elle pre-chargee?
    this.poolCaption = null;									// Legende de pool
}

/* Cette fonction definit une action a executer lorsque l'image sera chargee
** Elle est appelee par le div executant le diaporama */
diaporamaPic.prototype.setOnLoad = function(nextFadeTime, diapCaller, targetDiv, diapWasReady) {
	if (this.isLoaded) {
		targetDiv.loadNextPic(this.picsrc, nextFadeTime, diapCaller, diapWasReady);	// Si l'image est deja chargee, la transferer dans le DOM
		this.onLoadFired = true;
	}
	else {																			// Sinon, conserver les parametres necessaires pour son transfert ulterieur
		var oInstance = this;
		oInstance.onLoadFired = false;
		oInstance.nextFadeTime = nextFadeTime;
		oInstance.diapCaller = diapCaller;
		oInstance.diapWasReady = diapWasReady;
		oInstance.targetDiv = targetDiv;
		/* Il se peut qu'une 'race condition' entre setOnLoad() et markLoaded() fige definitivement cette image, ce qui a pour effet de
		** bloquer le diaporama sur le temoin de chargement. Pour se premunir de cela, on prevoit une verification dans 5 secondes. */
		window.setTimeout(new function() { oInstance.doubleCheckLoaded(); }, 5000);
	}
}

/* Cette fonction verifie si une race condition entre setOnLoad() et markLoaded() n'a pas fige cette image */
diaporamaPic.prototype.doubleCheckLoaded = function() {
	if (this.isLoaded && this.targetDiv && !this.onLoadFired)											// Si l'image etait bloquee, la debloquer
		this.targetDiv.loadNextPic(this.picsrc, this.nextFadeTime, this.diapCaller, this.diapWasReady);
	else {																								// Sinon reverifier dans 5 secondes
		var oInstance = this;
		window.setTimeout(new function() { oInstance.doubleCheckLoaded(); }, 5000);
	}
}

/* Cette fonction marque l'image comme chargee, et execute 'onload'.
** Elle est appelee (indirectement) par le pre-loader */
diaporamaPic.prototype.markLoaded = function() {
	if (this.targetDiv) {			// Si une action 'onload' a ete definie, l'executer (charger l'image dans le DOM)
		this.targetDiv.loadNextPic(this.picsrc, this.nextFadeTime, this.diapCaller, this.diapWasReady);
		this.onLoadFired = true;
	}
	this.isLoaded = true;			// Marquer l'image comme chargee
}

/* Cette fonction ajoute la legende de pool a une image */
diaporamaPic.prototype.setPoolCaption = function(poolCaption) {
	if (poolCaption != null) {												// Si la legende de pool n'est pas nulle
		if (this.caption == null || this.caption.toH1 != poolCaption.toH1)	// et si elle ne doit pas s'afficher au meme endroit que l'eventuelle legende d'image
			this.poolCaption = poolCaption;									// ajouter la legende de pool
	}
}

function picCaption(text, textColor, bgColor, transp, top, toH1) {		// definit une legende d'image
	this.text = text + '&nbsp;';										// texte
	this.textColor = checkCSSColor(textColor, captionText_Col_def);		// couleur du texte
	this.bgColor = checkCSSColor(bgColor, captionBg_Col_def);			// couleur du fond
	this.transp = CheckInteger(transp, captionBg_Transp_def, 1, 100);	// transparence du fond
	if (!top || top.length == 0) top = captionTop_def;
	this.top = top;														// position
	this.toH1 = toH1;													// Affichage dans le H1 ou dans un bandeau?
}

function picPool(myMode, fadein, fadeout, picList, interPool_Wait, interPool_Bg, firstImg, bgColor, isFirst, caption) {// definit un pool d'image a l'interieur d'un diaporama
	this.picMode = diapMode(myMode, picList.length, picMode_default);							// mode de choix des images (aleatoire par lot ou ordonne)
	if (this.picMode == 'random') this.picMode = 'batch';										// forcer le mode aleatoire par lot si aleatoire a ete choisi
    this.fadein = fadein;																		// animation d'apparition de la premiere image
    this.fadeout = fadeout;																		// animation de disparition de la derniere image
    this.picList = picList;																		// liste des images
    this.picCount = picList.length;																// nombre des images
    this.interPool_Bg = checkCSSColor(interPool_Bg, interpool_Bg_default);						// couleur de fond lors de l'attente du prochain pool
    this.firstImg = firstImg;																	// image d'en-tete
    this.bgColor = checkCSSColor(bgColor, bgColor_default);										// couleur de fond pour le fading
    interPool_Wait = CheckInteger(interPool_Wait, interpool_Wait_default, 0, 9999);				// temps d'attente avant le prochain pool
    if (interPool_Wait > 0) {																	// s'il y a un temps d'attente avant le prochain pool, definir l'image intercalaire
    	this.lastImg = new diaporamaPic(interPool_ImgSRC,										// URL definie dans le scope general
										new picFadein(0, fadeout.trans_speed, img_FadeIn),		// fadein 'inverse' du fadeout final
										new picFadeout(0, 1, img_FadeOut),						// fadeout immediat
										interPool_Wait,											// temps d'affichage correspondant au temps d'attente
										null);													// aucune legende
		this.lastImg.isLoaded = true;															// l'image intercalaire est toute petite et est consideree comme chargee par defaut
	}
    else this.lastImg = null;
    this.isFirst = (isFirst.length > 0);														// Ce pool doit-il toujours apparaitre en premier en mode batch?
    if (caption != null) {																		// Ajouter les legendes de pool aux images
    	if (this.firstImg != null) this.firstImg.setPoolCaption(caption);
    	for (var j = 0; j < this.picCount; j++) {
    		this.picList[j].setPoolCaption(caption);
    	}
    }
    
    this.batchCount = -1;																		// compteur du nombre de fois qu'un lot a ete genere. Utilise si le diaporama est en mode batch
	this.lastPic = -2;																			// indice de la derniere image dequeee
	this.lastPicURL = '';																		// url de la derniere image dequeuee
    this.reset();																				// melanger l'ordre des images si le mode est aleatoire
}

/* Cette fonction choisit la prochaine image du pool a envoyer au diaporama */
picPool.prototype.deQueue = function() {
	this.lastPic++;											// incrementer lastPic, qui est desormais l'index de l'image a retourner
	var nextPic = null, lastPic = this.lastPic, picCount = this.picCount;
	if (lastPic < picCount) {								// si le stock d'images n'est pas epuise...
		if (lastPic == -1) {								//... si l'on est en debut de liste, renvoyer l'image d'en-tete si elle est definie
			nextPic = this.clonePic(this.firstImg);
			if (nextPic == null) {
				nextPic = this.clonePic(this.picList[0]);
				this.lastPic = 0;
			}
			nextPic.fadein = this.fadein;					//... et utiliser le fadein initial
		}
		else nextPic = this.clonePic(this.picList[lastPic]);//... si l'on n'est pas au debut, renvoyer l'image suivante
		if (lastPic == picCount - 1) {						//... si on est a la fin
			nextPic.fadeout = this.fadeout;					//... utiliser le fadeout final
			if (this.lastImg != null)
				nextPic.bgColor = this.interPool_Bg;		//... et le background final (sauf si on a un intercalaire de prevu)
		}
	}
	else if (lastPic == picCount && this.lastImg != null) {	// si le stock d'images est epuise et qu'un temps d'attente a ete demande, renvoyer l'image intercalaire
		nextPic = this.clonePic(this.lastImg);
		nextPic.bgColor = this.interPool_Bg;
	}
	else this.reset();										// sinon incrementer le compteur de lots generes, repartir du debut, et renvoyer null
	lastPic = null;
	if (nextPic != null) this.lastPicURL = nextPic.picsrc;	// sauvegarder l'url de la pderniere image dequeee. Cela permet d'empecher que la premiere image du batch suivant soit la derniere de celui en cours
	return nextPic;
}

/* Cette fonction est appelee chaque fois qu'on termine le pool */
picPool.prototype.reset = function() {
	if (this.picMode == 'batch') {
		this.picList.sort(function(a, b) { return 0.5 - Math.random(); });			// melanger l'ordre des images si le mode est aleatoire
		if (this.firstImg == null && this.picList[0].picsrc == this.lastPicURL) {	// empechere que la premiere image du batch suivant soit la meme que la derniere du precedent
			var firstInLine = this.picList.shift();
			this.picList.push(firstInLine);											// si c'etait la meme, la replacer en queue de liste
			firstInLine = null;
		}
	}
	this.batchCount++;																	// incrementer le compteur d'utilisation du pool
	this.lastPic = -2;																	// redemarrer de la premiere image
}

/* Cette fonction permet de cloner une image. Cela permet de renvoyer une copie
** eventuellement modifiee (fadein, fadeout) sans changer la liste definie */
picPool.prototype.clonePic = function(origPic) {
	var clonedPic = null;											// initialise la variable a retourner
	if (origPic != null)  {											// renvoyer null si l'originale est null
		clonedPic = new diaporamaPic(	origPic.picsrc,
										new picFadein(origPic.fadein.initial_trans, origPic.fadein.trans_speed, img_FadeIn),
										new picFadeout(origPic.fadeout.final_trans, origPic.fadeout.trans_speed, img_FadeOut),
										origPic.time,
										this.cloneCaption(origPic.caption)
									);
		myCaption = null;
		clonedPic.poolIndex = -1;										// definir la propriete poolIndex qui sera utilisee par le diaporama pour tagger l'image
    	clonedPic.bgColor = this.bgColor;								// definir la couleur du fond
    	clonedPic.isLoaded = origPic.isLoaded;							// copier le marqueur de chargement
    	clonedPic.poolCaption = this.cloneCaption(origPic.poolCaption);	// copier la legende de pool
	}
	return clonedPic;
}

/* Cette fonction clone un objet caption */
picPool.prototype.cloneCaption = function(origCap) {
	/* A priori, dans l'etat actuel des choses, les div ne modifient aucune des valeurs des legendes lors de leur affichage
	** dans un diaporama. Il n'est donc pas utile de faire un clonage, une copie suffit
	var clonedCap = null;	// Renvoyer null si l'original est null
	if (origCap != null)	// sinon, cloner les valeurs
		clonedCap = new picCaption(origCap.text, origCap.textColor, origCap.bgColor, origCap.transp, origCap.top, origCap.toH1);
	return clonedCap;*/
	return origCap;
}

/* Cette fonction recherche une image dans la liste du pool et la marque comme chargee
** Elle suppose qu'un pool ne contient pas deux fois la meme image (meme URL) */
picPool.prototype.markLoaded = function(loadedUrl) {
	if (this.firstImg != null && this.firstImg.picsrc == loadedUrl)
		this.firstImg.isLoaded = true;
	else {
		var myPicList = this.picList, picCount = this.picCount;
		for (var j = 0; j < picCount; j++) {
			if (myPicList[j].picsrc == loadedUrl) {
				myPicList[j].isLoaded = true;
				break;
			}
		}
		myPicList = null; picCount = null;
	}
}

/* Cette fonction marque toutes les images comme non chargees */
picPool.prototype.markAllUnloaded = function() {
	if (this.firstImg != null) this.firstImg.isLoaded = false;
	var poolPicCount = this.picCount, poolPicList = this.picList;
	for (var j = 0; j < poolPicCount; j++) {
		poolPicList[j].isLoaded = false;
	}
	poolPicCount = null; poolPicList = null;
}

/* Cette fonction recherche une image dans la liste du pool et l'enleve
** Elle suppose qu'un pool ne contient pas deux fois la meme image (meme URL) */
picPool.prototype.removePic = function(deadPicURL) {
	var myPicList = this.picList, picCount = this.picCount;
	for (var j = 0; j < picCount; j++) {
		if (myPicList[j].picsrc == deadPicURL) {
			myPicList.splice(j, 1);
			this.picCount--;
			break;
		}
	}
	myPicList = null; picCount = null;
}

function diaporama(myMode, fadein, fadeout, poolList, linkObj, divTarget, width, height, waitStart) {
	this.id = diapID++;															// attribuer une ID au diaporama. C'est aussi son index dans le tableau diaporamas
    this.isPreview = (linkObj != null);											// s'agit-il du'une preview ou d'un diaporama generique
	this.poolList = poolList;													// liste des pools d'images
	this.poolCount = poolList.length;											// nombre de pools
	if (this.poolCount == 1) this.poolList[0].lastImg = null;
	this.poolMode = diapMode(myMode, poolList.length, poolMode_default);		// mode de choix des pools (aleatoire, aleatoire par lot ou ordonne)
	if (this.poolMode == 'batch' && this.poolCount == 2)						// si l'on a que deux pools, utiliser le mode random aura le meme effet que d'utiliser le mode batch tout en necessitant moins de calculs
		this.poolMode = 'random';
    this.fadein = fadein;														// animation d'apparition de la premiere image
    this.fadeout = fadeout;														// animation de disparition de la derniere image
    this.poolRotationCount = 0;													// compteur de transitions de pools effectuees. En mode 'batch', ce compteur est utilise pour calculer combien de fois chaque pool doit avoir ete utilise
    this.activePool = -1;														// index du dernier pool utilise
    this.activePool = this.chooseNextPool();									// choisir le premier pool
    this.queueDepth = 5;														// determine combien d'images doivent etre selectionnees a l'avance
    this.picQueue = new Array();												// liste des images pre-selectionnees
    this.targetDivID = divTarget.id;											// sauvegarder l'ID du div cible au lieu de l'objet permet d'eviter une reference circulaire DOM-js mais ajoute quelques document.getElementById
    this.justOnePic = (poolList.length == 1 && (poolList[0].picCount == 0 || (poolList[0].firstImg == null && poolList[0].picCount == 1)));	// true si le diaporama n'a qu'une image
    this.lastDequeuedPoolIndex = -1;											// index de la derniere image envoyee au div cible. Lorsque le diaporama s'arrete, les images du debut de la pre-selection qui viennent du meme pool sont eliminees
    this.deQueuedNotLoaded = null;												// reference de l'image qui a ete dequeee (et n'est donc plus dans picQueue) alors qu'elle n'etait pas encore pre-chargee
    this.lastDequeued = 0;														// heure du dernier dequeuing
    
    width = CheckInteger(width, width_default, 1, 9999) + 'px';
    height = CheckInteger(height, height_default, 1, 9999) + 'px';
    if (!divTarget.allSet) this.setupDiv(divTarget, width, height);				// creation des elements necessaires a l'affichage du diaporama dans le div cible
    
    this.fillQueue();															// generer une pre-selection des prochaines images a afficher
    
    diaporamas[this.id] = this;													// inclure le diaporama dans le tableau des diaporamas
    if (!this.isPreview) {														// Si c'est un diaporama generique, le lancer
    	divTarget.showLoading();												// Afficher le temoin de chargement
    	divTarget.diapID = this.id;
    	this.start(CheckInteger(waitStart, waitStart_def, 0, 60000) - (new Date().getTime() - loadPageTime.getTime()));
    }
    else {																		// Si c'est une preview
		linkObj.diapID = this.id;												//... associer le diaporama avec le lien
		linkObj.onmouseover = linkMouseover;									//... demarrer le diaporama lorsque la souris passe au-dessus du lien
		linkObj.onmouseout = linkMouseout;										//... arreter le diaporama lorsque la souris quitte le lien
		linkObj.onclick = linkClick;											//... arreter le diaporama lorsque l'on clique sur le lien
	}
}

/* Cette fonction cree les elements necessaires a l'affichage du diaporama dans le div cible */
diaporama.prototype.setupDiv = function(myDiv, width, height) {
	var myElmnt = document.createElement('img');			// Creer l'element img pour l'une des deux images
	this.styleImg(myElmnt.style, 6, width, height, myDiv);	// ajuster son style CSS
	myDiv.Img1 = myElmnt;									// Garder une reference de l'objet dans la propriete Img1 du div
    myDiv.appendChild(myElmnt);								// Ajout au DOM
    setTransp(myElmnt, 0);									// L'element doit etre dans le DOM pour que IE veuille bien lui attribuer un filtre
    myDiv.Img1.willFadeIn = true;							// Le definir comme l'image qui apparait
    
	myElmnt = document.createElement('img');				// Creer l'element img pour l'une des deux images
	this.styleImg(myElmnt.style, 4, width, height, myDiv);	// ajuster son style CSS
    myDiv.Img2 = myElmnt;									// Garder une reference de l'objet dans la prorpriete Img2 du div
    myDiv.appendChild(myElmnt);								// Ajout au DOM
    setTransp(myElmnt, 0);									// L'element doit etre dans le DOM pour que IE veuille bien lui attribuer un filtre
    myDiv.Img2.willFadeIn = false;							// Le definir comme l'image qui disparait
    
	myElmnt = document.createElement('div');				// Creer l'element div pour la couleur de fond du fading
	this.styleElmt(myElmnt.style, 2, width, height, myDiv);	// ajuster son style CSS
	myElmnt.style.backgroundColor = 'transparent';			// initialiser sa couleur de fond a transparent
	myDiv.bgDiv = myElmnt;									// Garder une reference de l'objet dans la propriete bgDiv du div
    myDiv.appendChild(myElmnt);								// Ajout au DOM
    
	myElmnt = document.createElement('div');				// Creer l'element div pour le message de chargement
	var myLoadImg = document.createElement('img');			// Ajouter l'image
	myLoadImg.src = load_ImgSRC;
	myLoadImg.alt = '';
	myElmnt.appendChild(myLoadImg);
	myLoadImg = null;
	this.styleElmt(myElmnt.style, 10, width, height, myDiv);// ajuster son style CSS
	myElmnt.className = 'loadingDiv';						// lui donner sa classe CSS
	myDiv.loadDiv = myElmnt;								// Garder une reference de l'objet dans la propriete loadDiv du div
    myDiv.appendChild(myElmnt);								// Ajout au DOM
    
	myElmnt = document.createElement('span');				// Creer l'element span pour les legendes
	this.styleElmt(myElmnt.style, 8, width, height, myDiv);	// ajuster son style CSS
	myElmnt.style.height = '';								// annuler son reglage de hauteur
	myElmnt.className = 'caption';							// lui donner sa classe CSS
	myDiv.captionSpan = myElmnt;							// Garder une reference de l'objet dans la prorpriete captionSpan du div
    myDiv.appendChild(myElmnt);								// Ajout au DOM
	myElmnt = null;
	
	this.diapStart = 0;										// Heure de demarrage du dernier diaporama
	
	myDiv.diapInit = true;									// true si le div n'affiche encore aucune image
	
	/* Cette fonction recupere le tag unique du diaporama en cours en combinant son ID et son heure de demarrage */
	myDiv.getAnimationTag = function() {
		var activeDiapID = this.activeDiapID;
		if (activeDiapID != null) activeDiapID = activeDiapID.toString() + '_' + this.diapStart.toString();
		return activeDiapID;
	}
	
	/* Cette fonction verifie si le tag passe en argument est identique au tag de l'animation en cours */
	myDiv.checkAnimationTag = function(tagChallenge) {
		var activeDiapID = this.activeDiapID;
		return (activeDiapID != null && tagChallenge == activeDiapID.toString() + '_' + this.diapStart.toString());
	}
	
	/* Cette fonction extrait le diapID du tage unique. Elle permet de rendre atomique la verification de conformite du tag et la verification du diapID */
	myDiv.extractDiapIDFromTag = function(tag) {
		var diapId = '', usIndex = -1;
		if (tag != null) usIndex = tag.indexOf('_', 1);
		if (usIndex > -1) diapId = tag.substring(0, usIndex);
		usIndex = null;
		return diapId;
	}
	
	/* Cette fonction recupere l'element destine a l'image qui apparait */
	myDiv.getFadeInImg = function() {
		if (this.Img1.willFadeIn) return this.Img1;
		else return this.Img2;
	}
	
	/* Cette fonction recupere l'element destine a l'image qui disparait */
	myDiv.getFadeOutImg = function() {
		if (this.Img1.willFadeIn) return this.Img2;
		else return this.Img1;
	}
	
	/* Cette fonction affiche le temoin de chargement en choisissant le fond approprie */
	myDiv.showLoading = function() {
		if (!this.diapInit) this.showLoadingParam(preloadBg_def, preloadBgTransp_def);
		else this.showLoadingParam(preloadBgInit_def, preloadBgInitTransp_def);
	}
	
	/* Cette fonction affiche le temoin de chargement choisi */
	myDiv.showLoadingParam = function(Bg, transp) {
		var myLoadDiv = this.loadDiv, myLoadDivStyle = myLoadDiv.style;
		myLoadDivStyle.backgroundColor = Bg;	// definir la couleur de fond
		setTransp(myLoadDiv, transp);			// definir la transparence
		myLoadDiv = null;
		myLoadDivStyle.display = 'block';		// afficher le temoin
		myLoadDivStyle = null;
	}
	
	/* Cette fonction masque le temoin de chargement */
	myDiv.hideLoading = function() {
		this.loadDiv.style.display = 'none';
	}
	
	/* Cette fonction lance un diaporama */
	myDiv.start = function(myDiap, dateStart) {
		var activeDiapID = this.activeDiapID;
		if (!activeDiapID) {										// s'il n'y a pas de diaporama actif, il faut faire apparaitre les div
			this.Img1.style.display = 'block';
			this.Img2.style.display = 'block';
			this.bgDiv.style.display = 'block';
			if (this.stopFadeout())									// au cas ou la preview precedente est encore en fadeoutAnimFinal
				setTransp(this.getFadeOutImg(), 0);
			this.diapInit = true;
		}
		else if (myDiap.isPreview) {								// si le diaporama a lancer est une preview, arreter le diaporama generique actif
			var activeDiap = diaporamas[activeDiapID];
			if (!activeDiap.isPreview) {
				this.stop();										// on ne peut pas passer par le mutex, appel direct dans ce cas
				activeDiap.postStop();
			}
			activeDiap = null;
		}
		activeDiapID = null;
		this.activeDiapID = myDiap.id;								// garder la reference du diaporama actif
		this.diapStart = dateStart;									// marquer l'heure de demarrage
		this.setNextImage(myDiap.deQueueStartPic(), dateStart);		// preparer la premiere image du diaporama
	}
	
	/* Cette fonction arrete un diaporama */
	myDiv.stop = function() {
		this.cancelFade();												// Si un fading est programme, l'annuler
		var wasFading = this.stopFadein();								// Arreter le fading in en cours
		if (this.stopFadeout() || wasFading) this.swapImgs();			// Arreter le fading out en cours. Si un fadein ou un fadeout etait en cours, conclure le fading
		wasFading = null;
		this.hideCaption();												// Masquer la legende
		this.hideLoading();												// Masquer le temoin de chargement
		var activeDiap = diaporamas[this.activeDiapID];
	    if (activeDiap && activeDiap.isPreview) {						// Si le diaporama arrete est une preview...
	    	if (this.diapID) this.start(diaporamas[this.diapID], 0);	//... relancer le diaporama generique
	    	else {														//... s'il n'y a pas de diaporama generique
	    		var oInstance = this, myFadeoutImg = oInstance.getFadeOutImg();
			    myFadeoutImg.fadeout = activeDiap.fadeout;				// Associer le fadeout du diaporama a l'image actuellement affichee
			    myFadeoutImg.fadeout.trans_step = - HundredTimesFluidite / myFadeoutImg.fadeout.trans_speed;
	    		oInstance.bgDiv.style.backgroundColor = 'transparent';	//... faire disparaitre les div (sauf l'image qui disparait)
	    		oInstance.getFadeInImg().style.display = 'none';
	    		oInstance.bgDiv.style.display = 'none';
	    		oInstance.activeDiapID = null;							//... marquer qu'il n'y a plus de diaporama actif
	    		oInstance.fadeoutID = window.setInterval(function() { oInstance.fadeoutAnimFinal(myFadeoutImg); }, fluiditeAnimation);	//... faire disparaitre l'image par un fading out
	    	}
	    }
	    activeDiap = null;
	}
	
	/* Cette fonction prepare la prochaine image a faire apparaitre et planifie le prochain fading */
	myDiv.setNextImage = function(nextPic, nextFadeTime) {
		var diapCaller = this.getAnimationTag();														// Marquer l'ID du diaporama appelant pour eviter les boucles de fading orphelines
		if (diapCaller != null) {
			var myFadeinImg = this.getFadeInImg();
			myFadeinImg.fadeout = nextPic.fadeout;														// Lui associer son fadeout
			myFadeinImg.time = nextPic.time;															// Lui associer son temps de visualisation
			myFadeinImg.fadein = nextPic.fadein;														// Lui associer son fadein
			myFadeinImg.bgColor = nextPic.bgColor;														// Lui associer sa couleur de fond
			myFadeinImg.caption = nextPic.caption;														// Lui associer sa legende
			myFadeinImg.poolCaption = nextPic.poolCaption;												// Lui associer sa legende de pool
			myFadeinImg.fadein.trans_step = HundredTimesFluidite / myFadeinImg.fadein.trans_speed;		// Calculer les pas de transparence du fadin
			myFadeinImg.fadeout.trans_step = - HundredTimesFluidite / myFadeinImg.fadeout.trans_speed;	// Calculer les pas de transparence du fadout
			myFadeinImg = null;
			if (myPreLoader.activeCache && nextPic.picsrc != interPool_ImgSRC) {
				var diapIsReady = diaporamas[this.extractDiapIDFromTag(diapCaller)].isReady();
				if (!diapIsReady) //{
					this.showLoading();																	// Si le diaporama n'est pas pret, afficher le temoin de chargement
					//nextFadeTime = (new Date()).getTime() + this.getFadeOutImg().time;				// a priori inutile ici puisqu'on le fait dans  scheduleNextFade()
				//}
				nextPic.setOnLoad(nextFadeTime, diapCaller, this, diapIsReady);							// Passer a l'image les parametres necessaires au lancement de loadNextPic des qu'elle sera chargee
				diapIsReady = null;
			}
			else this.loadNextPic(nextPic.picsrc, nextFadeTime, diapCaller, true);						// Si le cache ne fonctionne pas, charger l'image
		}
	}
	
	/* Cette fonction charge la prochaine image du diaporama dans le DOM */
	myDiv.loadNextPic = function(src, nextFadeTime, diapCaller, diapWasReady) {
		if (this.checkAnimationTag(diapCaller)) {																	// Si le diaporama appelant est toujours le diaporama actif
			var oInstance = this, myFadeinImg = oInstance.getFadeInImg(), myFadeinImgStyle = myFadeinImg.style;
			myFadeinImg.onload = function() { oInstance.scheduleNextFade(nextFadeTime, diapCaller, diapWasReady); };// planifier le prochain fading lorsque l'image sera chargee
			myFadeinImgStyle.marginTop = '-999px';																	// ceci evite de faire apparaitre brievement les barres de defilement lorsque l'on passe du petite image a une grande image
			myFadeinImgStyle.marginLeft = '-999px';
			myFadeinImgStyle = null;
			myFadeinImg.src = src;																					// Charger l'image
			myFadeinImg = null;
		}
	}
	
	/* Cette fonction planifie le fading suivant */
	myDiv.scheduleNextFade = function(nextFadeTime, diapCaller, diapWasReady) {
		if (this.checkAnimationTag(diapCaller)) {																// Si le diaporama qui a appele ce fading est toujours le diaporama actif
			var diapIsReady = (diapWasReady || diaporamas[this.extractDiapIDFromTag(diapCaller)].isReady());	// Verifier si le diaporama est pret
			if (!diapWasReady) nextFadeTime = (new Date()).getTime() + this.getFadeOutImg().time;				// Si le temoin de chargement est affiche, recalculer la date du prochain fading
			if (diapIsReady || this.diapInit) {																	// Si le diaporama est pret ou qu'il s'agit de la premiere image
				var myFadeinImg = this.getFadeInImg(), initTrans = myFadeinImg.fadein.initial_trans, myFadeinImgStyle = myFadeinImg.style;
				myFadeinImgStyle.marginTop = Math.floor(- myFadeinImg.height * 0.5) + 'px';						// Centrer l'image
				myFadeinImgStyle.marginLeft = Math.floor(- myFadeinImg.width * 0.5) + 'px';
				myFadeinImgStyle = null;
				if (initTrans > 0) setTransp(myFadeinImg, initTrans);											// Pre-regler la transparence initiale de fadein
				initTrans = null; myFadeinImg = null;
				if (diapIsReady) this.hideLoading();															// Masquer le temoin de chargement si le diaporama est pret
				var nextFadeDelay = nextFadeTime - (new Date()).getTime();										// Lancer le fading dans tant de millisec
				if (nextFadeDelay < 9) this.fade(diapCaller, diapIsReady);										// Si l'heure de lancement est deja passee ou presque arrivee, lancer tout de suite
				else {
					var oInstance = this;																		// Sinon planifier pour la bonne heure
					oInstance.nextFadeID = window.setTimeout(function () { oInstance.fade(diapCaller, diapIsReady); }, nextFadeDelay);
				}
				nextFadeDelay = null;
			}
			else {																								// Si le diaporama n'est pas pret, verifier a nouveau dans 1 seconde
				var oInstance = this;
				window.setTimeout(function() { oInstance.scheduleNextFade(nextFadeTime, diapCaller, false); }, 999);
			}
		}
	}
	
	/* Cette fonction lance un fading */
	myDiv.fade = function(diapCaller, diapWasReady) {
		if (this.checkAnimationTag(diapCaller)) {																// si le diaporama actif n'est plus le diaporama qui a demande ce fading, arreter
			this.nextFadeID = null;																				// marquer qu'il n'y a plus de fading planifie
			var oInstance = this, myFadeinImg = oInstance.getFadeInImg(), myFadeoutImg = oInstance.getFadeOutImg();
			if (myFadeoutImg.caption) {																			// si l'image qui disparait avait une legende, la cacher
				if (myFadeoutImg.caption.toH1) oInstance.hideCaptionH1();
				else oInstance.hideCaptionSpan();
			}
    		oInstance.diapInit = false;																			// marquer que le div affiche actuellement une image
	    	if (!diapWasReady) oInstance.showLoading();															// mettre a jour le temoin d'affichage si besoin (en fonction de diapInit)
			if (myFadeinImg.fadein.trans_speed <= fluiditeAnimation) oInstance.fadeinAnim(myFadeinImg);			// si le fading in est realise en une etape, inutile de lancer des setInterval
			else {
			    oInstance.fadeinID = window.setInterval(function() { oInstance.fadeinAnim(myFadeinImg); }, fluiditeAnimation);			// lancer les iterations de fadein
			    if (myFadeoutImg.src.length > 0)
			    	oInstance.fadeoutID = window.setInterval(function() { oInstance.fadeoutAnim(myFadeoutImg); }, fluiditeAnimation);	// lancer les iterations de fadeout
			}
		}
	}
	
	/* Cette fonction arrete un fade out et renvoie true si un fade out etait en cours */
	myDiv.stopFadeout = function() {
		var wasFading = false, fadeoutID = this.fadeoutID;
		this.fadeoutID = null;				// marquer que le fading out est arrete
		if (fadeoutID) {					// si un fading out est en cours...
			window.clearInterval(fadeoutID);//... arreter le fading out
			fadeoutID = null;				//... marquer que le fading out est arrete
			wasFading = true;
		}
		return wasFading;
	}
	
	/* Cette fonction arrete un fade in et renvoie true si un fade in etait en cours */
	myDiv.stopFadein = function() {
		var wasFading = false, fadeinID = this.fadeinID;
		this.fadeinID = null;
		if (fadeinID) {						// si un fade in est en cours...
			window.clearInterval(fadeinID);	//... arreter le fade in
			fadeinID = null;
			wasFading = true;
		}
		this.lastFadeStepStart = null;
		return wasFading;
	}
	
	/* Cette fonction annule le prochain fading prevu */
	myDiv.cancelFade = function() {
		var nextFadeID = this.nextFadeID;
		this.nextFadeID = null;
		if (nextFadeID) {
			window.clearTimeout(nextFadeID);// Annuler le prochain fading s'il est deja planifie
			nextFadeID = null;
		}
		this.getFadeInImg().onload = null;	// Arreter le prechargement de la prochaine image qui declancherait le prochain fading onload
	}
	
	/* Cette fonction execute une etape de la disparition d'une image */
	myDiv.fadeoutAnim = function(myFadeoutImg) {
		var finished = false;
	    if (ChangeTransp(myFadeoutImg, myFadeoutImg.fadeout.trans_step) <= myFadeoutImg.fadeout.final_trans) {	// si on a atteint la valeur finale souhaitee...
	        this.stopFadeout();																					// ... arreter le fading out
	        finished = true;
	    }
	    return finished;
	}
	
	/* Cette fonction execute une etape de la disparition de la derniere image d'un diaporama preview a l'arret */
	myDiv.fadeoutAnimFinal = function(myFadeoutImg) {
		if (this.fadeoutAnim(myFadeoutImg)) {			// si l'animation est terminee (image disparue)
			if (myFadeoutImg.fadeout.final_trans > 0)
				setTransp(myFadeoutImg, 0);				// faire completement disparaitre l'image si besoin
	    	myFadeoutImg.style.display = 'none';		// masquer le div
		}
	}
	
	/* Cette fonction execute une etape de l'apparition d'une image d'un diaporama */
	myDiv.fadeinAnim = function(myFadeinImg) {
		this.adjustFluidity(myFadeinImg);
	    if (ChangeTransp(myFadeinImg, myFadeinImg.fadein.trans_step) >= 100) {	// si l'image est completement opaque...
	    	this.stopFadein();													//... marquer que les iterations de fade in ne sont plus en cours
	        this.stopFadeout();													//... arreter le fade out
	        this.swapImgs();													//... permutter les deux images de fading
	        var activeDiap = diaporamas[this.activeDiapID];						// this.activeDiapID peut etre nul a ce stade en fonction du timing avec l'appel a stop()
	        if (activeDiap) {
		        var myNextPic = activeDiap.deQueue();							//... demander la prochaine image au diaporama
		        activeDiap = null;
		        if (myNextPic != null) {										//... s'il y a une prochaine image, la preparer et programmer le prochain fading
		        	this.setNextImage(myNextPic, (new Date()).getTime() + this.getFadeOutImg().time);
		        	myNextPic = null;
		        	this.lastFadeStepStart = null;								// annulation du compteur utilise dans adjustFluidity()
		        }
		        else this.hideLoading();										//... sinon, masquer le temoin de chargement - le diaporma s'arrete
		    }
	    }
	}
	
	/* Cette fonction ajuste la fluidite des fadings en fonction de la reactivite de l'ordinateur
	** L'idee est de calculer combien de temps s'est ecoule entre x pas de fading. Si le temps est plus court
	** qu'attendu, c'est que le processeur n'est pas a la traine et que la fluidite peut etre augmentee.
	** Si le temps est nettement plus long, on leve le pied sur la fluidite */
	myDiv.adjustFluidity = function(myFadeinImg) {
		if (this.lastFadeStepStart) {
			this.fadeCounter++;
			if (this.fadeCounter >= 5) {																				// Mesurer le temps ecoule tous les 5 pas de fading
				var elapsedTimePerStep = ((new Date()).getTime() - this.lastFadeStepStart) / this.fadeCounter,			// Calcul du temps ecoule par pas
					wasAdjusted = false;
				if (fluiditeAnimation < fluidite_MAX && elapsedTimePerStep > fluiditeAnimation * CPUOverUsedFactor) {	// Si le temps est trop long
					if (myPreLoader) myPreLoader.lowerCPUUsage();														// Demander au pre-loader de moins solliciter le processeur
					fluiditeAnimation = Math.min(fluiditeAnimation + 10, fluidite_MAX);									// diminuer le nombre de pas par seconde
					wasAdjusted = true;
				}
				else if (fluiditeAnimation > fluidite_MIN && elapsedTimePerStep < fluiditeAnimation) {					// Si le temps est plus court que prevu
					fluiditeAnimation = Math.max(fluiditeAnimation - 5, fluidite_MIN);									// augmenter le nombre de pas par seconde
					wasAdjusted = true;
				}
				elapsedTime = null;
				if (wasAdjusted) {																						// Si la fluidite a ete ajustee...
					HundredTimesFluidite = 100 * fluiditeAnimation;
					myFadeinImg.fadein.trans_step = HundredTimesFluidite / myFadeinImg.fadein.trans_speed;				//... recalculer les pas de transparence
					myFadeinImg.fadeout.trans_step = - HundredTimesFluidite / myFadeinImg.fadeout.trans_speed;
					var myFadeoutImgFadeout = (this.getFadeOutImg()).fadeout;
					if (myFadeoutImgFadeout) myFadeoutImgFadeout.trans_step = - HundredTimesFluidite / myFadeoutImgFadeout.trans_speed;
					myFadeoutImgFadeout = null;
				    myDebugger.log('fluidity adjusted to ' + fluiditeAnimation);
				}
				wasAdjusted = null;
				this.lastFadeStepStart = (new Date()).getTime();
				this.fadeCounter = 0;
			}
		}
		else {
			this.lastFadeStepStart = (new Date()).getTime();
			this.fadeCounter = 0;
		}
	}
	
	/* Cette fonction echange les deux images */
	myDiv.swapImgs = function() {
		var myFadeoutImg = this.getFadeOutImg(), myFadeinImg = this.getFadeInImg(), outZIndex = myFadeoutImg.style.zIndex;
		myFadeoutImg.willFadeIn = true;									// Echange des roles
		myFadeinImg.willFadeIn = false;
		myFadeoutImg.style.zIndex = myFadeinImg.style.zIndex;			// Echange des z-index
		setTransp(myFadeoutImg, 0);										// Regler la transparence de l'image disparue a zero
		myFadeoutImg = null;
		myFadeinImg.style.zIndex = outZIndex;							// Echange des z-index
		outZIndex = null;
		this.bgDiv.style.backgroundColor = myFadeinImg.bgColor;			// Reglage de la couleur de fond
		this.showCaption(myFadeinImg.caption, myFadeinImg.poolCaption);	// Actualisation des legendes
	    myFadeinImg = null;
	}
	
	/* Cette fonction met a jour les legendes */
	myDiv.showCaption = function(myCaption, myPoolCaption) {
		if (myPoolCaption != null) {								// S'il y a une legende de pool, l'afficher et masquer la zone d'affichage inutilisee
			if (myPoolCaption.toH1) {
				this.showCaptionH1(myPoolCaption.text);
				this.hideCaptionSpan();
			}
			else {
				this.showCaptionSpan(myPoolCaption);
				this.hideCaptionH1();
			}
		}
		else this.hideCaption();									// sinon, masquer toutes les legendes
		if (myCaption != null) {									// S'il y a une legende d'image, l'afficher
			if (myCaption.toH1) this.showCaptionH1(myCaption.text);
			else this.showCaptionSpan(myCaption);
		}
	}
	
	/* Cette fonction affiche une legende dans le H1 */
	myDiv.showCaptionH1 = function(text) {
		if (capH1Elmnt != null) capH1Elmnt.innerHTML = text;
	}
	
	/* Cette fonction affiche une legende dans le bandeau d'affichage */
	myDiv.showCaptionSpan = function(myCaption) {
    	var capSpan = this.captionSpan, capStyle = capSpan.style;
    	capSpan.innerHTML = myCaption.text;				// Definir le texte
    	setTransp(capSpan, myCaption.transp);			// Definir la transparence
    	capSpan = null;
    	capStyle.display = 'block';						// Afficher le bandeau
    	capStyle.color = myCaption.textColor;			// Definir la couleur du texte
    	capStyle.backgroundColor =  myCaption.bgColor;	// Definir la couleur de fond
    	capStyle.top = myCaption.top;					// Definir la position
    	capStyle = null;
	}
	
	/* Cette fonction masque toutes les legendes */
	myDiv.hideCaption = function() {
		this.hideCaptionH1();	// Masquer la legende dans le H1
		this.hideCaptionSpan();	// Masquer le bandeau de legende
	}
	
	/* Cette fonction masque la legende du H1 */
	myDiv.hideCaptionH1 = function() {
		if (capH1Elmnt != null) capH1Elmnt.innerHTML = capH1InnerHTML;	// Remplacer le contenu du H1 par sa valeur initiale
	}
	
	/* Cette fonction masque le bandeau de legende */
	myDiv.hideCaptionSpan = function() {
		this.captionSpan.style.display = 'none';
	}
	
	myDiv.allSet = true;	// Marquer le div comme pret a recevoir une animation. Ceci evite de le preparer deux fois si deux diaporamas doivent s'executer sur le meme div cible
}

/* Cette fonction definit les style CSS des elements du div cible du diaporama */
diaporama.prototype.styleElmt = function(myElmntStyle, myZindex, w, h, parentDiv) {
	myElmntStyle.position = 'absolute';				// position absolute necessaire pour pouvoir fixer un z-index
    myElmntStyle.width = w;							// fixer la largeur
    myElmntStyle.height = h;						// fixer la hauteur
    myElmntStyle.zIndex = myZindex;					// fixer le plan
    myElmntStyle.left = '0px';						// positionner en haut a gauche
    myElmntStyle.top = '0px';
    myElmntStyle.display = 'none';					// Masquer l'element
}

diaporama.prototype.styleImg = function(myElmntStyle, myZindex, w, h, parentDiv) {
	myElmntStyle.position = 'absolute';							// position absolute necessaire pour pouvoir fixer un z-index
    myElmntStyle.zIndex = myZindex;								// fixer le plan
    myElmntStyle.left = Math.round(parseInt(w) * 0.5) + 'px';	// Centrer
    myElmntStyle.top = Math.round(parseInt(h) * 0.5) + 'px';
    myElmntStyle.marginLeft = '-999px';							// Positionner tres en haut et a gauche pour eviter l'apparition des ascenseurs
    myElmntStyle.marginTop = '-999px';
    myElmntStyle.display = 'none';								// Masquer l'element
}

/* Cette fonction demarre le diaporama */
diaporama.prototype.start = function(delay) {
	if (delay == null || isNaN(delay) || delay < 0) delay = 0;						// sanity check
	this.beserkCount = 0;															// voir deQueue()
	myMutex.add(this.targetDivID, true, (new Date()).getTime() + delay, this.id);	// demander le demarrage
}

/* Cette fonction arrete le diaporama */
diaporama.prototype.stop = function() {
	myMutex.add(this.targetDivID, false, (new Date()).getTime(), this.id);	// demander l'arret
}

/* Cette fonction est executee apres un arret du diaporama et permet de s'assurer que le prochain
** demarrage se fera au debut d'un pool */
diaporama.prototype.postStop = function() {
	this.deQueuedNotLoaded = null;									// Oublier la reference de l'image prioritaire dans le preloader
	if (this.poolCount > 1 || this.poolList[0].firstImg != null || this.poolList[0].lastImg != null) {// s'il y a plus d'un pool dans le diaporama, ou que l'unique pool a une image d'en-tete ou un intercalaire
		var picQueue = this.picQueue,
			picQueueLength = picQueue.length,
			currentPoolIndex = this.lastDequeuedPoolIndex,
			removeUpTo = -1;										// s'arranger pour reprendre au debut d'un nouveau pool d'images au prochain demarrage
		for (var j = 0; j < picQueueLength; j++) {					// noter l'index de la derniere image de ce pool en debut de queue
			if (picQueue[j].poolIndex == currentPoolIndex) removeUpTo++;
			else break;
		}
		picQueue = null;
		this.poolList[this.activePool].reset();						// re-initiliaser le pool actif pour qu'il redemarre du debut lors de sa prochaine sollicitation
		if (removeUpTo == picQueueLength - 1) {						// si toute la queue est composee d'images de ce pool
			this.picQueue = new Array();							// vider la queue
			if (this.activePool == currentPoolIndex && this.poolCount > 1)
				this.activePool = this.chooseNextPool();			// choisir le prochain pool
		}
		else if (removeUpTo > -1)
			this.picQueue.splice(0, removeUpTo + 1);				// sinon on enleve toutes les images du debut de la queue qui sont du meme pool que celui de la derniere image affichee
		picQueueLength = null; removeUpTo = null; currentPoolIndex = null;
		this.fillQueue();											// remplir a nouveau la queue
	}
}

/* Cette fonction retourne la prochaine image a afficher */
diaporama.prototype.deQueue = function() {
	var myPic = null, now = (new Date()).getTime();			// initialiser la variable a retourner
	if (now - this.lastDequeued < 50) this.beserkCount++;
	else this.beserkCount = 0;
	if (!this.justOnePic && this.beserkCount < 3) {			// si le diaporama n'a qu'une seule image, renvoyer null
		this.lastDequeued = now;							// marquer l'heure de cette dequeue
		myPic = this.picQueue.shift();						// extraire la prochaine image de la liste pre-selectionner
		if (!myPic.isLoaded && myPic.picsrc != interPool_ImgSRC)
			this.deQueuedNotLoaded = myPic;					// si l'image a dequeuee n'est pas encore chargee, garder un pointeur pour pouvoir lancer 'onload' plus tard et empechere sa garbage collection
		this.fillQueue();									// re-remplir la liste des pre-selections
		this.lastDequeuedPoolIndex = myPic.poolIndex;		// garder en memoire de quel pool est extrait la derniere image affichee
		myPreLoader.markUsed(myPic.picsrc, new Date());		// indiquer au preloader que cette image vient d'etre utilisee
	}
	else if (this.beserkCount > 2)							// si la derniere dequeue a eu lieu il y a moins de 50ms, c'est qu'il y a bug. Renvoyer null pour bloquer cette animation
		myDebugger.log('Beserk mode detected and blocked');
	/* Il y a un bug, je pense cree par une race condition rare et indefinie, qui entraine un emballement du script. Les fadings s'enchainent indefiniement
	** et a tres grande vitesse (plusieurs centaines par seconde). Une animation normale ne demande jamais un fading toutes les 50ms. On peut imaginer
	** une situation ou une preview est demandee puis annulee puis demandee a nouveau tres rapidement, auquel cas deux dequeues peuvent etre tres rapprochees,
	** mais le Mutex doit normallement prevenir ce genre de probleme. Donc il est quasi certain que si une animation demande plus de deux images en moins de
	** 50 millisecondes, elle merite la mort */
	/* PS: il y a une exceptionn lorsqu'on demarre un diaporama sans fading initial et que la premiere image est deja en cache, on a alors deux dequeues tres rapprochees
	** Je rajoute donc un beserkCount initialise dans start() */
	now = null;
	return myPic;
}

/* Cette fonction retourne la premiere image a afficher */
diaporama.prototype.deQueueStartPic = function() {
	var myPic = null;								// initialiser la variable a retourner
	if (this.justOnePic) myPic = this.picQueue[0];	// s'il n'y en a qu'une, c'est la une
	else myPic = this.deQueue();					// sinon renvoyer la premiere image de la pre-selection
	myPic.fadein = this.fadein;						// attribuer le fadein d'apparition du diaporama
	return myPic;
}

/* Cette fonction remplit la liste des images pre-selectionnees du diaporama */
diaporama.prototype.fillQueue = function() {
	if (this.justOnePic) {												// si le diaporama n'a qu'une seule image
		this.picQueue[0] = this.poolList[0].deQueue();					// simplement remplir la liste avec cette image
		this.picQueue[0].poolIndex = 0;									// tagger l'image obtenue avec l'index de son pool. C'est a priori inutile dans ce cas precis mais je prefere etre coherent avec le cas suivant
	}
	else {
		var myPool = this.poolList[this.activePool],
			myPic,
			queueDepth = this.queueDepth,
			picQueueLength = this.picQueue.length;
		while (picQueueLength < queueDepth) {							// tant que la liste n'a pas le nombre d'images voulu
			myPic = myPool.deQueue();									// demander la prochaine image au pool en cours
			if (myPic == null) {
				if (this.poolCount > 1) {
					this.activePool = this.chooseNextPool();			// s'il n'en a plus, choisir le prochain pool
					myPool = this.poolList[this.activePool];
				}
			}
			else {
				myPic.poolIndex = this.activePool;						// tagger l'image obtenue avec l'index de son pool
				picQueueLength = this.picQueue.push(myPic);				// l'ajouter a la liste
			}
		}
		queueDepth = null; myPool = null; myPic = null; picQueueLength = null;
	}
	myPreLoader.update(this.picQueue, this.id, this.deQueuedNotLoaded);	// Envoyer la liste au pre-loader pour qu'il mette a jour ses priorites et rajoute les nouvelles images
}

/* Le pre-loader appelle cette fonction lorsqu'une image vient d'etre chargee */
diaporama.prototype.loadedProgress = function(loadedURL) {
	var poolIndex = -1;
	if (this.deQueuedNotLoaded != null && loadedURL == this.deQueuedNotLoaded.picsrc) {	// S'il s'agit de l'image prioritaire
		this.deQueuedNotLoaded.markLoaded();											// La marquer comme chargee
		poolIndex = this.deQueuedNotLoaded.poolIndex;									// Noter son index de pool
		this.deQueuedNotLoaded = null;													// Oublier sa reference.
	}
	var myQueue = this.picQueue, myQueueCount = myQueue.length;
	for (var j = 0; j < myQueueCount; j++) {											// Rechercher l'image dans la queue
		if (myQueue[j].picsrc == loadedURL) {
			myQueue[j].markLoaded();													// La marquer comme chargee dans la queue
			poolIndex = myQueue[j].poolIndex;											// Noter son index de pool
		}
	}
	myQueue = null; myQueueCount = null;
	if (poolIndex >= 0) this.poolList[poolIndex].markLoaded(loadedURL);					// La marquer comme chargee dans le pool
	poolIndex = null;
}

/* Cette fonction indique si le diaporama est pret a defiler */
diaporama.prototype.isReady = function() {
	var itIs = true;											// Le diaporama est pret si le cache ne fonctionne pas
	if (myPreLoader.activeCache) {								// Si le cache fonctionne
		var myQueue = this.picQueue, myQueueCheck = Math.min(myQueue.length, 2);
		for (var j = 0; j < myQueueCheck; j++) {				// Verifier si les deux premieres images de la queue sont chargees
			if (!myQueue[j].isLoaded && myQueue[j].picsrc != interPool_ImgSRC) {
				itIs = false;									// L'image intercalaire peut ne pas etre chargee, le chargement sera rapide de toute facon vue sa petite taille
				notReadyURL = myQueue[j].picsrc;
				break;
			}
		}
	}
	return itIs;
}

/* Cette fonction choisit le prochain pool a utiliser */
diaporama.prototype.chooseNextPool = function() {
	var nextPool = null, currentPool = this.activePool, poolCount = this.poolCount;
	switch (this.poolMode) {
		case 'list':
			nextPool = currentPool + 1;													// en mode liste, choisir le pool suivant
			break;
		case 'random':
			nextPool = Math.floor(RandomNotOne() * poolCount);							// en mode random, le prochain pool est choisi au hasard
			if (nextPool == currentPool) nextPool++;									// si le hasard tire le pool actuellement actif, on choisit le prochain sur la liste
			break;
		case 'batch':																	// en mode batch, chaque pool doit avoir ete utilise un nombre de fois identique
    		var indexCandidates = new Array(), myPoolList = this.poolList,
				viewLimit = Math.floor(this.poolRotationCount / poolCount);				// determiner quel est ce nombre
    		for (var j = 0; j < poolCount; j++) {										// passer en revue tous les pools du diaporama
    			if (myPoolList[j].batchCount <= viewLimit)								// sauvegarder ceux qui ont ete affiches moins que necessaire dans un tableau
    				indexCandidates.push(j);
    		}
    		viewLimit = null;
    		var candidatesCount = indexCandidates.length;
    		if (candidatesCount == poolCount) {											// si tous les pools ont ete affiches le meme nombre de fois...
    			for (var j = 0; j < poolCount; j++) {									//... choisir celui qui est marque 'isFirst'
    				if (myPoolList[j].isFirst) { nextPool = j; break; }
    			}
		    	if (nextPool == null) {													//... s'il n'y a pas de 'isFirst', en choisir un au hasard
		    		nextPool = Math.floor(RandomNotOne() * poolCount);
		    		if (nextPool == currentPool) nextPool++;							//... si le hasard tire le pool actuellement actif, on choisit le prochain sur la liste
		    	}
    		}
    		else
    			nextPool = indexCandidates[Math.floor(RandomNotOne() * candidatesCount)];// sinon on en choisit un au hasard parmi ceux qui sont en deficit
    		indexCandidates = null; candidatesCount = null; myPoolList = null;
			this.poolRotationCount++;													// incrementer le compteur de rotations de pools
			break;
	}	
	currentPool = null;
	if (nextPool >= poolCount) nextPool = 0;											// Il arrive que l'on choisisse 'le prochain sur la liste'. S'il n'y avait pas de prochain, on prend le premier de la liste
	poolCount = null;
	return nextPool;
}

/* Cette fonction s'assure que la valeur renvoyee par Math.random() ne soit pas 1.0 */
function RandomNotOne() {
	var myDice = Math.random();
	if (myDice == 1) myDice -= Number.MIN_VALUE;
	return myDice;
}

/* Cette fonction marque toutes les images comme non-chargees
** Elle est appelee lorsque le cache a ete detecte comme plein */
diaporama.prototype.markAllUnloaded = function() {
	var diapPoolCount = this.poolCount, poolList = this.poolList;
	for (var j = 0; j < diapPoolCount; j++) {	// Marquer les images comme non chargees dans les pool
		poolList[j].markAllUnloaded();
	}
	diapPoolCount = null; poolList = null;
	var myQueue = this.picQueue, myQueueLength = myQueue.length;
	for (var j = 0; j < myQueueLengthh; j++) {	// Marquer les images comme non chargees dans la queue
		myQueue[j].isLoaded = false;
	}
	myQueue = null; myQueueLength = null;
}

/* Cette fonction enleve une image de la liste
** Elle est appelee lorsque le chargement de cette image a echoue */
diaporama.prototype.removePic = function(deadPicURL) {
	var poolList = this.poolList;
	for (var j = 0; j < this.poolCount; j++) {				// Enlever l'image de tous les pools
		poolList[j].removePic(deadPicURL);
		if (poolList[j].picCount == 0) {					// Si le pool n'a plus d'images
			poolList.splice(j, 1);							// Enlever le pool
			this.poolCount--;
			if (this.activePool == j) this.chooseNextPool();// Choisir un nouveau pool si le pool enleve etait le pool actif
			j--;
		}
	}
	if (poolList.length == 0) {								// Si le diaporama n'a plus de pool
		if (this.isPreview) {								// Enlever le delencheur sur les liens si c'est une preview
			var myAs = document.getElementsByTagName('a');
			if (myAs != null) {
				var myAsCount = myAs.length;
				for (var j = 0; j < myAsCount; j++) {
					if (myAs[j].diapID == this.id) {
						myAs[j].onmouseover = null;
						myAs[j].onmouseout = null;
						myAs[j].onclick = null;
						myAs[j].diapID = null;
						break;
					}
				}
				myAs = null; myAsCount = null;
			}
		}													// Enlever l'association avec le div cible si c'est un generique
		else document.getElementById(this.targetDivID).diapID = null;
		this.stop();										// L'arreter
		diaporamas[this.diapID] = null;						// L'enlever du tableau des diaporamas
	}
	this.justOnePic = (poolList.length == 1 && (poolList[0].picCount == 0 || (poolList[0].firstImg == null && poolList[0].picCount == 1)));
	poolList = null;
	var wasInQueue = false;
	for (var j = 0; j < this.picQueue.length; j++) {		// Enlever l'image de la queue
		if (this.picQueue[j].picsrc == deadPicURL) {
			this.picQueue.splice(j, 1);
			j--;
			wasInQueue = true;
		}
	}
	if (wasInQueue) this.fillQueue();						// Si l'image etait dans la queue, remplir la queue a nouveau
	wasInQueue = null;
}

/*************/
/*** Mutex ***/
/*************/
/* Initiallement, le but de cet objet etait de s'assurer qu'un start() ne pouvait
** pas etre appele pendant qu'un stop() etait encore en train de se terminer.
** L'enjeu etait d'eviter que des fade() orphelins soient crees apres avoir survole
** tres rapidement de nombreux liens preview. Dans ce cadre, l'objet etait bien, mais pas top
** (pas 100% efficace). La solution finale a ete de faire suivre
** l'ID du diaporama appelant dans toute la chaine de fade() (setNextImage -> scheduleNextFade -> fade)
** et de l'interrompre si le diaporama actif n'est plus le meme que le diaporama appelant.
** Cependant, cette classe Mutex permet d'intercepter
** et d'annuler des ordres antagonistes: si un arret est demande immediatement apres un depart,
** les deux ordres seront annules. En condition d'utilisation normale, je pense que l'overhead est
** assez minime donc le benefice net reste positif. */

/* Objet definissant une action temporisee (start ou stop) a accomplir */
function plannedAction(targetDivID, start, dateStart, diapID) {
	this.targetDivID = targetDivID;	// ID du div cible
	this.start = start;				// true si c'est un demarrage, false si c'est un arret
	this.dateStart = dateStart;		// date prevue pour l'action
	this.diapID = diapID;			// ID du diaporama demandeur
}

/* Cette fonction determine si deux actions sont antagonistes */
plannedAction.prototype.isAntagonist = function(diapID, start, dateStart) {
	return (this.diapID == diapID											// les deux actions sont demandees par le meme diaporama
			&& (
				(start && !this.start && dateStart <= this.dateStart)		// l'action 'this' est un arret, l'autre est un demarrage et this est planifiee apres l'autre
				|| (!start && this.start && this.dateStart <= dateStart)	// l'inverse (this <=> l'autre)
				)
			);
}

/* Cette fonction execute une action temporisee */
plannedAction.prototype.execute = function() {
	if (this.start) document.getElementById(this.targetDivID).start(diaporamas[this.diapID], this.dateStart);	// demarrage
	else {
		document.getElementById(this.targetDivID).stop();														// arret sur le div
		diaporamas[this.diapID].postStop();																		// traitement de la queue du diaporama arrete
	}
}

function Mutex() {
	this.queue = new Array();								// Liste des actions temporisees
	this.addings = 0;										// Nombre d'ajouts en cours
	this.running = false;									// Les actions sont-elles en train d'etre traitees (permet de s'assurer qu'il n'y a qu'une boucle de traitement)
	this.needsToRun = (this.addings == 0 && !this.running);	// Faut-il traiter les actions (aucun ajout en cours a la queue et pas de traitement deja en cours)
	this.buffer = 100;										// Tampon de temporisation (en millisec)
}

/* Cette fonction permet d'ajouter une action a la queue */
Mutex.prototype.add = function(targetDivID, start, dateStart, diapID) {
	this.addings++;																	// marquer qu'un ajout est en cours
	var antagonistIndex = -1, myQueue = this.queue, myQueueCount = myQueue.length;
	for (var j = 0; j < myQueueCount; j++) {										// verifier si nous avons un antagoniste deja dans la queue
		if (myQueue[j].isAntagonist(diapID, start, dateStart)) {
			antagonistIndex = j;
			myDebugger.log('Mutex: antagonist actions blocked');
			break;
		}
	}
	myQueueCount = null;
	if (antagonistIndex > -1) myQueue.splice(antagonistIndex, 1);					// s'il y avait un antagoniste, on peut l'enlever
	else myQueue.push(new plannedAction(targetDivID, start, dateStart, diapID));	// sinon on ajoute l'action
	antagonistIndex = null; myQueue = null;
	this.addings--;																	// marquer que l'ajout est termine
	this.ping();																	// reveiller la boucle de traitement
}

/* Cette fonction reveille la boucle de traitement des actions */
Mutex.prototype.ping = function() {
	var oInstance = this;
	if (oInstance.needsToRun) {													// si la voie est libre pour executer les actions
		oInstance.running = true;												// marquer que la boucle tourne
		window.setTimeout( function() { oInstance.start(); }, oInstance.buffer);// lancer l'execution dans buffer millisecondes
	}
}

/* Cette fonction execute les actions temporisees */
Mutex.prototype.start = function() {
	var myQueue = this.queue;
	while (myQueue.length > 0 && this.addings == 0) {	// tant qu'on a des actions et qu'aucun ajout n'est en cours
		myQueue.shift().execute();						// enlever la premiere action de la chaine et l'executer
	}
	this.running = false;								// la boucle ne troune plus
	myQueue = null;
}

/*******************************************/
/*** Fonctions gerant l'animation fading ***/
/*******************************************/
/* Cette fonction arrete le bubbling d'un evenement
** ce qui permet d'alleger la charge de calculs du script */
function stopPropag(e) {// http://www.quirksmode.org/js/events_order.html
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}

/* Cette fonction gere l'arrive de la souris sur un lien associe a une preview */
function linkMouseover(e) {
	stopPropag(e);						// arrete le bubbling
	diaporamas[this.diapID].start(0);	// demarre le diaporama associe
}

/* Cette fonction gere la sortie de la souris d'un lien associe a une preview */
function linkMouseout(e) {
	stopPropag(e);					// arrete le bubbling
	diaporamas[this.diapID].stop();	// arrete le diaporama associe
}

/* Cette fonction gere le click sur un lien associe a une preview
** Il est preferable d'arreter le diaporama. S'il n'est pas arrete
** et que l'utilisateur utilise la fonction 'Back' du navigateur,
** il reviendra sur la page avec la preview qui tourne toujours,
** meme si la souris n'est plus au-desus du lien... */
function linkClick(e) {
	this.onmouseout(e);		// simule le depart de la souris du lien
	/* Deux options sont possibles: soit la variable globale backTransition a la valeur 'fade',
	** et dans ce cas, si l'utilisateur fait usage de la fonction 'Back' du navigateur, il verra
	** a son retour sur la page la transition qu'il aurait vu s'il avait simplement enleve la souris
	** du lien (animation de retour vers le diaporama generique). Soit backTransition a une autre valeur,
	** et le retour sur la page se fera directement sur le diaporama generique (pas de transition). */
	if (backTransition != 'fade') {
		var myDiv = document.getElementById(diaporamas[this.diapID].targetDivID);
		myDiv.getFadeOutImg().fadeout = new picFadeout(0, 1, img_FadeIn);
		myDiv.getFadeInImg().fadein = new picFadein(0, 1, img_FadeOut);
		myDiv = null;
	}
}

/* Cette fonction change la transparence d'un objet
** Les arguments passes sont l'objet et la difference avec sa transparence actuelle */
function ChangeTransp(elmnt, transpDiff) { return setTransp(elmnt, getTransp(elmnt) + transpDiff); }

/* Cette fonction determine la transparence actuelle d'un objet */
function getTransp(elmt) { return 100 * elmt.style.opacity; }

/* Cette fonction change la transparence d'un objet
** Les arguments passes sont l'objet et la nouvelle transparence souhaitee */
function setTransp(elmt, transp) {
    if (transp < 0) transp = 0;										// Transparence negative impossible, remplacer par zero
    else if (transp > 100) transp = 100;							// Transparence superieure a 100 impossible, remplacer par 100
    var eStyle = elmt.style;
    eStyle.opacity = transp / 100;									// CSS 3
    if (elmt.filters)
    	eStyle.filter = 'alpha(opacity=' + parseInt(transp) + ')';	// Internet Explorer
    eStyle = null;
    return transp;
/*  eStyle.MozOpacity = transp / 100;								// Firefox 0.9- (Mozilla 1.7-, juin 2004)
    eStyle.KhtmlOpacity = transp / 100;								// Safari 1.1 (octobre 2003)
    eStyle.webkitOpacity = transp / 100;							// Safari 1.2 (fevrier 2004)*/
}

/************************************************/
/*** Objet gerant la mise en cache des images ***/
/************************************************/
function preLoadedPic(url, diapID, queuePos) {						// une image pre-chargee ou a pre-charger
	this.url = url;													// son url
	this.queuePositions = new Array(new Array(diapID, queuePos));	// sa position dans la queue des differents diaporamas

	this.lowestQueuePosition = queuePos;							// la position la plus petite tous diaporamas confondus
	this.pic = new Image();											// l'objet Image servant au prechargement
	this.isLoading = false;											// marqueur de chargement
	this.isLoaded = false;											// vrai si l'image a ete chargee
	this.exists = true;												// faux si le chargement a echoue
	this.lastAccess = new Date(2030,12);							// dernier acces a cette image
}

/* Cette fonction ajoute ou met a jour la liste des positions de cette image dans les queues des diaporamas */
preLoadedPic.prototype.insertQueuePositions = function(diapID, queuePos) {
	if (!this.updateQueuePositions(diapID, queuePos))	// Si la position dans ce diaporama n'etait pas connue, l'ajouter et actualiser la position la plus faible
	{
		this.queuePositions.push(new Array(diapID, queuePos));
		this.lowestQueuePosition = Math.min(this.lowestQueuePosition, queuePos);
	}
}

/* Cette fonction met a jour la liste des positions de cette image dans les queues des diaporamas */
preLoadedPic.prototype.updateQueuePositions = function(diapID, queuePos) {
	var qpLength = this.queuePositions.length, myLowestQueue = 999, PosArray, foundIt = false;
	for (var j = 0; j < qpLength; j++) {						// Faire une boucle sur le tableau des positions pour recherche la position a mettre a jour et determiner la position la plus petite
		PosArray = this.queuePositions[j];
		if (PosArray[0] == diapID) {
			PosArray[1] = queuePos;
			myLowestQueue = Math.min(myLowestQueue, queuePos);
			foundIt = true;
		}
		else myLowestQueue = Math.min(myLowestQueue, PosArray[1]);
	}
	qpLength = null; PosArray = null;
	if (foundIt) this.lowestQueuePosition = myLowestQueue;		// Si la mise a jour a ete faite, actualiser la position la plus faible
	myLowestQueue = null;
	return foundIt;
}

/* Cette fonction pre-charge une image */
preLoadedPic.prototype.load = function() {
	var oInstance = this, myPic = oInstance.pic;
	oInstance.isLoading = true;								// Marquer que le chargement est en cours
	myPic.onload = function() { oInstance.markLoaded(); };	// Prevoir un temoin de fin de chargement
	myPic.onerror = function() { oInstance.markInvalid(); };// Prevoir un temoin de fin de chargement pour cause d'erreur
	myPic.onabort = function() { oInstance.clearEvents(); };// Prevoir un temoin de fin de chargement pour cause d'abandon
	oInstance.touch(new Date());							// Mettre a jour la derniere date d'acces
	myPic.src = oInstance.url;								// Lancer le chargement
	myPic = null;
}

/* Temoin de fin de chargement de l'image */
preLoadedPic.prototype.markLoaded = function() {
	this.clearEvents();												// vider les evenements enregistres
	this.isLoaded = true;											// marquer l'image comme chargee
	this.isLoading = false;											// marquer la fin du chargement
	myPreLoader.loadReport();										// Prevenir le pre-loader de la fin du chargement
	var queuePositions = this.queuePositions;
	for (var j = 0; j < queuePositions.length; j++) {
		diaporamas[queuePositions[j][0]].loadedProgress(this.url);	// Indiquer a tous les diaporamas concernes que l'image est prete
	}
	queuePositions = null;
}

/* Temoin de fin de chargement de l'image pour cause d'erreur */
preLoadedPic.prototype.markInvalid = function() {
	this.markLoaded();						// fin du chargement
	this.exists = false;					// marquer l'image comme inexistante sur le serveur
	var diapCount = diaporamas.length;
	for (var j = 0; j < diapCount; j++) {	// Enlever cette image des diaporamas
		diaporamas[j].removePic(this.url);
	}
	diapCount = null;
	myDebugger.log('Could not load ' + this.url);
}

/* Cette fonction met a jour la derniere date d'acces */
preLoadedPic.prototype.touch = function(accessDate) {
	this.lastAccess = accessDate;
}

/* Cette fonction vide les evenements enregistres */
preLoadedPic.prototype.clearEvents = function() {
	var myPic = this.pic;
	myPic.onload = null;
	myPic.onerror = null;
	myPic.onabort = null;
	myPic.loadReporter = null;
	myPic = null;
}

/* Objet de pre-chargement */
function preLoader() {
	this.cachedList = new Array();			// Liste des images pre-chargees ou a pre-charger
	this.loadedCountTarget = 99;			// Nombre d'images que peut contenir le cache
	this.concurrentDownloads_MAX = 5;		// Nombre maximum de pre-chargements en parallele
	/* Sur ma machine de test, le gain de vitesse est proportionnel entre 1 et 5 telechargements en parallele (avec 5, on va 5x plus vite)
	** puis ca plafonne au-dela (avec 10, on va seulement 9% plus vite qu'avec 5). Comme l'augmentation du nombre de telechargements en parallele
	** se fait sentir sur les machines avec un processeur un peu faible (mon eee par exemple, mais pas ma machine de test), autant plafonner a 5 */
	this.concurrentDownloads_MIN = 1;		// Nombre minimum de pre-chargements en parallele
	this.concurrentDownloads = 2;			// Nombre de pre-chargements en parallele autorises
	this.activeDownloads = 0;				// Nombre de pre-chargements en cours
	this.webLoadTime = 999;					// Duree necessaire au telechargement de l'image intercalaire (ms)
	this.cacheLoadTime = 5;					// Duree necessaire a la lecture de l'image intercalaire depuis le cache (ms)
	this.activeCache = true;				// Le cache fonctionne-t-il?
	this.cacheLoadMax = this.webLoadTime;	// Au-dessus de quel temps de chargement considere-t-on que le chargement n'a pas pu etre fait depuis le cache
	this.lastCheckerStart = null;			// Marqueur du debut de la derniere boucle de verification de cache plein (=> estimation de la charge processeur)
	this.checkerLoop = 2000;				// Frequence des verifications de cache plein (en millisecondes)
	this.skipFirstms = 10000;				// Le verificateur de cache plein ne demarrera pas avant tant de ms
	this.isLoading = false;					// Permet de s'assurer qu'une seule instance de startLoading() s'execute a la fois
	this.lastDLBoost = (new Date).getTime();// Date de la derniere augmentation du nombre de charegements paralleles autorises
	this.DLBoostTempo = 1500;				// Le script n'augmentera pas le nombre de telechargements paralleles plus d'une fois toutes les DLBoostTempo millisecondes.
	/* Chez moi, le telechargement d'une image prend en gros une seconde, donc j'attends qu'environ un cycle de telechargement
	** soit effectue avant d'envisager une augmentation */
	
	this.setSpeeds(interPool_ImgSRC + '?' + (new Date()).getTime(), 'web');	// Mesurer les valeurs reelles de webLoadTime et cacheLoadTime
}

/* Cette fonction mesure les temps respectifs de chargement de l'image intercalaire depuis le web et depuis le cache */
preLoader.prototype.setSpeeds = function(url, webOrCache) {
	if (!this.picCheck) {																// Si c'est le premier appel a cette fonction
		var oInstance = this;
		oInstance.picCheck = new Image();												// Definir l'objet image temoin
		oInstance.picCheck.onload = function() { oInstance.setSpeeds(url, 'web'); };	// Lui associer un marqueur de fin de chargement
		oInstance.picCheck.startDL = (new Date()).getTime();							// Marquer la date du debut du chargement
		oInstance.picCheck.src = url;													// Le charger
	}
	else if (webOrCache == 'web') {														// Si c'est le deuxieme appel a cette fonction (fin du chargement depuis le web)
		var oInstance = this;
		oInstance.webLoadTime = (new Date()).getTime() - oInstance.picCheck.startDL;	// Mettre a jour webLoadTime
		oInstance.cacheLoadMax = oInstance.webLoadTime;									// Mettre a jour cacheLoadMax
		oInstance.picCheck = null;
		oInstance.picCheck = new Image();												// Re-initialiser l'image
		oInstance.picCheck.onload = function() { oInstance.setSpeeds(url, 'cache'); };	// Lui associer un marqueur de fin de chargement
		oInstance.picCheck.startDL = (new Date()).getTime();							// Marquer la date du debut du chargement
		oInstance.picCheck.src = url;													// Charger a nouveau la meme image
		myDebugger.log('web access time: ' + oInstance.webLoadTime + 'ms');
	}
	else {
		this.cacheLoadTime = (new Date()).getTime() - this.picCheck.startDL;			// Si c'est le troisieme appel a cette fonction (fin du chargement depuis le cache)
		delete this.picCheck;															// Effacer l'objet image
		myDebugger.log('cache access time: ' + this.cacheLoadTime + 'ms');
		if (this.cacheLoadTime >= this.cacheLoadMax) this.disable();
		else this.startFullCacheChecker();												// S'il est fonctionnel, demarrer la boucle de verification de cache plein
	}
}

/* Cette fonction desactive le preloader */
preLoader.prototype.disable = function() {
	this.activeCache = false;
	this.cachedList = new Array();
	this.concurrentDownloads_MAX = 0;
	this.concurrentDownloads = 0;
	this.loadedCountTarget = 0;
	this.stopFullCacheChecker();
	myDebugger.log('<b>Browser cache not working!</b>');
}

/* Cette fonction met a jour la liste des images a pre-charger en fonction de la nouvelle queue d'un diaporama */
preLoader.prototype.update = function(newQueue, diapID, priorityPic) {
	if (this.activeCache) {													// Si le cache fonctionne
		var newQueueCount = newQueue.length, singletonArray = new Array(), picSrc = newQueue[0].picsrc, singletonArrayLength = 0, priorityPicSrc = '';
		if (priorityPic != null) priorityPicSrc = priorityPic.picsrc;
		if (newQueueCount > 1) {
			for (var j = 1; j < newQueueCount; j++) {						// Enlever les duplicats de la queue en ne gardant que la premiere repetition
				if (picSrc != null) {
					singletonArray[j-1] = picSrc;
					singletonArrayLength = j;
				}
				picSrc = newQueue[j].picsrc;
				for (var k = 0; k < singletonArrayLength; k++) {
					if (picSrc == singletonArray[k]) { picSrc = null; break; }
				}
			}
			if (picSrc != null) {
				singletonArray[j-1] = picSrc;
				singletonArrayLength = j;
			}
		}
		else {
			singletonArray[0] = picSrc;
			singletonArrayLength = 1;
		}
		newQueueCount = null;
		var cachedPic, cachedPicUrl, foundIt = false, cachedList = this.cachedList, isPriority = false;
		for (var j = 0; j < cachedList.length; j++) {						// Mettre a jour les informations de la liste des images a pre-charger
			foundIt = false;
			cachedPic = cachedList[j];
			cachedPicUrl = cachedPic.url;
			isPriority = (cachedPicUrl == priorityPicSrc);
			if (isPriority) myDebugger.log(getPicName(priorityPicSrc) + ' is a priority');
			for (var k = 0; k < singletonArrayLength; k++) {
				picSrc = singletonArray[k];
				if (picSrc != null && cachedPicUrl == picSrc) {
					foundIt = true;
					if (isPriority) cachedPic.insertQueuePositions(diapID, -1);
					else cachedPic.insertQueuePositions(diapID, k);
					if (k == singletonArrayLength - 1) {
						singletonArray.length--;
						singletonArrayLength--;
						while (singletonArrayLength > 0 && singletonArray[singletonArrayLength - 1] == null) {
							singletonArray.length--;
							singletonArrayLength--;
						}
					}
					else singletonArray[k] = null;
					break;
				}
			}
			if (!foundIt) {
				if (isPriority) cachedPic.updateQueuePositions(diapID, -1);
				else cachedPic.updateQueuePositions(diapID, 999);
			}
		}
		foundIt = null; picSrc = null; cachedPic = null; cachedList = null; cachedPicUrl = null; isPriority = null;
		var GotNewOnes = false;
		for (var j = 0; j < singletonArrayLength; j++) {					// Ajouter les images de cette queue qui ne figuraient pas deja dans la liste des images a pre-charger
			if (singletonArray[j] != null) {
				this.cachedList.push(new preLoadedPic(singletonArray[j], diapID, j));
				GotNewOnes = true;
			}
		}
		singletonArray = null; singletonArrayLength = null;
		this.cachedList.sort(prioritySort);									// Classer la liste par ordre de priorite
		if (GotNewOnes) {													// Si de nouvelles images ont ete ajoutees, s'assurer que le nombre max d'images n'est pas depasse
			var oInstance = this;
			oInstance.cachedList.length = Math.min(oInstance.cachedList.length, oInstance.loadedCountTarget);
			window.setTimeout(function() { oInstance.startLoading(); }, 1);	// et redemarrer le pre-chargement s'il etait arrete. startLoading peut prendre du temps, setTimeout permet de liberer le thread immediatement, ce qui peut etre critique lorsqu'un diaporama appelle fillQueue() via postStop()
		}
		GotNewOnes = null;
	}
	//myDebugger.log(this.dumpCachedList());
}

/* Cette fonction permet de classer deux images suivant leur priorite */
function prioritySort(pic1, pic2) {
	if (pic1.lowestQueuePosition < pic2.lowestQueuePosition) return -1;
	else if (pic1.lowestQueuePosition == pic2.lowestQueuePosition) return 0;
	else return 1;
}

/* Cette fonction permet a un diaporama d'alerter le preloader qu'une image vient d'etre mise dans le DOM */
preLoader.prototype.markUsed = function(url, accessDate) {
	var myPic;
	for (var j = 0; j < this.cachedList.length; j++) {	// Recherche cette image dans la liste
		myPic = this.cachedList[j];
		if (myPic.url == url) {
			myPic.touch(accessDate);					// Mettre a jour sa date d'acces
			break;
		}
	}
	myPic = null;
}

/* Cette fonction permet a une image d'alerter le preloader qu'elle vient de se precharger */
preLoader.prototype.loadReport = function() {
	this.activeDownloads = Math.max(this.activeDownloads - 1, 0);	// indiquer que le nombre de chargements a diminue
	this.startLoading();											// commencer de nouveaux chargements
}

/* Cette fonction lance des pre-chargements d'image */
preLoader.prototype.startLoading = function() {
	if (!this.isLoading) {																						// s'assurer qu'un autre startLoading() n'est pas deja en train de tourner
		this.isLoading = true;																					// marquer qu'un startLoading() est en train de tourner
		var cachedListIndex = 0, myPic;
		while (this.activeDownloads < this.concurrentDownloads && cachedListIndex < this.cachedList.length) {	// tant que toutes les images ne sont pas chargees et que le nombre de chargements parralleles n'est pas sature
			myPic = this.cachedList[cachedListIndex];															// selectionner la prochaine image par ordre de prioirte
			if (!myPic.isLoaded && !myPic.isLoading && myPic.exists) {
				this.activeDownloads++;																			// incrementer le compteur de chargements en cours
				myPic.load();																					// charger l'image
			}
			cachedListIndex++;
		}
		this.isLoading = false;
		myPic = null; cachedListIndex = null;
		if (this.activeDownloads == this.concurrentDownloads) this.speedUP();									// Si l'on atteint la capacite maximum de chargements paralleles, essayer de repousser la limite
	}
}

/* Cette fonction diminue la pression du preloader sur le processeur */
preLoader.prototype.lowerCPUUsage = function() {
	if (this.concurrentDownloads > this.concurrentDownloads_MIN) {		// Si l'on a pas deja atteint le plancher du nombre de chargements paralleles
		this.concurrentDownloads_MAX = this.concurrentDownloads - 1;	// Diminuer le nombre maximum autorise au nombre de chargements en cours actuellement
		myDebugger.log('Concurrent DLs capped at ' + this.concurrentDownloads_MAX);
		this.concurrentDownloads = this.concurrentDownloads_MIN;		// Et abaisser le nombre de chargements en cours au minimum
	}
}

/* Cette fonction accelere le preloader */
preLoader.prototype.speedUP = function() {			// Si l'on est pas au nombre maximum de chargements paralleles et que la derniere augmentation etait il y a suffisament longtemps
	if (this.concurrentDownloads < this.concurrentDownloads_MAX && (new Date()).getTime() - this.lastDLBoost > this.DLBoostTempo) {
		this.lastDLBoost = (new Date()).getTime();	// Marquer la date de cette augmentation
		this.concurrentDownloads++;					// Augmenter le nombre autorise
		myDebugger.log('Concurrent DLs boosted to ' + this.concurrentDownloads);
	}
}

/* Cette fonction verifie si le cache est plein */
preLoader.prototype.fullCacheChecker = function() {
	var now = (new Date()).getTime(), lastCheckerStart = this.lastCheckerStart;
	this.lastCheckerStart = now;							// Diminuer la pression du preloader sur le processeur si celui-ci est surcharge
	if (now - lastCheckerStart >= this.checkerLoop * CPUOverUsedFactor)
		this.lowerCPUUsage();								// Si le processeur est sature, diminuer le nombre de chargements parralleles
	lastCheckerStart = null;
	if (now - loadPageTime.getTime() > this.skipFirstms && this.cachedList.length > 0 && !this.imgCacheChecker) {
		var myPic, myOldestPic;
		for (var j = 0; j < this.cachedList.length; j++) {	// Rechercher quelle image a ete utilisee le plus anciennement. On exclue interPool_ImgSRC qui a plus de chance d'etre exclue du cache de part sa petite taille
			myPic = this.cachedList[j];
			if ((!myOldestPic || myPic.lastAccess < myOldestPic.lastAccess) && myPic.isLoaded && myPic.exists && myPic.url != interPool_ImgSRC) myOldestPic = myPic;
		}
		myPic = null;
		if (myOldestPic) {
			myOldestPic.touch(new Date());					// Indiquer que cette image est utilisee maintenant
			var myImg = new Image(), oInstance = this;		// Preparer un marqueur de fin de chargement
			myImg.onload = function() { oInstance.cacheCheckerReporter(); };
			this.imgCacheChecker = myImg;					// Garder un pointeur vers cette image
			myImg.startDL = (new Date()).getTime();			// Sauver la date du debut du chargement
			myImg.src = myOldestPic.url;					// Charger l'image
			myOldestPic = null;
		}
	}
	now = null;
}

/* Cette fonction demarre le verificateur de cache plein */
preLoader.prototype.startFullCacheChecker = function() {
	var oInstance = this;
	oInstance.lastCheckerStart = (new Date()).getTime();
	this.fullCacheCheckerID = window.setInterval(function() { oInstance.fullCacheChecker(); }, oInstance.checkerLoop);
}

/* Cette fonction arrete le verificateur de cache plein */
preLoader.prototype.stopFullCacheChecker = function() {
	if (this.fullCacheCheckerID != null) {
		window.clearInterval(this.fullCacheCheckerID);
		this.fullCacheCheckerID = null;
	}
}

/* Marquer de fin de chargement de l'image test */
preLoader.prototype.cacheCheckerReporter = function() {
	var DLTime = (new Date()).getTime() - this.imgCacheChecker.startDL;
	var picsrc = this.imgCacheChecker.src;
	this.imgCacheChecker = null;
	if (DLTime > this.cacheLoadMax) {										// Si le temps de chargement indique que cette image n'etait plus en cache
		this.stopFullCacheChecker();										// Arreter le verificateur de cache plein
		myDebugger.log('<b>Cache is full</b> ' + getPicName(picsrc) + ' took ' + DLTime + 'ms to download');
		var diapCount = diaporamas.length;
		for (var j = 0; j < diapCount; j++) {								// Marquer toutes les images comme non chargees dans les diaporamas
			diaporamas[j].markAllUnloaded();
		}
		diapCount = null;
		this.loadedCountTarget = Math.floor(this.cachedList.length * 0.9);	// Diminuer le nombre d'images du preloader
		if (this.loadedCountTarget > 0) {
			this.cachedList.length = this.loadedCountTarget;				// Elaguer la liste
			for (var j = 0; j < this.cachedList.length; j++) {				// Marquer toutes les images comme non chargees
				this.cachedList[j].isLoaded = false;
			}
			this.startLoading();											// Demarrer les pre-chargements
			this.startFullCacheChecker();									// Redemarrer le verificateur de cache plein
			myDebugger.log('Cache depth lowered to ' + this.loadedCountTarget + ' files');
		}
		else this.disable();
	}
	picsrc = null;
	DLTime = null;
}

/******************************/
/*** Fonctions correctrices ***/
/******************************/
/* A cause de problemes d'arrondis, il arrive que fadeinpic soit decalee de 1px de sa position souhaitee.
** Ceci est cause par le fait que 1/ body est positionne en absolute avec top:50% (d'ou l'arrondi) et 2/ que fadeinpic est positionee
** en absolute aussi. En fonction de la taille du viewport, les deux arrondis ne coincident pas forcement.
** http://www.sohtanaka.com/web-design/1px-background-alignment-bug/ */
function ReCentrer() {
	var bodyDiv = document.getElementById('body');
	if (bodyDiv != null && typeof window.innerHeight != 'undefined') {
		bodyDiv.style.top = Math.round(window.innerHeight * 0.5) + 'px';
		if (window.addEventListener) window.addEventListener('resize', ReCentrer, false);	//Firefox
		else if (document.attachEvent) document.attachEvent('onresize', ReCentrer);			//Internet Explorer
	}
	bodyDiv = null;
}

/* Fenetre de debuggage */
function debuggerObj() {			// Creation du div
	if (debugging) {
		var myElmnt = document.createElement('div'), myElmntStyle = myElmnt.style;
		myElmntStyle.position = 'absolute';
		myElmntStyle.top = '5px';
		myElmntStyle.left = '5px';
		myElmntStyle.zIndex = 99;
		myElmntStyle.display = 'block';
		myElmntStyle.width = '300px';
		myElmntStyle.height = '99px';
		myElmntStyle.overflow = 'auto';
		myElmntStyle.backgroundColor = 'black';
		myElmntStyle.color = 'white';
		myElmntStyle.border = 'solid 1px white';
		myElmntStyle.fontSize = '0.6em';
		myElmntStyle.textAlign = 'left';
		myElmntStyle = null;
		document.body.appendChild(myElmnt);
		this.reporterDiv = myElmnt;		// Garder un pointeur vers le div
		myElmnt = null;
	}
}

/* Cette fonction permet d'afficher un message dans la fenetre */
debuggerObj.prototype.log = function(str) {
	if (this.reporterDiv) {											// Si le div existe
		var myElmnt = document.createElement('p');					// Rajouter un <p> contenant le texte
		myElmnt.innerHTML = str;
		this.reporterDiv.appendChild(myElmnt);
		myElmnt = null;
		this.reporterDiv.scrollTop = this.reporterDiv.scrollHeight;	// Faire defiler la fenetre vers le bas
	}
}

/* Cette fonction permet d'afficher un message de chronometrage dans la fenetre */
debuggerObj.prototype.logTimer = function(str) {
	str += ': t+' + (new Date().getTime() - loadPageTime.getTime()) + 'ms';
	this.log(str);
}

preLoader.prototype.dumpCachedList = function() { // for debugging
	var str = '<hr />';
	for (var j = 0; j < this.cachedList.length; j++) {
		str += getPicName(this.cachedList[j].url) + ' (';
		for (var k = 0; k < this.cachedList[j].queuePositions.length; k++) {
			str += 'DiapID: ' + this.cachedList[j].queuePositions[k][0];
			str += ' pos: ' + this.cachedList[j].queuePositions[k][1] + '; ';
		}
		str += this.cachedList[j].lowestQueuePosition + ') Loaded: ' + this.cachedList[j].isLoaded + '<br />'
	}
	return str + '<hr />';
}

diaporama.prototype.queueDump = function() { // for debugging
	var str = 'Queue dump:<br />';
	for (var j = 0; j < this.picQueue.length; j++) {
		str += getPicName(this.picQueue[j].picsrc) + ': ' + this.picQueue[j].isLoaded + '<br />';
	}
	myDebugger.log(str);
	str = null;
}

function getPicName(url) {
	return url.substring(url.lastIndexOf('/') + 1);
}