front_idill/src/mosaic/js/mosaic.js
changeset 52 277c94533395
parent 47 4e1ee94d70b1
child 55 afd60399a7b5
--- a/front_idill/src/mosaic/js/mosaic.js	Mon Jul 23 10:52:41 2012 +0200
+++ b/front_idill/src/mosaic/js/mosaic.js	Mon Jul 23 16:59:35 2012 +0200
@@ -21,291 +21,584 @@
  * Classe définissant la mosaïque.
  * Elle contient sa longueur, le nombre d'images total, une liste d'urls pour les vidéos, leurs snapshots principaux et leur position.
  * Contient également les dimensions en px de la mosaïque.
+ * Est appelé dans :
+ * la page d'index, afin de créer la mosaique.
  */
-function mosaic(config, default_conf)
+function Mosaic(config, default_conf)
 {
-	//Interactions souris/kinect.
-	this.mouseInteractions = true;
-	//Interactions avec/sans préphase.
-	this.prephaseEnabled = true;
-	
-	this.gestures = ["fall", "jump", "circle", "screw", "bend", "arc", "pendulum", "knee-up", "right-angle", "wave", "slow", "hello", "no-motion", "wheel", "contact", "run", "up-down", "grand-jete"];
+    this.gestures = ["fall", "jump", "circle", "screw", "bend", "arc", "pendulum", "knee-up", "right-angle", "wave", "slow", "hello", "no-motion", "wheel", "contact", "run", "up-down", "grand-jete"];
 
-	//Chemin du fichier de configuration.
-	this.config_path = config;
-	//Configuration par défaut en cas de valeur erronnée.
-	this.default_config = default_conf;
-	this.config = new Object();
-	//Tableaux des urls des vidéos, des snapshots et de leur position dans la mosaïque.
-	this.videos = [];
-	this.urls = [];
-	this.sources = [];
-	this.imgs = [];
-	this.opacities = [];
-	this.timeToGoAt = [];
-	this.ids = [];
-	this.fillingIds = [];
-	this.currentRandomVideoIdx = 0;
-	
-	//Dictionnaire pour les courbes de recherche.
-	this.dictionary = [];
-	
-	//Dernières positions des pointeurs.
-	this.mainPointerLastX;
-	this.mainPointerLastY;
-	this.secondPointerLastX;
-	this.secondPointerLastY;
-	//Dernières positions avant le lancement de la fonction d'idle.
-	this.mainPointerIdleStartX;
-	this.mainPointerIdleStartY;
-	this.secondPointerIdleStartX;
-	this.secondPointerIdleStartY;
-	
-	//Coordonnées de la souris dans le mode d'interaction souris.
-	this.mousePosX;
-	this.mousePosY;
-	//Coordonnées précédentes de la souris dans le mode d'interaction souris.
-	this.mousePosLastX;
-	this.mousePosLastY;
-	//Valeur du déplacement entre un mouse up et un mouse down.
-	this.mouseUpDownDelta = 0;
-	
-	//Dimensions de la mosaïque en pixels.
-	this.width;
-	this.height;
-	//Dimensions d'un snapshot en pixels.
-	this.snapshotWidth;
-	this.snapshotHeight;
-	//Espacement entre les snapshots en pixels.
-	this.marginWidth;
-	
-	//Booléens permettant ou non certaines intéractions selon le contexte.
-	this.zoomed = false;
-	this.fullscreen = false;
-	this.canMoveToNeighbour = false;
-	this.mainPointerExitBorder = false;
-	this.secondPointerExitBorder = false;
-	this.isMainPointerDisplayed = false;
-	this.isSecondPointerDisplayed = false;
-	this.helpDisplayed = false;
-	//Indique si l'utilisateur a manuellement pausé la vidéo.
-	this.userPaused = false;
-	//Indique si on est en train de se déplacer vers un voisin.
-	this.currentlyMoving = false;
-	//Indique si on est en train de dézoomer vers la mosaïque.
-	this.currentlyUnzooming = false;
-	//Indique si on peut s'approcher de kinect.
-	this.canStart = false;
-	//Indique si on est actuellement sur un snapshot.
-	this.isOnASnapshot = false;
-	//Indique si l'idle des pointeurs est disponible (deux mains détectées).
-	this.pointersIdleAvailable = false;
-	//Indique si le timeout pour l'idle des pointeurs à besoin d'être lancé.
-	this.pointersIdleNeedLaunch = false;
-	//Indique si les deux mains sont là.
-	this.areBothPointersHere = false;
-	//Indique si le timeout pour la détection de deux pointeurs a été lancé.
-	this.areBothPointersTimeoutLaunched = false;
-	//Indique si la mosaïque a été filtrée.
-	this.isMosaicFiltered = false;
-	//Indique si on est actuellement dans une recherche par gesture.
-	this.isCurrentlyInASearchByGesture = false;
-	//Indique si un pointeur est déjà sur une notification de recherche par gesture.
-	this.alreadyOnNotification = false;
-	//Si on a fait un swipe.
-	this.isSwipe = false;
-	//On peut swiper.
-	this.canSwipe = false;
-	//On passe vers une autre video automatiquement à la fin d'une lecture.
-	this.autoMove = false;
-	//Si l'utilisateur a demandé à sélectionner la TL.
-	this.isTLRequested = false;
-	//Le pointeur gauche a sélectionné la TL.
-	this.isTLSelectedBySecondPointer = false;
-	//Le pointeur droit a sélectionné la TL.
-	this.isTLSelectedByMainPointer = false;
-	//On peut afficher l'aide.
-	this.canNotifyHelp = false;
-	//Indique si la mosaique est en train d'être filtrée.
-	this.isMosaicFiltering = false;
-	this.arrowLeftLoading = false;
-	this.arrowRightLoading = false;
-	this.arrowUpLoading = false;
-	this.arrowDownLoading = false;
-	//On est dans une recherche par courbes.
-	this.isSearchByCurvesOn = false;
-	this.canDrawNextCurve = false;
-	//Dans le mode d'interaction souris, indique si on se situe actuellement sur un snapshot entièrement prézoomé.
-	this.isOnAPrezoomSN = false;
-	//Indique si une courbe de recherche donne au moins un résultat.
-	this.curvesGesturesFound = false;
-	//Indique si on souhaite supprimer la recherche en cours.
-	this.gestureDelRequested = false;
-	//Code de gesture actuellement calculé par les détecteurs de courbes.
-	this.actualCode = '';
-	//Indique si l'utilisateur est entré dans la zone de recherche.
-	this.isUserInSearchZone = false;
-	
-	//Timeout (attente) pour le zoom après un préZoom.
-	this.zoomTimeout = null;
-	//Timeout (attente) pour le passage vers un voisin.
-	this.moveToNeighbourTimeout = null;
-	this.mainPointerExitBorderTimeout = null;
-	this.secondPointerExitBorderTimeout = null;
-	//Idle time pour les pointeurs afin d'informer le front qu'on souhaite faire une recherche.
-	this.pointersSearchIdleTimeout = null;
-	//Vérifie toutes les N ms que les deux pointeurs sont détectés.
-	this.areBothPointersHereTimeout = null;
-	//Délai de suppression d'une notification de recherche par gesture.
-	this.removeNotificationByGestureTimeout = null;
-	//Délai de suppression d'une notification de recherche par gesture infructueuse.
-	this.removeFailedNotificationByGestureTimeout = null;
-	//Délai avant la suppression de notification swipe.
-	this.notifySwipeTimeout = null;
-	//Délai pour la sélection de la TL.
-	this.selectTLTimeout = null;
-	//Délai pour slider sur la TL.
-	this.canSlideInTLTimeout = null;
-	//Délai pour afficher l'aide.
-	this.canNotifyHelpTimeout = null;
-	this.arrowLeftTimeout = null;
-	this.arrowRightTimeout = null;
-	this.arrowUpTimeout = null;
-	this.arrowDownTimeout = null;
-	
-	this.arrowSpinnerTimeout = null;
-	this.nouserTimeout = null;
-	this.nextDrawCurveTimeout = null;
-	
-	//Dernier message INCOMING (pour éviter d'effectuer n fois la même action.
-	this.lastIncomingMessage = '';
-	
-	//Type de marqueur recherché dans la mosaïque (en mode filter).
-	this.filterSearchedType = "";
-	
-	//Mode actuel.
-	this.currentMode = "NO-USER";
-	//Snapshot sur lequel on a zoomé.
-	this.previousZoomedSN = null;
-	//Snapshot sur lequel on a prezoomé.
-	this.previousPrezoomDiv = null;
-	//Son ID.
-	this.previousId = null;
-	//Dernier snapshot prézoomé non null.
-	this.lastNonNullSN = null;
-	//Largeur de la marge pour le centrage vertical de la mosaïque.
-	this.MPTop_margin;
-	this.top_margin;
-	
-	//Gestures actuellement cherchées dans les vidéos.
-	this.currentSearchGesture = [];
-	
-	//Mosaïque locale.
-	this.localMos;
-	//Position des voisins lors d'un zoom.
-	this.neighboursIds = [];
-	//ID du snapshot du milieu lors d'un zoom.
-	this.centerId;
-	
-	//Voisins sélectionnés par les pointeurs.
-	this.mainPointerNeighbourSelectedId = null;
-	this.secondPointerNeighbourSelectedId = null;
-	
-	//Snapshots a afficher.
-	this.snapshotsToShow = 1;
-	
-	//Lecteur.
-	this.player = null;
-	//Si le lecteur est prêt.
-	this.playerIsReady = false;
-	
-	//Annotations (pour les marqueurs los d'un filtrage).
-	this.annotations = [];
-	
-	//Client websocket pour recevoir les notifications du Middleware.
-	this.client;
-	
-	//Coordonnées et dimensions d'un snapshot zoomé.
-	this.snTop = 0;
-	this.snLeft = 0;
-	this.snWidth = 0;
-	this.snHeight = 0;
-	
-	this.searchCanvas;
-	//Position actuelle de la vidéo zoomée.
-	this.notifyTopVideo;
-	this.notifyLeftVideo;
-	this.loadParameters(this.config_path);
+    //Chemin du fichier de configuration.
+    this.config_path = config;
+    //Configuration par défaut en cas de valeur erronnée.
+    this.default_config = default_conf;
+    this.config = new Object();
+    //Tableaux des urls des vidéos, des snapshots et de leur position dans la mosaïque.
+    this.videos = [];
+    this.urls = [];
+    this.sources = [];
+    this.imgs = [];
+    this.opacities = [];
+    this.timeToGoAt = [];
+    this.ids = [];
+    this.fillingIds = [];
+    this.currentRandomVideoIdx = 0;
+    
+    //Dictionnaire pour les courbes de recherche.
+    this.dictionary = [];
+    
+    //Dernières positions des pointeurs.
+    this.mainPointerLastX = null;
+    this.mainPointerLastY = null;
+    this.secondPointerLastX = null;
+    this.secondPointerLastY = null;
+    //Dernières positions avant le lancement de la fonction d'idle.
+    this.mainPointerIdleStartX = null;
+    this.mainPointerIdleStartY = null;
+    this.secondPointerIdleStartX = null;
+    this.secondPointerIdleStartY = null;
+    
+    //Coordonnées de la souris dans le mode d'interaction souris.
+    this.mousePosX = null;
+    this.mousePosY = null;
+    //Coordonnées précédentes de la souris dans le mode d'interaction souris.
+    this.mousePosLastX = null;
+    this.mousePosLastY = null;
+    //Valeur du déplacement entre un mouse up et un mouse down.
+    this.mouseUpDownDelta = 0;
+	//Coordonnées de la souris au dernier mouse down.
+	this.mouseDownPosX = null;
+	this.mouseDownPosY = null;
+    
+    //Dimensions de la mosaïque en pixels.
+    this.width = null;
+    this.height = null;
+    //Dimensions d'un snapshot en pixels.
+    this.snapshotWidth = null;
+    this.snapshotHeight = null;
+    //Espacement entre les snapshots en pixels.
+    this.marginWidth = null;
+    
+    //Booléens permettant ou non certaines intéractions selon le contexte.
+    this.zoomed = false;
+    this.fullscreen = false;
+    this.canMoveToNeighbour = false;
+    this.mainPointerExitBorder = false;
+    this.secondPointerExitBorder = false;
+    this.isMainPointerDisplayed = false;
+    this.isSecondPointerDisplayed = false;
+    this.helpDisplayed = false;
+    //Indique si l'utilisateur a manuellement pausé la vidéo.
+    this.userPaused = false;
+    //Indique si on est en train de se déplacer vers un voisin.
+    this.currentlyMoving = false;
+    //Indique si on est en train de dézoomer vers la mosaïque.
+    this.currentlyUnzooming = false;
+    //Indique si on peut s'approcher de kinect.
+    this.canStart = false;
+    //Indique si on est actuellement sur un snapshot.
+    this.isOnASnapshot = false;
+    //Indique si l'idle des pointeurs est disponible (deux mains détectées).
+    this.pointersIdleAvailable = false;
+    //Indique si le timeout pour l'idle des pointeurs à besoin d'être lancé.
+    this.pointersIdleNeedLaunch = false;
+    //Indique si les deux mains sont là.
+    this.areBothPointersHere = false;
+    //Indique si le timeout pour la détection de deux pointeurs a été lancé.
+    this.areBothPointersTimeoutLaunched = false;
+    //Indique si la mosaïque a été filtrée.
+    this.isMosaicFiltered = false;
+    //Indique si on est actuellement dans une recherche par gesture.
+    this.isCurrentlyInASearchByGesture = false;
+    //Indique si un pointeur est déjà sur une notification de recherche par gesture.
+    this.alreadyOnNotification = false;
+    //Si on a fait un swipe.
+    this.isSwipe = false;
+    //On peut swiper.
+    this.canSwipe = false;
+    //On passe vers une autre video automatiquement à la fin d'une lecture.
+    this.autoMove = false;
+    //Si l'utilisateur a demandé à sélectionner la TL.
+    this.isTLRequested = false;
+    //Le pointeur gauche a sélectionné la TL.
+    this.isTLSelectedBySecondPointer = false;
+    //Le pointeur droit a sélectionné la TL.
+    this.isTLSelectedByMainPointer = false;
+    //On peut afficher l'aide.
+    this.canNotifyHelp = false;
+    //Indique si la mosaique est en train d'être filtrée.
+    this.isMosaicFiltering = false;
+    this.arrowLeftLoading = false;
+    this.arrowRightLoading = false;
+    this.arrowUpLoading = false;
+    this.arrowDownLoading = false;
+    //On est dans une recherche par courbes.
+    this.isSearchByCurvesOn = false;
+    this.canDrawNextCurve = false;
+    //Dans le mode d'interaction souris, indique si on se situe actuellement sur un snapshot entièrement prézoomé.
+    this.isOnAPrezoomSN = false;
+    //Indique si une courbe de recherche donne au moins un résultat.
+    this.curvesGesturesFound = false;
+    //Indique si on souhaite supprimer la recherche en cours.
+    this.gestureDelRequested = false;
+    //Code de gesture actuellement calculé par les détecteurs de courbes.
+    this.actualCode = '';
+    //Indique si l'utilisateur est entré dans la zone de recherche.
+    this.isUserInSearchZone = false;
+	this.isMouseDown = false;
+	//Indique si on est en train de prézoomer.
+	this.isPrezooming = false;
+    
+    //Timeout (attente) pour le zoom après un préZoom.
+    this.zoomTimeout = null;
+    //Timeout (attente) pour le passage vers un voisin.
+    this.moveToNeighbourTimeout = null;
+    this.mainPointerExitBorderTimeout = null;
+    this.secondPointerExitBorderTimeout = null;
+    //Idle time pour les pointeurs afin d'informer le front qu'on souhaite faire une recherche.
+    this.pointersSearchIdleTimeout = null;
+    //Vérifie toutes les N ms que les deux pointeurs sont détectés.
+    this.areBothPointersHereTimeout = null;
+    //Délai de suppression d'une notification de recherche par gesture.
+    this.removeNotificationByGestureTimeout = null;
+    //Délai de suppression d'une notification de recherche par gesture infructueuse.
+    this.removeFailedNotificationByGestureTimeout = null;
+    //Délai avant la suppression de notification swipe.
+    this.notifySwipeTimeout = null;
+    //Délai pour la sélection de la TL.
+    this.selectTLTimeout = null;
+    //Délai pour slider sur la TL.
+    this.canSlideInTLTimeout = null;
+    //Délai pour afficher l'aide.
+    this.canNotifyHelpTimeout = null;
+    this.arrowLeftTimeout = null;
+    this.arrowRightTimeout = null;
+    this.arrowUpTimeout = null;
+    this.arrowDownTimeout = null;
+    
+    this.arrowSpinnerTimeout = null;
+    this.nouserTimeout = null;
+    this.nextDrawCurveTimeout = null;
+    
+    //Dernier message INCOMING (pour éviter d'effectuer n fois la même action.
+    this.lastIncomingMessage = '';
+    
+    //Type de marqueur recherché dans la mosaïque (en mode filter).
+    this.filterSearchedType = "";
+    
+    //Mode actuel.
+    this.currentMode = "NO-USER";
+    //Snapshot sur lequel on a zoomé.
+    this.previousZoomedSN = null;
+    //Snapshot sur lequel on a prezoomé.
+    this.previousPrezoomDiv = null;
+    //Son ID.
+    this.previousId = null;
+    //Dernier snapshot prézoomé non null.
+    this.lastNonNullSN = null;
+    //Largeur de la marge pour le centrage vertical de la mosaïque.
+    this.MPTop_margin = null;
+    this.top_margin = null;
+    
+    //Gestures actuellement cherchées dans les vidéos.
+    this.currentSearchGesture = [];
+    
+    //Position des voisins lors d'un zoom.
+    this.neighboursIds = [];
+    //ID du snapshot du milieu lors d'un zoom.
+    this.centerId = null;
+    
+    //Voisins sélectionnés par les pointeurs.
+    this.mainPointerNeighbourSelectedId = null;
+    this.secondPointerNeighbourSelectedId = null;
+    
+    //Snapshots a afficher.
+    this.snapshotsToShow = 1;
+    
+    //Lecteur.
+    this.player = null;
+    //Si le lecteur est prêt.
+    this.playerIsReady = false;
+    
+    //Annotations (pour les marqueurs los d'un filtrage).
+    this.annotations = [];
+    
+    //Client websocket pour recevoir les notifications du Middleware.
+    this.client = null;
+    
+    //Coordonnées et dimensions d'un snapshot zoomé.
+    this.snTop = 0;
+    this.snLeft = 0;
+    this.snWidth = 0;
+    this.snHeight = 0;
+    
+    this.searchCanvas = null;
+    //Position actuelle de la vidéo zoomée.
+    this.notifyTopVideo = null;
+    this.notifyLeftVideo = null;
+    this.loadParameters(this.config_path);
 }
 
 /*
  * Méthode d'affichage de la mosaïque.
  * Génère une matrice de imgs.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadMosaic, afin de créer les éléments visuels de la mosaique.
  */
-mosaic.prototype.createMosaic = function()
+Mosaic.prototype.createMosaic = function()
 {
-	// console.log('CREATE');
-	// this.currentMode = "NO-USER";
-	var initPanel = '<div id="initPanel"></div>';
-	var mp = $('#mainPanel');
-	mp.append(initPanel);
-	$('#initPanel').css(
+	var _this = this;
+	
+    //On ajoute le player au body pour le lancer la première fois qu'on zoom.
+	$('body').append('<div class="player" id="video"></div><div class="LdtPlayer" id="LdtPlayer"></div>');
+	
+	//On recharge la fenêtre si sa taille a changé.
+	$(window).resize(function()
 	{
-		background: 'transparent',
-		width: mp.width(),
-		height: mp.height(),
-		top: mp.position().top,
-		left: mp.position().left,
-		'margin-top': this.MPTop_margin
+		_.debounce(window.location.reload(), _this.config.timeReloadAfterResize);
 	});
 	
-	var len = this.config['length'], imgs = this.config['imagesToShow'], imgsTotal = this.config['imagesTotal'];
-	
-	//S'il s'agit d'un rectangle.
+    var initPanel = '<div id="initPanel"></div>';
+    var mp = $('#mainPanel');
+    mp.append(initPanel);
+    $('#initPanel').css(
+    {
+        background: 'transparent',
+        width: mp.width(),
+        height: mp.height(),
+        top: mp.position().top,
+        left: mp.position().left,
+        'margin-top': this.MPTop_margin
+    });
+    
+    var len = this.config.length, imgs = this.config.imagesToShow, imgsTotal = this.config.imagesTotal;
+    
+    //S'il s'agit d'un rectangle.
     if(imgs % len == 0)
     {
-		this.lastIncomingMessage = 'INCOMING-0';
-		
-		var str = '';
-		
-		if(this.imgs.length >= imgs)
-		{
-			for(var i = 0 ; i < imgs ; i++)
-			{
-				//On charge les images de petite taille pour ne pas surcharger la mosaïque lors de l'affichage global.
-				str += '<div id="snapshotDiv-' + i + '" class="snapshotDivs" style="opacity: 0;"><img id="snapshot-' + i + '" class="snapshots" src="snapshots-little/' + this.imgs[i] + '" /></div>';
-				//Au départ aucune vidéo n'a de gesture de recherche.
-				this.currentSearchGesture[i] = '';
-			}
-		}
-		
-		return str + '<div id="ghostPanel"></div>';
-	}
-	else
+        this.lastIncomingMessage = 'INCOMING-0';
+        
+        var str = '';
+        
+        if(this.imgs.length >= imgs)
+        {
+            for(var i = 0 ; i < imgs ; i++)
+            {
+                //On charge les images de petite taille pour ne pas surcharger la mosaïque lors de l'affichage global.
+                str += '<div id="snapshotDiv-' + i + '" class="snapshotDivs" style="opacity: 0;"><img id="snapshot-' + i + '" class="snapshots" src="snapshots-little/' + this.imgs[i] + '" /></div>';
+                //Au départ aucune vidéo n'a de gesture de recherche.
+                this.currentSearchGesture[i] = '';
+            }
+        }
+        
+        return str + '<div id="ghostPanel"></div>';
+    }
+    else
+    {
+        alert("Le nombre d'images a afficher doit être divisible par la longueur de la mosaïque !");
+    }
+}
+
+/*
+ * Fonction appelée lors d'un mouse down en mode d'interaction souris.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadMosaic, attachée à l'événement jQuery mousedown.
+*/
+Mosaic.prototype.onMouseDown = function(e)
+{
+	this.isMouseDown = true;
+	
+	//On met à jour les coordonnées de la souris au dernier mouse down.
+	this.mouseDownPosX = e.pageX;
+	this.mouseDownPosY = e.pageY;
+	
+	//Si on est sur une notification de gesture de recherche.
+	this.removeSearchNotificationIfOnIt(e.pageX, e.pageY);
+	this.isUserInSearchZone = true;
+	
+	//Si on est en mode de tracé de courbes, on indique qu'on a commencé à tracer au canvas.
+	if(this.isSearchByCurvesOn)
 	{
-		alert("Le nombre d'images a afficher doit être divisible par la longueur de la mosaïque !");
+		this.searchCanvas.onPointerIn(this.mousePosX, this.mousePosY, null, null);
 	}
 }
 
 /*
- * Permet de raffraichir la mosaïque.
- */
-mosaic.prototype.loadMosaic = function()
+ * Fonction appelée lors d'un mouse move en mode d'interaction souris.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadMosaic, attachée à l'événement jQuery mousemove.
+*/
+Mosaic.prototype.onMouseMove = function(e)
 {
-	// console.log('LOAD');
-	var createMosaic = this.createMosaic();
-	
-	if(createMosaic == '')
+	//Si on n'a pas appuyé sur la souris avant, on part.
+	if(!this.isMouseDown)
 	{
 		return;
 	}
 	
 	var _this = this;
 	
+	//Si on est en mode de tracé de courbes, on met à jour la courbe.
+	if(this.isSearchByCurvesOn)
+	{
+		this.searchCanvas.onPointerMove(this.mousePosX, this.mousePosY - this.MPTop_margin, null, null);
+	}
+	
+	//On met à jour l'ancienne position de la souris si elle est nulle.
+	if(!this.mousePosLastX && this.mousePosLastX != 0)
+	{
+		this.mousePosLastX = this.mousePosX;
+	}
+	if(!this.mousePosLastY && this.mousePosLastY != 0)
+	{
+		this.mousePosLastY = this.mousePosY;
+	}
+	
+	//Le delta s'accroît si la souris bouge.
+	this.mouseUpDownDelta += Math.floor(Math.sqrt((this.mousePosLastX - e.pageX) * (this.mousePosLastX - e.pageX) + (this.mousePosLastY - e.pageY) * (this.mousePosLastY - e.pageY)));
+	
+	//On met à jour l'ancienne position de la souris.
+	if(this.mousePosLastX != this.mousePosX)
+	{
+		this.mousePosLastX = this.mousePosX;
+	}
+	if(this.mousePosLastY != this.mousePosY)
+	{
+		this.mousePosLastY = this.mousePosY;
+	}
+	
+	//Si la souris a parcouru une trop grande distance, on entre en recherche.
+	if(this.mouseUpDownDelta > this.config.mouseUpDownDeltaTreshold)
+	{
+		//Si on est en mosaique, on entre en filtrage.
+		if(this.currentMode == "MOSAIC")
+		{
+			this.preUnzoom();
+			this.currentMode = "FILTER";
+			this.isMosaicFiltered = true;
+		}
+		//Si on est en mode de filtrage, mais qu'on n'est pas en tracé de courbes.
+		else if(this.currentMode == "FILTER" && !this.isSearchByCurvesOn && this.isUserInSearchZone)
+		{
+			//On lance une nouvelle recherche pas courbes.
+			this.preUnzoom();
+		}
+		//Si on est dans une vidéo, on entre en recherche.
+		else if(this.currentMode == "VIDEO" || this.currentMode == "TIMELINE")
+		{
+			this.currentMode = "SEARCH";
+		}
+		//Si on est en mode recherche dans une vidéo, mais qu'on n'est pas en tracé de courbes.
+		/*else if(this.currentMode == "SEARCH" && !this.isSearchByCurvesOn)
+		{
+			//On lance une nouvelle recherche pas courbes.
+		}*/
+		
+		if(this.currentMode != "NO-USER" && this.currentMode.indexOf("INCOMING") == -1 && !this.isSearchByCurvesOn)
+		{
+			this.isSearchByCurvesOn = true;
+			this.startSearch();
+			this.searchCanvas.onPointerIn(this.mousePosX, this.mousePosY - this.MPTop_margin, null, null);
+		}
+		
+		//S'il n'est pas possible d'afficher l'aide.
+		if(!this.canNotifyHelp)
+		{
+			//On rend son affichage possible après un certain délai.
+			this.canNotifyHelpTimeout = setTimeout(function()
+			{
+				_this.canNotifyHelp = true;
+			}, this.config.timeoutCanNotifyHelp);
+		}
+	}
+}
+
+/*
+ * Fonction appelée lors d'un mouse up en mode d'interaction souris.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadMosaic, attachée à l'événement jQuery mousemove.
+*/
+Mosaic.prototype.onMouseUp = function()
+{
+	this.isMouseDown = false;
+	
+	//Si on était en train de tracer une courbe.
+	if(this.isSearchByCurvesOn)
+	{
+		//On quitte la zone de recherche.
+		this.isUserInSearchZone = false;
+		
+		//On regarde si ce qu'on a tracé correspond à une courbe en particulier.
+		var gesture_match = this.gestureWithSameCode(this.actualCode);
+		this.actualCode = '';
+		
+		//Si oui.
+		if(gesture_match.length > 0)
+		{
+			//Si on est en mode recherche dans une vidéo et que le player est prêt.
+			if(this.currentMode == "SEARCH" && this.playerIsReady)
+			{
+				//On effectue une recherche dans cette vidéo.
+				this.player.widgets[0].searchByGesture(gesture_match);
+				this.isCurrentlyInASearchByGesture = this.player.widgets[0].isCurrentlyInASearchByGesture;
+				
+				//On va au premier marqueur trouvé.
+				if(this.player && this.player.widgets[0] && this.timeToGoAt[this.centerId] === 0 && this.player.widgets[0].atLeastOneSearchMarker(this.currentSearchGesture[this.centerId]))
+				{
+					this.player.widgets[0].goToFirstSearchedMarker(this.currentSearchGesture[this.centerId]);
+				}
+				
+				//On affiche la notification de gesture de recherche.
+				this.removeNotifications();
+				this.currentSearchGesture[this.centerId] = gesture_match;
+				this.searchGesture(gesture_match, 'valid');
+				this.curvesGesturesFound = false;
+			}
+			//Si on est en mode de filtrage de mosaique.
+			else if(this.currentMode == "FILTER")
+			{
+				if(this.isMosaicFiltered)
+				{
+					//On notifie la recherche par filtrage.
+					this.removeNotifications();
+					this.filterSearchedType = gesture_match;
+					this.filterGesture(gesture_match, 'valid');
+					//On filtre la mosaique.
+					this.searchFilter(gesture_match);
+					this.curvesGesturesFound = false;
+				}
+			}
+		}
+		//Si aucune gesture ne matche dans le dictionnaire.
+		else
+		{
+			//Si on était en mode filtrage de la mosaïque et qu'aucune gesture de filtrage n'avait été détectée avant ca, on revient en mode mosaïque.
+			if(this.currentMode == "FILTER" && this.filterSearchedType == "")
+			{
+				this.currentMode = "MOSAIC";
+				this.isMosaicFiltered = false;
+			}
+			//Sinon si on était en mode recherche dans une video et qu'aucune gesture n'avait été détectée avant ca, on revient en mode video.
+			if(this.currentMode == "SEARCH" && this.currentSearchGesture[this.centerId] == "")
+			{
+				this.currentMode = "VIDEO";
+			}
+		}
+		//On dit au module de recherche qu'on arrête de tracer des courbes.
+		this.searchCanvas.onPointerOut();
+	}
+	
+	this.mousePosLastX = null;
+	this.mousePosLastY = null;
+	
+	//Si la distance parcourue par la souris entre le mouse down et le mouse up est inférieure ou égale au seuil.
+	if(this.mouseUpDownDelta <= this.config.mouseUpDownDeltaTreshold)
+	{
+		//Si on est sur un snapshot prézoomé.
+		if(this.isOnAPrezoomSN && this.previousZoomedSN != '' && (this.currentMode == 'MOSAIC' || this.currentMode == 'FILTER') && !this.isPrezooming)
+		{
+			this.zoom();
+		}
+	}
+	
+	//On réinitialise le delta, on quitte la recherche par courbes.
+	this.mouseUpDownDelta = 0;
+	this.isSearchByCurvesOn = false;
+	this.leaveSearch();
+	
+	//Si on est en mode de filtrage et qu'on a une gesture de filtrage trouvée.
+	if(this.currentMode == 'FILTER' && this.filterSearchedType != '')
+	{
+		//On notifie.
+		this.removeNotifications();
+		this.filterGesture(this.filterSearchedType, 'valid');
+	}
+}
+
+/*
+ * Fonction appelée lors d'un clic en mode d'interaction souris.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadMosaic.
+*/
+Mosaic.prototype.onClick = function(x, y)
+{
+	//Si la position de la souris entre le mouse down et le mouse up change de plus de 10px, on part.
+	if(!this.mouseDownPosX || !this.mouseDownPosY || Math.sqrt((this.mouseDownPosX - x) * (this.mouseDownPosX - x) + (this.mouseDownPosY - y) * (this.mouseDownPosY - y)) > 10)
+	{
+		return;
+	}
+	
+	//Si on est dans un mode autre qu'un mode zoomé et qu'on n'affiche pas l'aide, on part.
+	if(this.currentMode != "VIDEO" && this.currentMode != "SEARCH" && this.currentMode != "TIMELINE" && !this.helpDisplayed)
+	{
+		return;
+	}
+	
+	//Si on clique en dehors de la video centrale, alors on dézoome.
+	var TL = $('.Ldt-Timeline');
+	var TLwidth = TL.width(), TLheight = TL.height();
+	var Ptop = $('.LdtPlayer').position().top, Pleft = $('.LdtPlayer').position().left;
+	var Pheight = $('.LdtPlayer').height();
+	var MPx = this.mousePosX, MPy = this.mousePosY;
+	
+	//On regarde si on a cliqué sur un snapshot.
+	var SN = this.pointerPositionToSN(MPx - this.notifyLeftVideo, MPy - this.notifyTopVideo, true);
+	var SNId;
+	if(SN)
+	{
+		SNId = parseInt(SN.attr('id').replace('snapshotDiv-', ''));
+	}
+	
+	//Si on n'a pas demandé à supprimé la notification de gesture.
+	if(!this.gestureDelRequested && !this.helpDisplayed)
+	{
+		//Si non, ou s'il ne fait pas partie des voisins.
+		if(!SNId || !_.include(this.neighboursIds, SNId))
+		{
+			//Si le clic a lieu en dehors du player et que l'aide n'est pas affichée, on dezoom.
+			if(MPx < Pleft || MPx > (+Pleft + TLwidth) || MPy < Ptop || MPy > (+Ptop + Pheight - TLheight))
+			{
+				this.unzoom();
+			}
+		}
+		//Si on se trouve sur un voisin, on bouge.
+		else if(SNId && _.include(this.neighboursIds, SNId) && this.canMoveToNeighbour)
+		{
+			this.moveToNeighbour($('#snapshotDiv-' + SNId));
+		}
+	}
+	//Si on l'a demandé, on enlève la demande de suppression.
+	else
+	{
+		this.gestureDelRequested = false;
+	}
+	
+	//Si l'aide est affichée, un clic la ferme.
+	if(this.helpDisplayed)
+	{
+		this.removeHelp();
+	}
+}
+
+/*
+ * Permet de raffraichir la mosaïque.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadFromJson, afin de charger la mosaique une fois que les fichiers de métadonnées ont été lus.
+ */
+Mosaic.prototype.loadMosaic = function()
+{
+    // console.log('LOAD');
+    var createMosaic = this.createMosaic();
+    
+    if(createMosaic == '')
+    {
+        return;
+    }
+    
+    var _this = this;
+    
     //On affecte les chemins vers les images à la mosaïque.
     this.previousZoomedSN;
-    //this.width = 
-	// console.log(createMosaic);
     //On met à jour la mosaïque.
     $('#mainPanel').html(createMosaic);
     //On récupère la taille des bordures.
@@ -314,7 +607,7 @@
     //On calcule la taille des divs contenant les snapshots.
     this.width = $('#mainPanel').innerWidth();
     //On ne calculera pas tout de suite la hauteur de la mosaique étant donnée qu'elle est calculée par la suite dynamiquement.
-    this.snapshotWidth = this.width / this.config['length'] - this.marginWidth;
+    this.snapshotWidth = this.width / this.config.length - this.marginWidth;
     this.snapshotHeight = this.snapshotWidth*9/16;
     $('.snapshotDivs').css('width', this.snapshotWidth).css('height', this.snapshotHeight).css('margin', this.marginWidth/2);
     
@@ -322,936 +615,774 @@
     //On centre verticalement la mosaïque.
     this.MPTop_margin = ($(document).height() - $('#mainPanel').height())/2;
     $('#mainPanel').css('margin-top', this.MPTop_margin).css('margin-bottom', this.MPTop_margin);
-	
-	//On fait coincider le background du body avec celui de la mosaïque.
-	$('body').css('background-position', '0px ' + this.MPTop_margin + 'px');
-	
-	/*$('.snapshotDivs').mouseover(function ()
-	{
-		//On effectue un prézoom dès qu'on va sur une image.
-		_this.preZoom($(this));
-	});*/
-	
-	this.addPointers();
-	
-	//Si dans le metadata player _ n'est pas déclaré, on le déclare.
-	if(typeof _ !== "undefined" && typeof IriSP._ === "undefined")
-	{
-		IriSP._ = _;
-	}
-	
-	if(typeof $ !== "undefined" && typeof IriSP.jQuery === "undefined")
-	{
-		IriSP.jQuery = $;
-	}
-	
-	//On charge les marqueurs.
-	var sourceManager = new IriSP.Model.Directory(),
-	globalAnnotations = new IriSP.Model.List(sourceManager),
-	nbFichiers = _this.urls.length,
-	fichiersCharges = 0;
-	
-	for (var i = 0; i < nbFichiers; i++)
-	{
-		// console.log('url : ' + _this.urls[i]);
-		_this.sources[i] = sourceManager.remoteSource({url: _this.urls[i], serializer: IriSP.serializers.ldt});
-		_this.sources[i].onLoad(function()
+    
+    //On fait coincider le background du body avec celui de la mosaïque.
+    $('body').css('background-position', '0px ' + this.MPTop_margin + 'px');
+    
+    this.addPointers();
+    
+    //Si dans le metadata player _ n'est pas déclaré, on le déclare.
+    if(typeof _ !== "undefined" && typeof IriSP._ === "undefined")
+    {
+        IriSP._ = _;
+    }
+    
+    if(typeof $ !== "undefined" && typeof IriSP.jQuery === "undefined")
+    {
+        IriSP.jQuery = $;
+    }
+    
+    //On charge les marqueurs.
+    var sourceManager = new IriSP.Model.Directory(),
+    globalAnnotations = new IriSP.Model.List(sourceManager),
+    nbFichiers = _this.urls.length,
+    fichiersCharges = 0;
+    
+    //Récupère les annotations.
+    for (var i = 0; i < nbFichiers; i++)
+    {
+        _this.sources[i] = sourceManager.remoteSource({url: _this.urls[i], serializer: IriSP.serializers.ldt});
+        _this.sources[i].onLoad(function()
+        {
+            var source = this;
+            globalAnnotations.addElements(source.getAnnotations());
+            fichiersCharges++;
+            if (fichiersCharges == nbFichiers)
+            {
+                //Instructions à exécuter quand tout est chargé.
+                _this.annotations = globalAnnotations;
+                // console.log(_this.annotations.length + ' annotations loaded from ' + nbFichiers + ' files.');
+                
+                //Si on gère les interactions à la souris.
+                if(_this.config.mouseInteractions)
+                {
+                    //On met à jour la position estimée de la souris.
+                    $(window).mousemove(function(e)
+                    {
+                        // _this.refreshPointers(e.pageX, e.pageY, _this, true);
+                        _this.refreshPointers(e.pageX, e.pageY, true);
+                        _this.mousePosX = e.pageX;
+                        _this.mousePosY = e.pageY;
+                    });
+                }
+                
+                //Si on a activé la préphase mais qu'on est en mode Kinect.
+                if(_this.config.noUserModeEnabled && !_this.config.mouseInteractions)
+                {
+                    //On active la préphase.
+                    _this.init();
+                    _this.showNImages(0);
+                    _this.currentMode = "NO-USER";
+                }
+                else
+                {
+                    //Sinon on affiche directement la mosaique.
+                    _this.showNImages(20);
+                    _this.currentMode = "MOSAIC";
+                }
+                
+                //Le premier snapshot sur lequel on zoom en préphase.
+                _this.previousZoomedSN = $('#snapshotDiv-' + _this.fillingIds[0]);
+            }
+        });
+    }
+    
+    //Si on est en mode d'intéraction souris.
+    if(this.config.mouseInteractions)
+    {
+        //Si on fait un mouse down sur le body, on vérifie enregistre le déplacement de la souris jusqu'au prochain mouse up.
+        $(window).mousedown(function (e)
+        {
+			_this.onMouseDown(e);
+			return false;
+        });
+		
+		//On écoute le déplacement de la souris.
+		$(window).mousemove(function(e)
 		{
-			var source = this;
-			// console.log(source);
-			globalAnnotations.addElements(source.getAnnotations());
-			// console.log(source.url + ' ' + source.getAnnotations().length);
-			fichiersCharges++;
-			if (fichiersCharges == nbFichiers)
+			_this.onMouseMove(e);
+			return false;
+		});
+		
+		//Si on fait un mouse up après ce mouse down.
+		$(window).mouseup(function()
+		{
+			_this.onMouseUp();
+			return false;
+		});
+		
+		//Si on fait un clic.
+		$(window).click(function(e)
+		{
+			_this.onClick(e.pageX, e.pageY);
+			_this.removeSearchNotificationIfOnIt(e.pageX, e.pageY);
+			return false;
+		});
+		
+		$(window).on('mousewheel', function(event, delta, deltaX, deltaY)
+        {
+			//Si on est dans un mode autre qu'on mode zoomé ou que l'aide est affichée, on part.
+			if(_this.currentMode != "VIDEO" && _this.currentMode != "SEARCH" && _this.currentMode != "TIMELINE" || _this.helpDisplayed)
 			{
-				// instructions à exécuter quand tout est chargé
-				_this.annotations = globalAnnotations;
-				// console.log(_this.annotations.length + ' ' + nbFichiers);
-				console.log(_this.annotations.length + ' annotations loaded from ' + nbFichiers + ' files.');
-				
-				//Si on gère les interactions à la souris.
-				if(_this.mouseInteractions)
-				{
-					$(window).mousemove(function(e)
-					{
-						_this.refreshMainPointer(e.pageX, e.pageY, _this);
-						_this.mousePosX = e.pageX;
-						_this.mousePosY = e.pageY;
-					});
-				}
-				
-				if(_this.prephaseEnabled && !_this.mouseInteractions)
-				{
-					_this.init();
-					_this.showNImages(0);
-					_this.currentMode = "NO-USER";
-				}
-				else
-				{
-					_this.showNImages(20);
-					_this.currentMode = "MOSAIC";
-				}
-				
-				// /!\ //
-				// _this.currentMode = "FILTER";
-				// _this.showNImages(20);
-				//_this.isSearchByCurvesOn = true;
-				// _this.startSearch();
-				// console.log('CANVAS READY');
-				// /!\ //
-				
-				_this.previousZoomedSN = $('#snapshotDiv-' + _this.fillingIds[0]);
-			}
-		});
-	}
-	
-	if(this.mouseInteractions)
-	{
-		//Si on fait un mouse down sur le body, on vérifie enregistre le déplacement de la souris jusqu'au prochain mouse up.
-		$(window).mousedown(function (e)
-		{
-			_this.removeSearchNotificationIfOnIt(e.pageX, e.pageY);
-			
-			_this.isUserInSearchZone = true;
-			if(_this.isSearchByCurvesOn)
-			{
-				_this.searchCanvas.onPointerIn(_this.mousePosX, _this.mousePosY, null, null);
+				return;
 			}
 			
-			console.log('mdown');
-			//On écoute le déplacement de la souris.
-			$(window).mousemove(function(e)
-			{
-				if(_this.isSearchByCurvesOn)
-				{
-					_this.searchCanvas.onPointerMove(_this.mousePosX, _this.mousePosY - _this.MPTop_margin, null, null);
-				}
-				
-				//On met à jour l'ancienne position de la souris si elle est nulle.
-				if(!_this.mousePosLastX && _this.mousePosLastX != 0)
-				{
-					_this.mousePosLastX = _this.mousePosX;
-				}
-				if(!_this.mousePosLastY && _this.mousePosLastY != 0)
-				{
-					_this.mousePosLastY = _this.mousePosY;
-				}
-				
-				//Le delta s'accroît si la souris bouge.
-				_this.mouseUpDownDelta += Math.floor(Math.sqrt((_this.mousePosLastX - e.pageX) * (_this.mousePosLastX - e.pageX) + (_this.mousePosLastY - e.pageY) * (_this.mousePosLastY - e.pageY)));
-				
-				// console.log(_this.mouseUpDownDelta, _this.mousePosLastX, e.pageX);
-				
-				if(_this.mousePosLastX != _this.mousePosX)
-				{
-					_this.mousePosLastX = _this.mousePosX;
-				}
-				if(_this.mousePosLastY != _this.mousePosY)
-				{
-					_this.mousePosLastY = _this.mousePosY;
-				}
-				
-				//Si la souris a parcouru une trop grande distance, on entre en recherche.
-				if(_this.mouseUpDownDelta > _this.config['mouseUpDownDeltaTreshold'])
-				{
-					//Si on est en mosaique, on entre en filtrage.
-					if(_this.currentMode == "MOSAIC")
-					{
-						_this.preUnzoom();
-						_this.currentMode = "FILTER";
-						_this.isMosaicFiltered = true;
-						
-						console.log(_this.date() + ' - ENTRE EN MODE FILTRAGE');
-						
-						_this.isSearchByCurvesOn = true;
-						_this.startSearch();
-						
-						/*if(!_this.curvesGesturesFound)
-						{
-							this.removeNotifications();
-							_this.filterSearch();
-						}*/
-						
-						_this.searchCanvas.onPointerIn(_this.mousePosX, _this.mousePosY - _this.MPTop_margin, null, null);
-					}
-					else if(_this.currentMode == "FILTER" && !_this.isSearchByCurvesOn && _this.isUserInSearchZone)
-					{
-						console.log('after search');
-						_this.preUnzoom();
-						_this.isSearchByCurvesOn = true;
-						_this.startSearch();
-						_this.searchCanvas.onPointerIn(_this.mousePosX, _this.mousePosY - _this.MPTop_margin, null, null);
-					}
-					//Si on est dans une vidéo, on entre en recherche.
-					else if(_this.currentMode == "VIDEO" || _this.currentMode == "TIMELINE")
-					{
-						_this.currentMode = "SEARCH";
-						
-						console.log(_this.date() + ' - ENTRE EN MODE RECHERCHE');
-						
-						_this.isSearchByCurvesOn = true;
-						_this.startSearch();
-						
-						/*if(!_this.curvesGesturesFound)
-						{
-							this.removeNotifications();
-							_this.searchSearch();
-						}*/
-						
-						_this.searchCanvas.onPointerIn(_this.mousePosX, _this.mousePosY - _this.MPTop_margin, null, null);
-					}
-					else if(_this.currentMode == "SEARCH" && !_this.isSearchByCurvesOn)
-					{
-						_this.isSearchByCurvesOn = true;
-						_this.startSearch();
-						_this.searchCanvas.onPointerIn(_this.mousePosX, _this.mousePosY - _this.MPTop_margin, null, null);
-					}
-					
-					//Il est possible d'afficher l'aide.
-					if(!_this.canNotifyHelp)
-					{
-						_this.canNotifyHelpTimeout = setTimeout(function()
-						{
-							_this.canNotifyHelp = true;
-						}, _this.config['timeoutCanNotifyHelp']);
-					}
-				}
-			});
-			
-			//Si on fait un mouse up après ce mouse down.
-			$(window).mouseup(function()
-			{
-				console.log('mup');
-				
-				if(_this.isSearchByCurvesOn)
-				{
-					_this.isUserInSearchZone = false;
-					
-					var gesture_match = _this.gestureWithSameCode(_this.actualCode);
-					_this.actualCode = '';
-					
-					if(gesture_match.length > 0)
-					{
-						if(_this.currentMode == "SEARCH" && _this.playerIsReady)
-						{
-							_this.player.widgets[0].searchByGesture(gesture_match);
-							_this.isCurrentlyInASearchByGesture = _this.player.widgets[0].isCurrentlyInASearchByGesture;
-							
-							if(_this.player && _this.player.widgets[0] && _this.timeToGoAt[_this.centerId] === 0 && _this.player.widgets[0].atLeastOneSearchMarker(_this.currentSearchGesture[_this.centerId]))
-							{
-								_this.player.widgets[0].goToFirstSearchedMarker(_this.currentSearchGesture[_this.centerId]);
-							}
-							
-							_this.removeNotifications();
-							_this.currentSearchGesture[_this.centerId] = gesture_match;
-							_this.searchGesture(gesture_match, 'valid');
-							_this.curvesGesturesFound = false;
-						}
-						else if(_this.currentMode == "FILTER")
-						{
-							if(_this.isMosaicFiltered)
-							{
-								_this.removeNotifications();
-								_this.filterSearchedType = gesture_match;
-								_this.filterGesture(gesture_match, 'valid');
-								_this.searchFilter(gesture_match);
-								_this.curvesGesturesFound = false;
-							}
-						}
-					}
-					
-					_this.searchCanvas.onPointerOut();
-				}
-				
-				//On unbind ce qui a été bindé après le mouse up.
-				$(window).unbind('mousemove');
-				$(window).unbind('mouseup');
-				//On rebind le mousemove principal du body, car ils ont tous été unbindés.
-				$(window).mousemove(function(e)
-				{
-					_this.refreshMainPointer(e.pageX, e.pageY, _this);
-					_this.mousePosX = e.pageX;
-					_this.mousePosY = e.pageY;
-				});
-				
-				_this.mousePosLastX = null;
-				_this.mousePosLastY = null;
-				
-				//Si la distance parcourue par la souris entre le mouse down et le mouse up est inférieure ou égale au seuil.
-				if(_this.mouseUpDownDelta <= _this.config['mouseUpDownDeltaTreshold'])
-				{
-					//Si on est sur un snapshot prézoomé.
-					if(_this.isOnAPrezoomSN && _this.previousZoomedSN != '' && (_this.currentMode == 'MOSAIC' || _this.currentMode == 'FILTER'))
-					{
-						_this.zoom();
-					}
-				}
-				
-				_this.mouseUpDownDelta = 0;
-				_this.isSearchByCurvesOn = false;
-				_this.leaveSearch();
-				
-				if(_this.currentMode == 'FILTER' && _this.filterSearchedType != '')
-				{
-					_this.removeNotifications();
-					_this.filterGesture(_this.filterSearchedType, 'valid');
-				}
-			});
-		});
-	}
+            //Quand on "tire" la molette vers soi, on dezoom.
+            if (delta < 0)
+            {
+                _this.unzoom();
+            }
+        });
+    }
 }
 
 /*
  * Charge les paramètres du Front. Local (true/false) est le mode de chargement des données.
+ * Est appelé dans le fichier :
+ * mosaic > fonction Mosaic, est appelée dans le constructeur de manière à charger les paramètres de configuration avant la création de la mosaique.
 */
-mosaic.prototype.loadParameters = function(file_path)
+Mosaic.prototype.loadParameters = function(file_path)
 {
-	var _this = this;
-	
-	var supposedToBeInt = ['length', 'imagesToShow', 'totalImages', 'timePrezoom', 'timePreUnzoom', 'timeZoom', 'zoomTime', 'timeUnzoom', 'timeNeighbourGlowing', 'timeNeighbourUnglowing', 'timeMovingToNeighbour', 'timeSearchFade', 'timeNotifyFade', 'timeFilterFade', 'timeANFade', 'timeFilling', 'zoomedMargin', 'timeoutZoom', 'timeoutUnzoom', 'timeoutMoveToNeighbour', 'timeoutPointersIdle', 'timeoutAreBothPointersHere', 'timeoutRemoveNotificationByGesture', 'timeoutRemoveFailedNotificationByGesture', 'timeoutNotifySwipe', 'timeoutSelectTL', 'timeoutSlideTL', 'timeoutCanNotifyHelp', 'timeoutRemoveSpinner', 'timeoutNouser', 'timeoutNextDrawCurve', 'mouseUpDownDeltaTreshold'];
-	var supposedToBeFloat = ['zoomPercentage', 'prezoomPercentage'];
-	
-	$.getJSON(file_path, function(data)
-	{
-		for(key in data)
-		{
-			var val = data[key];
-			
-			if(_.include(supposedToBeInt, key))
-			{
-				var intVal = parseInt(val);
-				if(isNaN(intVal))
-				{
-					// console.log(_this.default_config);
-					_this.config[key] = _this.default_config[key];
-					console.log("param[" + key + "] : Valeur " + val + " incorrecte (non Int). Valeur par défaut " + _this.default_config[key] + " chargée à la place.");
-				}
-				else
-				{
-					_this.config[key] = intVal;
-				}
-			}
-			else if(_.include(supposedToBeFloat, key))
-			{
-				var floatVal = parseFloat(val);
-				if(isNaN(floatVal))
+    var _this = this;
+    
+    //Variables censées être des ints.
+    var supposedToBeInt = ['length', 'imagesToShow', 'totalImages', 'timeReloadAfterResize', 'timePrezoom', 'timePreUnzoom', 'timeZoom', 'zoomTime', 'timeUnzoom', 'timeNeighbourGlowing', 'timeNeighbourUnglowing', 'timeMovingToNeighbour', 'timeSearchFade', 'timeNotifyFade', 'timeFilterFade', 'timeANFade', 'timeFilling', 'zoomedMargin', 'timeoutZoom', 'timeoutUnzoom', 'timeoutMoveToNeighbour', 'timeoutPointersIdle', 'timeoutAreBothPointersHere', 'timeoutRemoveNotificationByGesture', 'timeoutRemoveFailedNotificationByGesture', 'timeoutNotifySwipe', 'timeoutSelectTL', 'timeoutSlideTL', 'timeoutCanNotifyHelp', 'timeoutRemoveSpinner', 'timeoutNouser', 'timeoutNextDrawCurve', 'mouseUpDownDeltaTreshold'];
+    //Variables censées êtres des floats.
+    var supposedToBeFloat = ['zoomPercentage', 'prezoomPercentage'];
+    
+    //On lit le fichier de configuration.
+    $.getJSON(file_path, function(data)
+    {
+        //Pour chaque ligne du fichier.
+        for(key in data)
+        {
+            var val = data[key];
+            
+            //Si la valeur est prise pour un int.
+            if(_.include(supposedToBeInt, key))
+            {
+                //On la parse et s'il y a une erreur, on l'indique dans la console avant d'affecter la valeur par défaut.
+                //Sinon on lui affecte la valeur lue.
+                var intVal = parseInt(val);
+                if(isNaN(intVal))
+                {
+                    _this.config[key] = _this.default_config[key];
+                    console.log("param[" + key + "] : Valeur " + val + " incorrecte (non Int). Valeur par défaut " + _this.default_config[key] + " chargée à la place.");
+                }
+                else
+                {
+                    _this.config[key] = intVal;
+                }
+            }
+            //Si la valeur est prise pour un float.
+            else if(_.include(supposedToBeFloat, key))
+            {
+                //On la parse et s'il y a une erreur, on l'indique dans la console avant d'affecter la valeur par défaut.
+                //Sinon on lui affecte la valeur lue.
+                var floatVal = parseFloat(val);
+                if(isNaN(floatVal))
+                {
+                    _this.config[key] = _this.default_config[key];
+                    console.log("param[" + key + "] : Valeur " + val + " incorrecte (non Float). Valeur par défaut " + _this.default_config[key] + " chargée à la place.");
+                }
+                else
+                {
+                    _this.config[key] = floatVal;
+                }
+            }
+            else
+            {
+				//S'il s'agit de la langue, on met par défaut si la valeur est nulle.
+				if(key == 'lang' && val == '')
 				{
 					_this.config[key] = _this.default_config[key];
-					console.log("param[" + key + "] : Valeur " + val + " incorrecte (non Float). Valeur par défaut " + _this.default_config[key] + " chargée à la place.");
 				}
+                //Sinon si c'est une string, on l'affecte.
 				else
 				{
-					_this.config[key] = floatVal;
+					_this.config[key] = val;
 				}
-			}
-			else
-			{
-				_this.config[key] = val;
-			}
-		}
-		
-		//On remplit le tableau d'ids.
-		for(var i = 0 ; i < _this.config['totalImages'] ; i++)
-			_this.ids.push(i);
-		//On les mélange.
-		_this.ids.sort(function()
-		{
-			return 0.5 - Math.random()
-		});
-		
-		//On remplit le tableau d'ids destiné à afficher les snapshots au fur et à mesure.
-		for(var i = 0 ; i < _this.config['imagesToShow'] ; i++)
-			_this.fillingIds.push(i);
-		//On les mélange.
-		_this.fillingIds.sort(function()
-		{
-			return 0.5 - Math.random()
-		});
-		
-		if(_this.config['local'] == 'true')
-		{
-			// console.log("Loading local metadata.");
-			_this.loadFromJson('./player/json/local_videos.json');
-		}
-		else
-		{
-			// console.log("Loading online metadata.");
-			_this.loadFromJson('./player/json/online_videos.json');
-		}
-		
-		//On initialise le client.
-		if(!_this.mouseInteractions)
-		{
-			_this.client = new client(_this.config['host'], _this.config['port'], _this);
-		}
-		
-		_this.getDictionary();
-		_this.getLang();
-	});
+            }
+        }
+        
+        //On remplit le tableau d'ids.
+        for(var i = 0 ; i < _this.config.totalImages ; i++)
+            _this.ids.push(i);
+        //On les mélange.
+        _this.ids = _.shuffle(_this.ids);
+        
+        //On remplit le tableau d'ids destiné à afficher les snapshots au fur et à mesure.
+        for(var i = 0 ; i < _this.config.imagesToShow ; i++)
+            _this.fillingIds.push(i);
+        //On les mélange.
+        _this.fillingIds = _.shuffle(_this.fillingIds);
+        
+        //Si la config spécifie le chargement comme étant local/en ligne, on charge le fichier des vidéos approprié.
+        if(_this.config.local)
+        {
+            _this.loadFromJson(_this.config.videoConfigFileLocal);
+        }
+        else
+        {
+            _this.loadFromJson(_this.config.videoConfigFileOnline);
+        }
+        
+        //On initialise le client dans le cas d'intéractions Kinect.
+        if(!_this.config.mouseInteractions)
+        {
+            _this.client = new Client(_this.config.host, _this.config.port, _this);
+        }
+        
+        //On prend le dictionnaire stockant les codes des courbes de recherche.
+        _this.getDictionary();
+        //On charge le fichier des langues.
+        _this.getLang();
+    });
 }
 
 /*
  * Phase principale (utilisateur non détecté). Une vidéo se lance aléatoirement.
  * L'interface est identique à celle du zoom, mais sans interaction possible avec les voisins, ni les controles timeline.
  * Lors de la fin d'une lecture, on dézoome vers la mosaïque, puis on rezoome vers un autre snapshot (aléatoire).
+ * Est appelé dans les fichiers :
+ * mosaic > fonction loadMosaic.
+ * zoomInteractions > fonction unzoom.
 */
-mosaic.prototype.init = function()
+Mosaic.prototype.init = function()
 {
-	var _this = this;
-
-	if(this.currentRandomVideoIdx > this.config['imagesToShow'])
-	{
-		this.currentRandomVideoIdx = 0;
-	}
-	
-	
-	this.previousZoomedSN = $('#snapshotDiv-' + this.fillingIds[this.currentRandomVideoIdx]);
-	this.previousId = $('img', this.previousZoomedSN).attr('id');
-	
-	// console.log('CURRENT MODE : ' + _this.currentMode);
-	// console.log('ids', this.fillingIds[this.currentRandomVideoIdx]);
-	
-	this.previousZoomedSN.fadeTo(this.config['timePrezoom'], 1, function()
-	{
-		// console.log('CURRENT MODE : ' + _this.currentMode);
-		_this.zoom();
-		_this.currentRandomVideoIdx++;
-	});
+    var _this = this;
+    
+    //Si l'index de lecture des vidéos en mode sans utilisateur excède le nombre maximal de vidéos, il est remis à 0 pour tourner en boucle.
+    if(this.currentRandomVideoIdx > this.config.imagesToShow)
+    {
+        this.currentRandomVideoIdx = 0;
+    }
+    
+    //Le snapshot sur lequel on doit zoomé a pour id le tableau de lecture aléatoire à l'index de lecture.
+    this.previousZoomedSN = $('#snapshotDiv-' + this.fillingIds[this.currentRandomVideoIdx]);
+    this.previousId = $('img', this.previousZoomedSN).attr('id');
+    
+    //On fait apparaître le snapshot avant de zoomer dessus.
+    this.previousZoomedSN.fadeTo(this.config.timePrezoom, 1, function()
+    {
+        _this.zoom();
+        _this.currentRandomVideoIdx++;
+    });
 }
 
 /*
  * Remplissage de la mosaïque en fonction du nombre d'images à afficher.
+ * Est appelé dans le fichier :
+ * mosaic > fonctions loadMosaic et manageControlEvents.
 */
-mosaic.prototype.showNImages = function(n)
+Mosaic.prototype.showNImages = function(n)
+{
+    //Si on bouge vers un voisin, on n'utilise pas cette fonction.
+    if(this.currentlyMoving)
+    {
+        return;
+    }
+    
+    //Si il y a plus d'un snapshot à afficher, on entre dans le mode INCOMING avec en paramètre le nombre à afficher.
+    if(n > 1 && n < this.config.imagesToShow)
+    {
+        //On met en mode incoming avec le nombre de snapshots à afficher.
+        this.currentMode = "INCOMING-" + n;
+        //On dézoom.
+        this.unzoom();
+        
+        //On initialise les gestures de recherche.
+        for(var i = 0 ; i < this.config.imagesToShow ; i++)
+        {
+            this.currentSearchGesture[i] = '';
+        }
+        
+        //On enlève les notifications.
+        this.removeNotifications();
+        //On annule les recherches.
+        this.isMosaicFiltered = false;
+        this.isCurrentlyInASearchByGesture = false;
+        //On masque les pointeurs.
+        $('#mainPointer').fadeTo(this.config.timePrezoom, 0);
+        $('#secondPointer').fadeTo(this.config.timePrezoom, 0);
+        //On enlève le spinner.
+        $('#spinner').remove();
+        //On déselectionne tous les voisins.
+        this.deselectAllNeighbours();
+        //On enlève tous les prézooms en cours.
+        $('.prezoomContainers').remove();
+    }
+    
+    //Si on doit afficher la mosaique complète.
+    if(n >= this.config.imagesToShow)
+    {
+        //Si on est en mode sans utilisateur ou bien en mode d'utilisateur approchant.
+        if(this.currentMode == "NO-USER" || this.currentMode.indexOf("INCOMING-") > -1)
+        {
+            //Si on est en intéractions Kinect.
+            if(!this.config.mouseInteractions)
+            {
+                //On met le mode à jour.
+                this.currentMode = "INCOMING-20";
+                //On dézoom.
+                this.unzoom();
+            }
+            
+            //On passe en mode mosaique.
+            this.currentMode = "MOSAIC";
+            //On enlève les notifications, et on met les notifications de sélection et de recherche.
+            this.removeNotifications();
+            this.mosaicSelectionAndSearch();
+            //On retarde le mécanisme de redémarrage.
+            clearTimeout(this.nouserTimeout);
+        }
+    }
+    
+    //Pour les snapshots à afficher.
+    for(var i = 0 ; i < n ; i++)
+    {
+        //Si les snapshots ne sont pas affichés.
+        if($('#snapshotDiv-' + this.fillingIds[i]).css('opacity') < 1)
+        {
+            //On les fait apparaître.
+            $('#snapshotDiv-' + this.fillingIds[i]).fadeTo(this.config.timeFilling, '1');
+        }
+    }
+    //Pour ceux à masquer.
+    for(var i = n ; i < this.config.imagesToShow ; i++)
+    {
+        //Si les snapshots ne sont pas masqués et qu'il ne s'agit pas du dernier snapshot en lecture aléatoire (mode NO-USER).
+        if($('#snapshotDiv-' + this.fillingIds[i]).css('opacity') > 0 && this.fillingIds[i] != this.currentRandomVideoIdx)
+        {
+            //On les masque.
+            $('#snapshotDiv-' + this.fillingIds[i]).fadeTo(this.config.timeFilling, '0');
+        }
+    }
+}
+
+/*
+ * Gère les événements de contrôle dans la mosaïque.
+ * Est appelé dans le fichier :
+ * client > fonction processMsg.
+*/
+Mosaic.prototype.manageControlEvents = function(event)
 {
-	if(this.currentlyMoving)
+    var _this = this;
+    
+    if(typeof event === 'undefined')
+    {
+        return;
+    }
+    
+    var gestureReceived = '';
+    
+    //Si l'utilisateur arrive et qu'on a la préphase activée.
+    if(event.indexOf("INCOMING-") != -1 && this.config.noUserModeEnabled)
+    {
+        //Si on peut démarrer.
+        if(this.canStart)
+        {
+            //Si on veut afficher plus de snapshots que disponibles, on affiche le maximum.
+            if(this.snapshotsToShow > this.config.imagesToShow)
+            {
+                this.snapshotsToShow = this.config.imagesToShow;
+            }
+            else
+            {
+                //On récupère le nombre de snapshots à afficher.
+                var params = event.split('-');
+                this.snapshotsToShow = params[1];
+            }
+            
+            //Si la position de l'utilisateur a changé.
+            if(event != this.lastIncomingMessage)
+            {
+                //On la met à jour et on affiche la mosaique en conséquence.
+                this.lastIncomingMessage = event;
+                this.showNImages(this.snapshotsToShow);
+            }
+        }
+        
+        //On retarde le mécanisme de redémarrage.
+        clearTimeout(this.nouserTimeout);
+        
+        //Après un délai d'absence de l'utilisateur on redémarre.
+        this.nouserTimeout = setTimeout(function()
+        {
+            window.location.reload();
+        }, this.config.timeoutNouser);
+    }
+    //S'il n'y a qu'un snapshot à afficher et qu'on est en préphase, on redémarre.
+    else if((event == "NO-USER" || event == "INCOMING-0" || event == "INCOMING-1") && this.config.noUserModeEnabled)
+    {
+        window.location.reload();
+    }
+    //Si on a fait un swipe.
+    else if(event.indexOf("SWIPE") != -1)
+    {
+        //Si le player est près et qu'on n'est pas en train de faire un swipe.
+        if(this.player && this.player.widgets && this.playerIsReady && !this.isSwipe)
+        {
+            //On est en train de faire un swipe.
+            this.isSwipe = true;
+            
+            //Si on est en mode de recherche, que la mosaique est filtrée et qu'on est arrivé au dernier marqueur.
+            if(this.currentMode == 'SEARCH' && this.isMosaicFiltered && !this.player.widgets[0].isAMarkerAhead(this.currentSearchGesture[this.centerId]))
+            {
+                //On passe à la prochaine vidéo qui contient des résultats.
+                this.playNextVideo();
+            }
+            
+			//On spécifie s'il s'agit d'un swipe left ou right.
+			var swipeType = ((event.indexOf("LEFT") != -1) ? 'left' : 'right');
+			var isSwipeLeft = ((event.indexOf("LEFT") != -1) ? true : false);
+			
+			//On passe au marqueur suivant/précédent en fonction du type de swipe.
+			this.player.widgets[0].switchToMarker(isSwipeLeft, this.currentSearchGesture[this.centerId]);
+			//Si on est en mode vidéo.
+			if(this.currentMode == 'VIDEO')
+			{
+				//On affiche la notification.
+				this.removeNotifications();
+				this.videoSwipe(swipeType);
+			}
+			//Si on est en mode recherche dans une vidéo et qu'on n'a pas de gesture de recherche.
+			else if(this.currentMode == 'SEARCH' && this.currentSearchGesture[this.centerId] == '')
+			{
+				//On affiche la notification.
+				this.removeNotifications();
+				this.searchSearchAndSwipe(swipeType);
+			}
+			//Si on est en mode recherche dans une vidéo et qu'on a une gesture de recherche.
+			else if(this.currentMode == 'SEARCH' && this.currentSearchGesture[this.centerId] != '')
+			{
+				//On affiche la notification.
+				this.removeNotifications();
+				this.searchGestureAndSwipe(this.currentSearchGesture[this.centerId], 'valid', swipeType);
+			}
+            
+            //On le fait disparaitre au bout d'un certain temps.
+            this.notifySwipeTimeout = setTimeout(function()
+            {
+                _this.isSwipe = false;
+                _this.removeNotifications();
+                
+                //Si on est en mode de recherche dans une vidéo et qu'aucune gesture n'est recherchée.
+                if(_this.currentMode == 'SEARCH' && _this.currentSearchGesture[_this.centerId] == '')
+                {
+                    _this.searchSearch();
+                }
+                //Si on est en mode de recherche dans une vidéo et qu'on a une recherche par gesture.
+                else if(_this.currentMode == 'SEARCH' && _this.currentSearchGesture[_this.centerId] != '')
+                {
+                    _this.searchGesture(_this.currentSearchGesture[_this.centerId], 'valid');
+                }
+                
+            }, this.config.timeoutNotifySwipe);
+        }
+    }
+    //Si on reçoit l'instruction correspondant à une des quatre gestures de recherche corporelle.
+    else if(event.indexOf("BEND") != -1 || event.indexOf('KNEE-UP') != -1 || event.indexOf('FALL') != -1 || event.indexOf('JUMP') != -1)
+    {
+        //On la met en minuscule.
+        gestureReceived = event.toLowerCase();
+        //On met à jour la gesture recherchée.
+        this.currentSearchGesture[centerId] = gestureReceived;
+    }
+    //Si on a effectué un hello, qu'on peut notifier l'aide et que les deux mains ne sont pas dans la zone de recherche.
+    else if(event.indexOf("HELLO") != -1 && this.canNotifyHelp && !this.areBothPointersHere)
+    {
+        //On affiche différentes aides en fonction de si on se trouve dans une vidéo ou non.
+        if(this.currentMode == 'SEARCH')
+        {
+            this.notifyHelp(false);
+        }
+        else if(this.currentMode == 'FILTER')
+        {
+            this.notifyHelp(true);
+        }
+    }
+    
+    //Si on a reçu une gesture.
+    if(gestureReceived != '')
+    {
+        //Si on est en mode recherche et que le player est prêt.
+        if(this.currentMode == "SEARCH" && this.playerIsReady)
+        {
+            //On recherche dans la vidéo.
+            this.player.widgets[0].searchByGesture(gestureReceived);
+            this.isCurrentlyInASearchByGesture = this.player.widgets[0].isCurrentlyInASearchByGesture;
+            //On notifie.
+            this.removeNotifications();
+            this.searchGesture(gestureReceived, 'valid');
+        }
+        //Si on est dans un filtrage.
+        else if(this.currentMode == "FILTER")
+        {
+            if(this.isMosaicFiltered)
+            {
+                //On notifie et on filtre.
+                this.removeNotifications();
+                this.filterGesture(gestureReceived, 'valid');
+                this.searchFilter(gestureReceived);
+            }
+        }
+        
+        //Si l'aide est affichée, on l'enlève.
+        if(this.helpDisplayed)
+        {
+            this.removeHelp();
+        }
+    }
+}
+
+/*
+ * Fonction qui s'exécute lorsque le player a chargé.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadPlayer.
+*/
+Mosaic.prototype.onPlayerLoad = function()
+{
+	var _this = this;
+	
+	if(this.currentMode == 'NO-USER')
 	{
-		return;
+		//On peut s'approcher de la kinect.
+		this.canStart = true;
 	}
 	
-	// console.log('INCOMING ----- ' + n);
-	//Si il y a plus d'un snapshot à afficher, on entre dans le mode INCOMING avec en paramètre le nombre à afficher.
-	if(n > 1 && n < this.config['imagesToShow'])
+	//Lorsque le player est en pause (par exemple lorsque le curseur arrive à la fin de la timeline).
+	if(this.player.popcorn)
 	{
-		this.currentMode = "INCOMING-" + n;
-		this.unzoom();
-		
-		for(var i = 0 ; i < this.config['imagesToShow'] ; i++)
+		this.player.popcorn.listen('pause', function()
 		{
-			this.currentSearchGesture[i] = '';
-		}
+			//Si la pause est naturelle (fin de la timeline, dézoom, déplacement vers un voisin).
+			if(!_this.userPaused)
+			{
+				//Si c'est en mode sans utilisateur.
+				if(_this.currentMode == 'NO-USER')
+				{
+					//On dézoome.
+					_this.unzoom();
+				}
+				//Sinon, si ce n'est pas causé par un déplacement ou un dézoom.
+				else if(!_this.currentlyMoving && !_this.currentlyUnzooming)
+				{
+					//Si on est en mode timeline et qu'on est en pause, c'est probablement que l'user a placé le curseur à la fin.
+					if(_this.currentMode != 'TIMELINE')
+					{
+						_this.playNextVideo();
+					}
+				}
+			}
+		});
 		
-		this.removeNotifications();
-		this.isMosaicFiltered = false;
-		this.isCurrentlyInASearchByGesture = false;
-		$('#mainPointer').fadeTo(this.config['timePrezoom'], 0);
-		$('#secondPointer').fadeTo(this.config['timePrezoom'], 0);
-		$('#spinner').remove();
-		this.deselectAllNeighbours();
-		$('.prezoomContainers').remove();
+		//Si les marqueurs ont été chargés.
+		this.player.popcorn.on("markersready", function()
+		{
+			_this.onMarkersReady();
+		});
 	}
-	// console.log('n : ' + n);
-	if(n >= this.config['imagesToShow'])
+}
+
+/*
+ * Fonction qui s'exécute lorsque les marqueurs d'une video on été placés.
+ * Est appelé dans le fichier :
+ * mosaic > fonction onPlayerLoad.
+*/
+Mosaic.prototype.onMarkersReady = function()
+{
+	var _this = this;
+	
+	//Le player est prêt.
+	this.playerIsReady = true;
+	
+	if(this.player.widgets[0])
 	{
-		// this.unzoom();
-		if(this.currentMode == "NO-USER" || this.currentMode.indexOf("INCOMING-") > -1)
+		//On spécifie à la timeline dans quel mode d'intéraction on est.
+		this.player.widgets[0].setMouseInteractions(this.config.mouseInteractions);
+		//Idem pour la langue.
+		if(this.gesturesText.length > 0)
 		{
-			if(!this.mouseInteractions)
-			{
-				this.currentMode = "INCOMING-20";
-				this.unzoom();
-			}
-			
-			this.currentMode = "MOSAIC";
-			this.removeNotifications();
-			this.mosaicSelectionAndSearch();
-			clearTimeout(this.nouserTimeout);
-			console.log('OK');
+			this.player.widgets[0].setLang(this.gesturesText);
 		}
-		//On affiche les notifications.
-		// this.notifySelectionSearchMosaicFull();
-	
-		//$('#mainPointer').fadeTo(this.config['timePrezoom'], 1);
-		//$('#secondPointer').fadeTo(this.config['timePrezoom'], 1);
 	}
 	
-	//Pour les snapshots à afficher.
-	for(var i = 0 ; i < n ; i++)
+	//Si on est en mode video ou recherche ou timeline.
+	if(this.currentMode == 'VIDEO' || this.currentMode == 'SEARCH' || this.currentMode == 'TIMELINE')
+	{
+		//On peut faire des swipes.
+		this.canSwipe = true;
+	}
+	
+	//Si aucune recherche par gesture n'est effectuée.
+	if(this.currentSearchGesture[this.centerId] == '')
 	{
-		//Si les snapshots ne sont pas affichés.
-		if($('#snapshotDiv-' + this.fillingIds[i]).css('opacity') < 1)
+		//On enlève le filtre.
+		this.removeFilter();
+	}
+	//Sinon.
+	else
+	{
+		//On entre en mode recherche dans une vidéo.
+		this.currentMode = 'SEARCH';
+		//On recherche la gesture.
+		this.player.widgets[0].searchByGesture(this.currentSearchGesture[this.centerId]);
+		this.isCurrentlyInASearchByGesture = this.player.widgets[0].isCurrentlyInASearchByGesture;
+		
+		//On va au premier marqueur trouvé.
+		if(this.timeToGoAt[this.centerId] === 0 && this.player.widgets[0].atLeastOneSearchMarker(this.currentSearchGesture[this.centerId]))
 		{
-			//On les fait apparaître.
-			$('#snapshotDiv-' + this.fillingIds[i]).fadeTo(this.config['timeFilling'], '1');
+			this.player.widgets[0].goToFirstSearchedMarker(this.currentSearchGesture[this.centerId]);
 		}
-	}
-	//Pour ceux à masquer.
-	for(var i = n ; i < this.config['imagesToShow'] ; i++)
-	{
-		//Si les snapshots ne sont pas masqués et qu'il ne s'agit pas du dernier snapshot en lecture aléatoire (mode NO-USER).
-		if($('#snapshotDiv-' + this.fillingIds[i]).css('opacity') > 0 && this.fillingIds[i] != this.currentRandomVideoIdx)
+		//Si aucun marqueur n'est trouvé.
+		else
 		{
-			//On les masque.
-			$('#snapshotDiv-' + this.fillingIds[i]).fadeTo(this.config['timeFilling'], '0');
+			//On va juste là où on était la dernière fois qu'on a joué la vidéo.
+			this.player.popcorn.currentTime(this.timeToGoAt[this.centerId]);
 		}
 	}
 }
 
 /*
- * Gère les événements de contrôle dans la mosaïque.
+ * Chargement du player basé sur le metadataplayer.
+ * Est appelé dans les fichiers :
+ * neighbours > fonction moveToNeighbour.
+ * zoomInteractions > zoom.
 */
-mosaic.prototype.manageControlEvents = function(event)
+Mosaic.prototype.loadPlayer = function(newZoomTop, newZoomLeft, newSnWidth, newSnHeight, zoomTop, zoomLeft, timeToGo)
 {
-	// console.log('manage');
-	
-	var _this = this;
-	
-	if(typeof event === 'undefined')
-	{
-		return;
-	}
-	
-	var gestureReceived = '';
-	
-	if(event.indexOf("INCOMING-") != -1 && this.prephaseEnabled)
-	{
-		// console.log(this.date() + ' ' + event);
-		// console.log('CAN START : ' + this.canStart);
-		if(this.canStart)
-		{
-			if(this.snapshotsToShow > this.config['imagesToShow'])
-			{
-				this.snapshotsToShow = this.config['imagesToShow'];
-			}
-			else
-			{
-				var params = event.split('-');
-				// console.log(event);
-				this.snapshotsToShow = params[1];
-			}
-			
-			//Si la position de l'utilisateur a changé.
-			if(event != this.lastIncomingMessage)
-			{
-				console.log(this.snapshotsToShow);
-				this.lastIncomingMessage = event;
-				this.showNImages(this.snapshotsToShow);
-			}
-		}
-		
-		clearTimeout(this.nouserTimeout);
-		this.nouserTimeout = setTimeout(function()
-		{
-			/*_this.showNImages(0);
-			_this.init();
-			_this.canStart = false;
-			_this.currentMode = "NO-USER";*/
-			
-				window.location.reload();
-			// mos = new mosaic('./config.json', default_parameters);
-			
-			console.log('NOUSER');
-		}, this.config['timeoutNouser']);
-	}
-	else if((event == "NO-USER" || event == "INCOMING-0" || event == "INCOMING-1") && this.prephaseEnabled)
-	{
-		/*this.showNImages(0);
-		this.init();
-		this.canStart = false;
-		this.currentMode = "NO-USER";*/
-		
-			window.location.reload();
-		// mos = new mosaic('./config.json', default_parameters);
-		
-		console.log('NOUSER');
-		
-		/*this.currentMode = "NO-USER";
-		this.showNImages(0);
-		this.canStart = false;
-		this.init();*/
-	}
-	// /!\/!\ //
-	else if(event.indexOf("SWIPE") != -1)
-	{
-		if(this.player && this.player.widgets && this.playerIsReady && !this.isSwipe)
-		{
-			this.isSwipe = true;
-			
-			if(this.currentMode == 'SEARCH' && this.isMosaicFiltered && !this.player.widgets[0].isAMarkerAhead(this.currentSearchGesture[this.centerId]))
-			{
-				this.playNextVideo();
-			}
-			
-			//L'utilisateur a fait un swipe left.
-			if(event.indexOf("LEFT") != -1)
-			{
-				this.player.widgets[0].switchToMarker(true, this.currentSearchGesture[this.centerId]);
-				if(this.currentMode == 'VIDEO')
-				{
-					this.removeNotifications();
-					this.videoSwipe('left');
-				}
-				else if(this.currentMode == 'SEARCH' && !this.currentSearchGesture[this.centerId])
-				{
-					this.removeNotifications();
-					this.searchSearchAndSwipe('left');
-				}
-				else if(this.currentMode == 'SEARCH' && this.currentSearchGesture[this.centerId])
-				{
-					this.removeNotifications();
-					this.searchGestureAndSwipe(this.currentSearchGesture[this.centerId], 'valid', 'left');
-				}
-			}
-			//L'utilisateur a fait un swipe right.
-			else if(event.indexOf("RIGHT") != -1)
-			{
-				this.player.widgets[0].switchToMarker(false, this.currentSearchGesture[this.centerId]);
-				if(this.currentMode == 'VIDEO')
-				{
-					this.removeNotifications();
-					this.videoSwipe('right');
-				}
-				else if(this.currentMode == 'SEARCH' && !this.currentSearchGesture[this.centerId])
-				{
-					this.removeNotifications();
-					this.searchSearchAndSwipe('right');
-				}
-				else if(this.currentMode == 'SEARCH' && this.currentSearchGesture[this.centerId])
-				{
-					this.removeNotifications();
-					this.searchGestureAndSwipe(this.currentSearchGesture[this.centerId], 'valid', 'right');
-				}
-			}
-			
-			//On le fait disparaitre au bout d'un certain temps.
-			this.notifySwipeTimeout = setTimeout(function()
-			{
-				_this.isSwipe = false;
-				
-				// /!\ //
-				_this.removeNotifications();
-				
-				if(_this.currentMode == 'SEARCH' && !_this.currentSearchGesture[_this.centerId])
-				{
-					_this.searchSearch();
-				}
-				else if(_this.currentMode == 'SEARCH' && _this.currentSearchGesture[_this.centerId])
-				{
-					_this.searchGesture(_this.currentSearchGesture[_this.centerId], 'valid');
-				}
-				
-			}, this.config['timeoutNotifySwipe']);
-		}
-	}
-	else if(event.indexOf("BEND") != -1 || event.indexOf('KNEE-UP') != -1 || event.indexOf('FALL') != -1 || event.indexOf('JUMP') != -1)
-	{
-		gestureReceived = event.toLowerCase();
-		gestureReceived = gestureReceived.replace('wave', 'hello');
-		this.currentSearchGesture[centerId] = gestureReceived;
-	}
-	else if(event.indexOf("HELLO") != -1 && this.canNotifyHelp && !this.areBothPointersHere)
-	{
-		if(this.currentMode == 'SEARCH')
-		{
-			this.notifyHelp(false);
-		}
-		else if(this.currentMode == 'FILTER')
-		{
-			this.notifyHelp(true);
-		}
-	}
-	
-	if(gestureReceived != '')
-	{
-		if(this.currentMode == "SEARCH" && this.playerIsReady)
-		{
-			this.player.widgets[0].searchByGesture(gestureReceived);
-			this.isCurrentlyInASearchByGesture = this.player.widgets[0].isCurrentlyInASearchByGesture;
-			
-			this.removeNotifications();
-			this.searchGesture(gestureReceived, 'valid');
-		}
-		else if(this.currentMode == "FILTER")
-		{
-			if(this.isMosaicFiltered)
-			{
-				// console.log('FILTER !!!');
-				// this.notifySearch1Gesture(gestureReceived, 'valid');
-				this.removeNotifications();
-				this.filterGesture(gestureReceived, 'valid');
-				this.searchFilter(gestureReceived);
-			}
-		}
-		
-		if(this.helpDisplayed)
-		{
-			this.removeHelp();
-		}
-	}
-	// /!\/!\ //
-}
-
-/*
- * Chargement du player basé sur le metadataplayer.
-*/
-mosaic.prototype.loadPlayer = function(newZoomTop, newZoomLeft, newSnWidth, newSnHeight, zoomTop, zoomLeft, timeToGo)
-{
-	var _this = this;
-	
-	//On configure les options de lancement.
-	IriSP.libFiles.defaultDir = "../lib/";
-	IriSP.widgetsDir = "./player/metadataplayer/"
-	
-	var videoToPlay = this.videos[this.centerId];
-	var currentMetadata = this.urls[this.centerId];
-	
-	var _metadata = {
-		url: currentMetadata,
-		format: 'ldt'
-	};
-	
-	var _config = {
-		gui: {
-			zoomTop: zoomTop - this.marginWidth*2,
-			zoomLeft: zoomLeft,
-			width: newSnWidth,
-			height: newSnHeight,
-			container: 'LdtPlayer',
-			default_options: {
-				metadata: _metadata
-			},
-			css:'./player/metadataplayer/LdtPlayer-core.css',
-			widgets: [
-				{
-					type: "Timeline"
-				}
-			]
-		},
-		player:{
-			type: 'html5', // player type
-			video: videoToPlay,
-			live: true,
-			height: newSnHeight,
-			width: newSnWidth,
-			autostart: true
-		}
-	};
-	
-	//On positionne le player.
-	$('.LdtPlayer').css(
-	{
-		//display: 'none',
-		position: 'absolute',
-		'background-color': '#000000',
-		top: newZoomTop,
-		left: newZoomLeft
-	});
-	
-	//On démarre le player.
-	this.player = null;
-	
-	this.player = new IriSP.Metadataplayer(_config, _metadata);
-	
-	this.player.onLoad(function()
-	{
-		if(_this.currentMode == 'NO-USER')
-		{
-			//On peut s'approcher de la kinect.
-			_this.canStart = true;
-			console.log('CAN START !');
-			// console.log(_this.player);
-			// console.log(_this.player.popcorn);
-		}
-		
-		//Lorsque le player est en pause (par exemple lorsque le curseur arrive à la fin de la timeline).
-		if(_this.player.popcorn)
-		{
-			_this.player.popcorn.listen('pause', function()
-			{
-				//Si l'utilisateur a mis en pause.
-				if(_this.userPaused)
-				{
-				
-				}
-				//Si la pause est naturelle (fin de la timeline, dézoom, déplacement vers un voisin).
-				else
-				{
-					//Si c'est en mode sans utilisateur.
-					if(_this.currentMode == 'NO-USER')
-					{
-						//On dézoome.
-						_this.unzoom();
-					}
-					//Sinon.
-					else
-					{
-						//Si ce n'est pas causé par un déplacement ou un dézoom.
-						if(!_this.currentlyMoving && !_this.currentlyUnzooming)
-						{
-							//On réinitialise la position du curseur à la prochaine lecture de la vidéo.
-							console.log('REINIT');
-							//On passe a la video suivante.
-							console.log('AUTOMOVE');
-							
-							//Si on est en mode timeline et qu'on est en pause, c'est probablement que l'user a placé le curseur à la fin.
-							if(_this.currentMode != 'TIMELINE')
-							{
-								_this.playNextVideo();
-							}
-							//_this.timeToGoAt[parseInt(_this.centerId)] = 0;
-							console.log('time to go at to 0');
-							// return;
-						}
-					}
-				}
-			});
-			// console.log('mosaic filtered : ' + _this.isMosaicFiltered);
-			
-			_this.player.popcorn.on("markersready", function()
-			{
-				_this.playerIsReady = true;
-				
-				if(_this.player.widgets[0])
-				{
-					_this.player.widgets[0].setMouseInteractions(_this.mouseInteractions);
-					if(_this.gesturesText.length > 0)
-					{
-						_this.player.widgets[0].setLang(_this.gesturesText);
-					}
-				}
-				
-				if(_this.currentMode == 'VIDEO' || _this.currentMode == 'SEARCH' || _this.currentMode == 'TIMELINE')
-				{
-					_this.canSwipe = true;
-				}
-				
-				console.log('TIME TO GO AT : ' + _this.timeToGoAt[_this.centerId], _this.centerId, _this.imgs[_this.centerId]);
-				
-				// if(_this.isMosaicFiltered)
-				// {
-					if(_this.currentSearchGesture[_this.centerId] == '')
-					{
-						_this.removeFilter();
-					}
-					else
-					{
-						_this.currentMode = 'SEARCH';
-						// console.log(_this.currentSearchGesture);
-						_this.player.widgets[0].searchByGesture(_this.currentSearchGesture[_this.centerId]);
-						_this.isCurrentlyInASearchByGesture = _this.player.widgets[0].isCurrentlyInASearchByGesture;
-						
-						if(_this.timeToGoAt[_this.centerId] === 0 && _this.player.widgets[0].atLeastOneSearchMarker(_this.currentSearchGesture[_this.centerId]))
-						{
-							_this.player.widgets[0].goToFirstSearchedMarker(_this.currentSearchGesture[_this.centerId]);
-						}
-						else
-						{
-							_this.player.popcorn.currentTime(_this.timeToGoAt[_this.centerId]);
-						}
-					}
-				// }
-				// /!\ //
-				/*else
-				{
-					if(_this.player.popcorn)
-					{
-						_this.player.popcorn.currentTime(_this.timeToGoAt[_this.centerId]);
-					}
-				}*/
-			});
-		}
-	});
-}
-
-/*
- * Permet de tester l'égalité des éléments de deux objets.
- * Pour ce faire on compare les éléments définissant ces objets.
- */
-$.fn.equals = function(compareTo)
-{
-    if (!compareTo || !compareTo.length || this.length!=compareTo.length)
+    var _this = this;
+    
+    //On configure les options de lancement.
+    IriSP.libFiles.defaultDir = "../lib/";
+    IriSP.widgetsDir = "./player/metadataplayer/"
+    
+    var videoToPlay = this.videos[this.centerId];
+    var currentMetadata = this.urls[this.centerId];
+    
+    var _metadata = {
+        url: currentMetadata,
+        format: 'ldt'
+    };
+    
+    var _config = {
+        gui: {
+            zoomTop: zoomTop - this.marginWidth*2,
+            zoomLeft: zoomLeft,
+            width: newSnWidth,
+            height: newSnHeight,
+            container: 'LdtPlayer',
+            default_options: {
+                metadata: _metadata
+            },
+            css:'./player/metadataplayer/LdtPlayer-core.css',
+            widgets: [
+                {
+                    type: "Timeline"
+                }
+            ]
+        },
+        player:{
+            type: 'html5', // player type
+            video: videoToPlay,
+            live: true,
+            height: newSnHeight,
+            width: newSnWidth,
+            autostart: true
+        }
+    };
+    
+    //On positionne le player.
+    $('.LdtPlayer').css(
     {
-        return false;
-    }
-    for (var i=0; i<this .length; i++)
+        position: 'absolute',
+        'background-color': '#000000',
+        top: newZoomTop,
+        left: newZoomLeft
+    });
+    
+    //On démarre le player.
+    this.player = null;
+    
+    this.player = new IriSP.Metadataplayer(_config, _metadata);
+    
+    this.player.onLoad(function()
     {
-        if (this[i]!==compareTo[i])
-        {
-            return false;
-        }
-    }
-    return true;
+        _this.onPlayerLoad();
+    });
 }
 
 /*
  * Charge les vidéos, les snapshots et les annotations depuis un fichier json.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadParameters.
 */
-mosaic.prototype.loadFromJson = function(path)
+Mosaic.prototype.loadFromJson = function(path)
 {
-	var _this = this;
-	var i = 0;
-
-	$.getJSON(path, function(data)
-	{
-		$.each(data, function(key, val)
-		{
-			$.each(val, function(key_video, val_video)
-			{
-				$.getJSON(val_video.metadata, function(meta)
-				{
-					if(_this.config['local'] == 'true')
-					{
-						_this.affectVideoById(val_video.metadata, meta.medias[0].url.replace('rtmp://media.iri.centrepompidou.fr/ddc_player/', './player/videos/').replace('mp4:', '').replace('video/', '').replace('ldtplatform/', '').replace('.m4v', '.mp4'));
-						// console.log(meta.medias[0].url.replace('rtmp://media.iri.centrepompidou.fr/ddc_player/', './player/videos/').replace('mp4:', '').replace('video/', '').replace('ldtplatform/', '').replace('.m4v', '.mp4'));
-					}
-					else
-					{
-						_this.affectVideoById(val_video.metadata, meta.medias[0].url.replace('rtmp://', 'http://').replace('/ddc_player/', '/').replace('mp4:', '').replace('.m4v', '.mp4'));
-						// console.log(meta.medias[0].url.replace('rtmp://', 'http://').replace('/ddc_player/', '/').replace('mp4:', '').replace('.m4v', '.mp4'));
-					}
-				});
-				_this.imgs[_this.ids[i]] = val_video.snapshot;
-				_this.urls[_this.ids[i]] = val_video.metadata;
-				//Au départ, on commence à 0 ms dans les vidéos.
-				_this.timeToGoAt[_this.ids[i]] = 0;
-				// console.log('ids : ' + _this.ids[i]);
-				i++;
-			});
-		});
-		// console.log('rdy');
-		_this.loadMosaic();
-	});
+    var _this = this;
+    var i = 0;
+    
+    //On ouvre le fichier contenant les vidéos et les adresses des métadonnées.
+    $.getJSON(path, function(data)
+    {
+        $.each(data, function(key, val)
+        {
+            $.each(val, function(key_video, val_video)
+            {
+                //On extrait les métadonnées.
+                $.getJSON(val_video.metadata, function(meta)
+                {
+                    //Si on est en chargement local.
+                    if(_this.config.local)
+                    {
+                        //On cherche les vidéos dans les métadonnées.
+                        _this.affectVideoById(val_video.metadata, meta.medias[0].url.replace('rtmp://media.iri.centrepompidou.fr/ddc_player/', './player/videos/').replace('mp4:', '').replace('video/', '').replace('ldtplatform/', '').replace('.m4v', '.mp4'));
+                    }
+                    //Si on est en chargement en ligne.
+                    else
+                    {
+                        //On met l'adresse des videos en ligne.
+                        _this.affectVideoById(val_video.metadata, meta.medias[0].url.replace('rtmp://', 'http://').replace('/ddc_player/', '/').replace('mp4:', '').replace('.m4v', '.mp4'));
+                    }
+                });
+                
+                //On affecte les images et métadonnées.
+                _this.imgs[_this.ids[i]] = val_video.snapshot;
+                _this.urls[_this.ids[i]] = val_video.metadata;
+                //Au départ, on commence à 0 ms dans les vidéos.
+                _this.timeToGoAt[_this.ids[i]] = 0;
+                i++;
+            });
+        });
+        
+        //On charge la mosaique.
+        _this.loadMosaic();
+    });
 }
 
 /*
- * Affecte une vidéo au tableau des vidéos selon son id
+ * Affecte une vidéo au tableau des vidéos selon son id.
+ * Est appelé dans le fichier :
+ * mosaic > fonction loadFromJson.
 */
-mosaic.prototype.affectVideoById = function(metadata_id, video)
+Mosaic.prototype.affectVideoById = function(metadata_id, video)
 {
-	for (i = 0 ; i < this.urls.length ; i++)
-	{
-		if(this.urls[i] == metadata_id)
-		{
-			this.videos[i] = video;
-			break;
-		}
-	}
+    for (i = 0 ; i < this.urls.length ; i++)
+    {
+        if(this.urls[i] == metadata_id)
+        {
+            this.videos[i] = video;
+            break;
+        }
+    }
 }
 
 /*
- * Rebind keypress pour body.
+ * Affiche la date actuelle pour l'affichage de messages dans la console.
+ * Est appelé dans chaque console.log() où on veut afficher la date à laquelle l'instruction est exécutée.
 */
-mosaic.prototype.reaffectKeyPress = function()
-{
-	var _this = this;
-	
-	$('body').keypress(function (event)
-	{
-		_this.manageControlEvents(event);
-	});
-}
-
-mosaic.prototype.date = function()
+Mosaic.prototype.date = function()
 {
    var date, h, min, s;
    date = new Date();