/*
* This file is part of the TraKERS\Front IDILL package.
*
* (c) IRI <http://www.iri.centrepompidou.fr/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/*
 * Projet : TraKERS
 * Module : Front IDILL
 * Fichier : pointers.js
 * 
 * Auteur : alexandre.bastien@iri.centrepompidou.fr
 * 
 * Fonctionnalités : Définit les fonctions d'interaction avec les pointeurs.
 */

/*
 * Affiche les pointeurs.
 * Est appelé dans le fichier :
 * mosaic > fonction loadMosaic.
*/
Mosaic.prototype.addPointers = function()
{
    //On n'affiche pas les pointeurs en mode d'interaction souris.
    if(this.config.mouseInteractions)
    {
        return;
    }
	
	this.minX = $(window).width();
	this.minY = $(window).height();
	this.maxX = 0;
	this.maxY = 0;
    
    //On crée et ajoute les pointeurs.
    var mainPointer = '<div id="mainPointer" class="pointers"></div>';
    var secondPointer = '<div id="secondPointer" class="pointers"></div>';
    $('body').append(mainPointer + secondPointer);
    
    //On les place au centre de l'écran.
    $('#secondPointer').css(
    {
        top: $(window).height() / 2 - $('#secondPointer').height() / 2,
        left: $(window).width() / 4 - $('#secondPointer').width() / 2
    });
    
    this.secondPointerLastX = $(window).width() / 4 - $('#secondPointer').width() / 2;
    this.secondPointerLastY = $(window).height() / 2 - $('#secondPointer').height() / 2;
    
    $('#mainPointer').css(
    {
        top: $(window).height() / 2 - $('#mainPointer').height() / 2,
        left: $(window).width() * 3 / 4 - $('#mainPointer').width() / 2
    });

    this.mainPointerLastX = $(window).width() * 3 / 4 - $('#mainPointer').width() / 2;
    this.mainPointerLastY = $(window).height() / 2 - $('#mainPointer').height() / 2;
    
    //On met à jour les anciennes coordonnées des pointeurs.
    this.mainPointerIdleStartX = this.mainPointerLastX;
    this.mainPointerIdleStartY = this.mainPointerLastY;
    this.secondPointerIdleStartX = this.secondPointerLastX;
    this.secondPointerIdleStartY = this.secondPointerLastY;
}

/*
 * Affiche/Masque le pointeur principal.
 * Main est un booléen valant vrai s'il faut afficher le pointeur.
 * Est appelé dans le fichier :
 * client > fonction processMsg.
*/
Mosaic.prototype.mainPointerDisplay = function(main)
{
    var _this = this;
    
    //On n'affiche pas les pointeurs dans le mode sans utilisateur ni utilisateur en phase d'approche.
    if(this.currentMode != 'NO-USER' && this.currentMode.indexOf('INCOMING-') == -1)
    {
        if(main)
        {
            clearTimeout(this.arrowSpinnerTimeout);
            
            $('#mainPointer').fadeTo(this.config.timeFilling, '1');
        }
    }
    
    //Si le booléen est à faux, on masque le pointeur.
    if(!main)
    {
        $('#spinner').remove();
        
        $('#mainPointer').fadeTo(this.config.timeFilling, '0');
        
        //Si on a zoomé sur une vidéo.
        if(this.currentMode == 'VIDEO' || this.currentMode == 'SEARCH')
        {
            //On annule aussi la TL s'il y a lieu.
            if(this.isTLSelected())
            {
                this.isTLRequested = false;
                clearTimeout(this.selectTLTimeout);
            }
        }
        
        if(this.isTLSelectedByMainPointer)
        {
            //On déselectionne la TL.
            this.exitTimeline('');
        }
    }
}
/*
 * Affiche/Masque le pointeur secondaire.
 * Main est un booléen valant vrai s'il faut afficher le pointeur.
 * Est appelé dans le fichier :
 * client > fonction processMsg.
*/
Mosaic.prototype.secondPointerDisplay = function(second)
{
    //On n'affiche pas les pointeurs dans le mode sans utilisateur ni utilisateur en phase d'approche.
    if(this.currentMode != 'NO-USER' && this.currentMode.indexOf('INCOMING-') == -1)
    {
        if(second)
        {
            clearTimeout(this.arrowSpinnerTimeout);
            
            $('#secondPointer').fadeTo(this.config.timeFilling, '1');
        }
    }
    
    //Si le booléen est à faux, on masque le pointeur.
    if(!second)
    {
        $('#spinner').remove();
        
        $('#secondPointer').fadeTo(this.config.timeFilling, '0');
        
        if(this.isTLSelectedBySecondPointer)
        {
            //On déselectionne la TL.
            this.exitTimeline('');
        }
    }
}

/*
 * Assure les interactions des pointeurs avec la mosaique.
 * Est appelé dans le fichier :
 * pointers > fonction refreshPointers.
*/
Mosaic.prototype.pointersMosaicInteractions = function(pointerX, pointerY, isMainPointer)
{
	var snapshot = null, isOtherPointerDisplayed, pointer, pointerImg;
	
	if(isMainPointer)
	{
		isOtherPointerDisplayed = this.isSecondPointerDisplayed;
		pointer = $('#mainPointer');
		
		if(!this.isSearchByCurvesOn)
		{
			pointerImg = 'url(./img/cursors/pointer.png)';
		}
	}
	else
	{
		isOtherPointerDisplayed = this.isMainPointerDisplayed;
		pointer = $('#secondPointer');
		
		if(!this.isSearchByCurvesOn)
		{
			pointerImg = 'url(./img/cursors/pointer2.png)';
		}
	}
	
	//On regarde si on est sur un snapshot.
	snapshot = this.pointerPositionToSN(pointerX, pointerY, true);
	
	//Si c'est le cas, on récupère son id.
	if(this.previousZoomedSN != null)
	{
		var id = this.previousZoomedSN.attr('id').replace('snapshotDiv-', '');
	}
	
	//Sinon, on effectue un preunzoom et on redonne son apparence au pointeur principal.
	if(snapshot == null)
	{
		this.isOnASnapshot = false;
		this.lastNonNullSN = this.previousZoomedSN;
		this.preUnzoom();
		
		var image
		
		pointer.css('background-image', pointerImg);
	}
	
	//Si le pointeur secondaire n'est pas affiché et qu'on est sur un snapshot ou que le dernier snapshot sur lequel on est allé n'est pas nul et que les deux pointeurs ne sont pas là en même temps.
	if(!isOtherPointerDisplayed && snapshot != null && (this.previousZoomedSN != null && snapshot.attr('id') !== this.previousZoomedSN.attr('id') || this.lastNonNullSN != null && snapshot.attr('id') === this.lastNonNullSN.attr('id')) && !this.areBothPointersHere)
	{
		//On prézoom sur le snapshot sélectionné et on change l'apparence du pointeur.
		this.isOnASnapshot = true;
		this.previousZoomedSN = snapshot;
		this.lastNonNullSN = null;
		this.preZoom(snapshot);
		
		pointer.css('background-image', 'url(./img/cursors/selector_gray.png)');
	}
	
	//Si on se trouve actuellement dans une recherche par gestures.
	if(this.isMosaicFiltered && !this.isMosaicFiltering)
	{
		//On vérifie si on se trouve sur une notification.
		this.checkIfPointerIsOnSearchNotification(pointerX, pointerY, pointer);
	}
}

/*
 * Assure les interactions des pointeurs avec la mosaique.
 * Est appelé dans le fichier :
 * pointers > fonction refreshPointers.
*/
Mosaic.prototype.pointersVideoInteractions = function(pointerX, pointerY, isMainPointer)
{
	var _this = this, pointer, thisPointerNeighbourSelectedId, pointerImg, thisPointerExitBorder, otherPointerExitBorder;
	
	if(isMainPointer)
	{
		pointer = $('#mainPointer');
		thisPointerNeighbourSelectedId = this.mainPointerNeighbourSelectedId;
		thisPointerExitBorder = this.mainPointerExitBorder;
		otherPointerExitBorder = this.secondPointerExitBorder;

		if(!this.isSearchByCurvesOn)
		{
			pointerImg = 'url(./img/cursors/pointer.png)';
		}
	}
	else
	{
		pointer = $('#secondPointer');
		thisPointerNeighbourSelectedId = this.secondPointerNeighbourSelectedId;
		thisPointerExitBorder = this.secondPointerExitBorder;
		otherPointerExitBorder = this.mainPointerExitBorder;

		if(!this.isSearchByCurvesOn)
		{
			pointerImg = 'url(./img/cursors/pointer2.png)';
		}
	}
	
	var zoomX = pointerX - this.notifyLeftVideo, zoomY = pointerY - this.notifyTopVideo;
		
	//On calcule les interactions du pointeur avec la timeline.
	this.pointersTimelineSelection(pointerX, pointerY, isMainPointer);
	
	//Si on se trouve actuellement dans une recherche par gestures.
	if(this.currentSearchGesture[this.centerId])
	{
		this.checkIfPointerIsOnSearchNotification(pointerX, pointerY, pointer);
	}
	
	//on vérifie si le pointeur est sur un snapshot zoomé.
	snapshot = this.pointerPositionToSN(zoomX, zoomY, true);
	if(snapshot == null)
	{
		pointer.css('background-image', pointerImg);
		snapshot = this.pointerPositionToAN(pointerX, pointerY);
	}
	
	var intValueOfId;
	//Si c'est le cas.
	if(snapshot != null && snapshot.length > 0)
	{
		//S'il s'agit d'un voisin additionnel.
		if(snapshot.attr('id').indexOf('borderNeighbour') != -1)
		{
			intValueOfId = parseInt(snapshot.attr('id').replace('borderNeighbour-', ''));
		}
		//Sinon si c'est un voisin normal.
		else
		{
			intValueOfId = parseInt(snapshot.attr('id').replace('snapshotDiv-', ''));
		}
	}
	else
	{
		intValueOfId = -2;
	}
	
	//Si c'est un voisin additionnel.
	if(snapshot != null && snapshot.attr('id').indexOf('borderNeighbour') != -1)
	{
		//S'il a été trouvé parmi les voisins du centre.
		if(intValueOfId > -1 && intValueOfId < 5)
		{
			//On le sélectionne.
			this.selectNeighbour(snapshot, pointer);
			if(isMainPointer)
			{
				this.mainPointerExitBorder = true;
				this.mainPointerNeighbourSelectedId = intValueOfId + this.config.imagesToShow;
			}
			else
			{
				this.secondPointerExitBorder = true;
				this.secondPointerNeighbourSelectedId = intValueOfId + this.config.imagesToShow;
			}
		}
		else
		{
			//Sinon on le déselectionne.
			if(thisPointerNeighbourSelectedId != null && thisPointerNeighbourSelectedId > -1)
			{
				this.deselectNeighbour(thisPointerNeighbourSelectedId);
			}
		}
	}
	else
	{
		//Si c'est un voisin.
		if(_.include(this.neighboursIds, intValueOfId))
		{
			//On le sélectionne.
			this.selectNeighbour(snapshot, pointer);
			if(!this.config.mouseInteractions)
			{
				clearTimeout(this.moveToNeighbourTimeout);
				if(isMainPointer)
				{
					clearTimeout(this.mainPointerExitBorderTimeout);
					this.mainPointerExitBorder = true;
					this.mainPointerNeighbourSelectedId = intValueOfId;
				}
				else
				{
					clearTimeout(this.secondPointerExitBorderTimeout);
					this.secondPointerExitBorder = true;
					this.secondPointerNeighbourSelectedId = intValueOfId;
				}
			}
			else
			{
				this.mainPointerNeighbourSelectedId = intValueOfId;
			}
			
		}
		else
		{
			//Sinon on déselectionne le snapshot.
			if(thisPointerNeighbourSelectedId != null && thisPointerNeighbourSelectedId > -1)
			{
				this.deselectNeighbour(thisPointerNeighbourSelectedId);
				
				if(!this.config.mouseInteractions)
				{
					//Si le pointeur quitte le voisin sans que l'autre pointeur ne fasse de même ailleurs.
					if(thisPointerExitBorder && !otherPointerExitBorder)
					{
						//On va vers le voisin.
						this.correctMoveToNeighbour(thisPointerNeighbourSelectedId, zoomX, zoomY);
					}
					
					//Il n'est possible de se déplacer vers un voisin que dans un certain laps de temps lorsqu'on quitte la sélection d'un voisin.
					this.moveToNeighbourTimeout = setTimeout(function()
					{
						_this.canMoveToNeighbour = false;
					}, this.config.timeoutMoveToNeighbour);
					
					if(isMainPointer)
					{
						this.mainPointerExitBorderTimeout = setTimeout(function()
						{
							_this.mainPointerExitBorder = false;
						}, this.config.timeoutUnzoom);
					}
					else
					{
						this.secondPointerExitBorderTimeout = setTimeout(function()
						{
							_this.secondPointerExitBorder = false;
						}, this.config.timeoutUnzoom);
					}
					
					//On regarde si on a voulu faire de dézoom.
					this.checkForDezoom();
				}
			}
		}
	}
}

/*
 * Assure les interactions des pointeurs avec la timeline.
 * Est appelé dans le fichier :
 * pointers > fonction pointersVideoInteractions.
*/
Mosaic.prototype.pointersTimelineSelection = function(pointerX, pointerY, isMainPointer)
{
	var _this = this, pointer, isTLSelectedByThisPointer, isTLSelectedByOtherPointer, isOtherPointerDisplayed;
	
	if(isMainPointer)
	{
		pointer = $('#mainPointer');
		isTLSelectedByThisPointer = this.isTLSelectedByMainPointer;
		isTLSelectedByOtherPointer = this.isTLSelectedBySecondPointer;
		isOtherPointerDisplayed = this.isSecondPointerDisplayed;
	}
	else
	{
		pointer = $('#secondPointer');
		isTLSelectedByThisPointer = this.isTLSelectedBySecondPointer;
		isTLSelectedByOtherPointer = this.isTLSelectedByMainPointer;
		isOtherPointerDisplayed = this.isMainPointerDisplayed;
	}
	
	//On vérifie si on veut sélectionner la TL.
	if((this.currentMode != 'TIMELINE' || this.isTLRequested) && this.playerIsReady && !isTLSelectedByOtherPointer && !this.helpDisplayed)
	{
		//Si la timeline est sélectionnée.
		if(this.isTLSelected(true, true) && !this.isTLRequested && this.isVideoReading)
		{
			//On a demandé à aller dans la TL.
			this.isTLRequested = true;
			if(isMainPointer)
			{
				this.isTLSelectedByMainPointer = true;
				this.isTLSelectedBySecondPointer = false;
			}
			else
			{
				this.isTLSelectedBySecondPointer = true;
				this.isTLSelectedByMainPointer = false;
			}
			this.currentMode = 'TIMELINE';
			this.player.widgets[0].selectTimeline();
			pointer.css('background-image', 'url(./img/cursors/selector_gray.png)');
			
			//Si on est en mode d'interaction Kinect.
			if(!this.config.mouseInteractions)
			{
				//On met le spinner gif sur le pointeur s'il existe.
				if(pointer.length > 0)
				{
					var spinner = "<img id='spinner'></div>";
					$('body').append(spinner);
					$('#spinner').css(
					{
						position: 'absolute',
						top: pointer.position().top,
						left: pointer.position().left,
						width: 85,
						height: 85,
						'z-index': 600
					});
					$('#spinner').attr('src', './img/cursors/selector_anim.gif');
				}
			}
			
			this.selectTLTimeout = setTimeout(function()
			{
				//On permet l'interaction après un laps de temps.
				_this.canSlideInTL = true;
				_this.removeNotifications();
				_this.timelineTimeline();
			}, this.config.timeoutSlideTL);
		}
		//Sinon si on était sur la timeline sans qu'elle soit sélectionnée encore et qu'on l'a quitté avant la sélection.
		else if(!this.isTLSelected(true, true) && this.isTLRequested || !this.isVideoReading)
		{
			this.isTLRequested = false;
			clearTimeout(this.selectTLTimeout);
			//On déselectionne la TL.
			this.exitTimeline('');
		}
	}
	
	//Si on a quitté la timeline après une sélection.
	if(isTLSelectedByThisPointer && !this.isTLSelected(false, true))
	{
		//On déselectionne la TL.
		this.exitTimeline('');
	}
	
	//Si on a sélectionné la TL et qu'on a le pointeur droit dessus, on peut modifier la position de lecture.
	if(this.currentMode == 'TIMELINE' && this.playerIsReady && !isOtherPointerDisplayed && this.canSlideInTL)
	{
		var time, TL = $('.Ldt-Timeline'), P = $('.LdtPlayer');
		
		//Si le pointeur est trop à gauche, on met le curseur de lecture à 0.
		if(pointerX < P.position().left)
		{
			time = 0;
		}
		//S'il est trop à droite, on le place à la fin.
		else if(pointerX > (+P.position().left + TL.width()))
		{
			time = this.player.widgets[0].source.getDuration().getSeconds();
		}
		//Sinon, on le met à la position du pointeur.
		else
		{
			time = this.player.widgets[0].scaleIntervals(P.position().left, (+P.position().left + TL.width()), 0, this.player.widgets[0].source.getDuration().getSeconds(), pointerX);
		}
		
		this.player.popcorn.currentTime(time);
		this.player.popcorn.play();
	}
}

/*
 * Raffraîchit la position des pointeurs.
 * Est appelé dans les fichiers :
 * mosaic > fonction loadMosaic.
 * client > fonction processMsg.
*/
Mosaic.prototype.refreshPointers = function(x, y, isMainPointer)
{
    if(this.currentMode == "NO-USER" || this.currentMode.indexOf('INCOMING-') != -1)
    {
        return;
    }
    
    //Si on est en mode d'interaction Kinect, on effectue un réhaussement de la position.
    if(!this.config.mouseInteractions)
    {
		this.minMax(x, y);
		x = this.scaleIntervals(this.config.kinectMinCoordX, this.config.kinectMaxCoordX, 0, $(window).width(), x);
		y = this.scaleIntervals(this.config.kinectMinCoordY, this.config.kinectMaxCoordY, 0, $(window).height(), y);
        /*x *= 7;
        y *= 7;
        x -= $(window).width() * 3 / 4;
        y -= $(window).height() * 2 / 4;*/
    }
    
	var pointer;
	
	//S'il s'agit du pointeur principal.
	if(isMainPointer)
	{
		//On affecte les éléments relatifs au pointeur principal.
		pointer = $('#mainPointer');
		
		//Si le pointeur quitte la fenêtre en X, on ne le change pas.
		if(x < 0 || x > $(window).width())
		{
			x = this.mainPointerLastX;
		}
		//Sinon, on le met à jour.
		else
		{
			this.mainPointerLastX = x;
		}
		
		//Si le pointeur quitte la fenêtre en Y, on ne le change pas.
		if(y < 0 || y > $(window).height())
		{
			y = this.mainPointerLastY;
		}
		//Sinon, on le met à jour.
		else
		{
			this.mainPointerLastY = y;
		}
	}
	//Sinon.
	else
	{
		//On affecte les éléments relatifs au pointeur principal.
		pointer = $('#secondPointer');
		
		//Si le pointeur quitte la fenêtre en X, on ne le change pas.
		if(x < 0 || x > $(window).width())
		{
			x = this.secondPointerLastX;
		}
		//Sinon, on le met à jour.
		else
		{
			this.secondPointerLastX = x;
		}
		
		//Si le pointeur quitte la fenêtre en Y, on ne le change pas.
		if(y < 0 || y > $(window).height())
		{
			y = this.secondPointerLastY;
		}
		//Sinon, on le met à jour.
		else
		{
			this.secondPointerLastY = y;
		}
	}
	
    var pointerX, pointerY;
    
    //Si on est en mode d'interaction souris, on met à jour les coordonnées utilisées dans les fonctions par celles de la souris.
    //Sinon on se base sur les coordonnées des pointeurs.
    if(this.config.mouseInteractions)
    {
        pointerX = x;
        pointerY = y;
    }
    else
    {
        pointerX = x - pointer.width()/2;
        pointerY = y - pointer.height()/2;
    }
    var _this = this;
    
    pointer.css(
    {
        top: pointerY,
        left: pointerX
    });
    
    if($('#spinner').length > 0)
    {
        $('#spinner').css(
        {
            top: pointerY,
            left: pointerX
        });
    }
    
    var snapshot = null;
    
    //Si on est dans la mosaique ou en filtrage.
    if(this.currentMode == 'MOSAIC' || this.currentMode == 'FILTER' && this.isMosaicFiltered)
    {
		this.pointersMosaicInteractions(pointerX, pointerY, isMainPointer);
    }
    //Si on est dans une vidéo ou dans une recherche ou dans la timeline.
    else if(this.currentMode == 'VIDEO' || this.currentMode == 'SEARCH' || this.currentMode == 'TIMELINE')
    {
		this.pointersVideoInteractions(pointerX, pointerY, isMainPointer);
    }
}

/*
 * Détecte si les deux pointeurs sont dans la zone de recherche et qu'ils ne bougent pas.
 * Est appelé dans le fichier :
 * client > fonction processMsg.
*/
Mosaic.prototype.detectIdlePointers = function()
{
    var mainPointer = $('#mainPointer');
    var secondPointer = $('#secondPointer');
    
    //Si la position des pointeurs au début de l'analyse d'idle change de plus ou moins leur taille par rapport à leur position actuelle.
    if(Math.abs(this.mainPointerIdleStartX - this.mainPointerLastX) > mainPointer.width() || Math.abs(this.mainPointerIdleStartY - this.mainPointerLastY) > mainPointer.height() ||
    Math.abs(this.secondPointerIdleStartX - this.secondPointerLastX) > secondPointer.width() || Math.abs(this.secondPointerIdleStartY - this.secondPointerLastY) > secondPointer.height())
    {
        //On réinitialise les dernières positions connues.
        this.mainPointerIdleStartX = this.mainPointerLastX;
        this.mainPointerIdleStartY = this.mainPointerLastY;
        this.secondPointerIdleStartX = this.secondPointerLastX;
        this.secondPointerIdleStartY = this.secondPointerLastY;
        
        this.removeIdlePointers();
        this.pointersIdleNeedLaunch = true;
    }
    
    //Si l'aide est affichée, on la masque.
    if(this.helpDisplayed && !this.mustTakeOutHands)
    {
        this.removeHelp();
    }
    
    if((this.currentMode == 'SEARCH' || this.currentMode == 'FILTER') && !this.isSearchByCurvesOn)
    {
        // this.isSearchByCurvesOn = true;
        // this.startSearch();
    }
}

/*
 * Enlève la vérification sur les pointeurs qui ne bougent pas.
 * Est appelé dans les fichiers :
 * pointers > detectIdlePointers.
 * client > fonction processMsg.
*/
Mosaic.prototype.removeIdlePointers = function()
{
    clearTimeout(this.pointersSearchIdleTimeout);
}

/*
 * Lance une vérification sur les pointeurs qui ne bougent pas.
 * Est appelé dans le fichier :
 * client > fonction processMsg.
*/
Mosaic.prototype.launchIdlePointers = function()
{
    var _this = this;
    
    //Si on est en mode TL, on ne peut pas effectuer de recherche.
    if(this.currentMode == 'TIMELINE' || (!this.playerIsReady && (this.currentMode == 'VIDEO' || this.currentMode == 'SEARCH')))
    {
        return;
    }
    
    if(this.currentMode == 'VIDEO')
    {
        //On peut le faire que sur la video au dessus de la TL.
        var mainPointer = $('#mainPointer'), secondPointer = $('#secondPointer'), 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 = mainPointer.position().left + mainPointer.width() / 2, MPy = mainPointer.position().top + mainPointer.height() / 2;
        var SPx = secondPointer.position().left + secondPointer.width() / 2, SPy = secondPointer.position().top + secondPointer.height() / 2;
        
        //Si les pointeurs ne sont pas sur le player, on part.
        if(MPx < Pleft || MPx > (+Pleft + TLwidth) || MPy < Ptop || MPy > (+Ptop + Pheight - TLheight) ||
        SPx < Pleft || SPx > (+Pleft + TLwidth) || SPy < Ptop || SPy > (+Ptop + Pheight - TLheight))
        {
            return;
        }
    }
    
    //A la fin du timeout, si rien n'est venu l'interrompre, on entre en recherche/filtrage en fonction du mode dans lequel on se trouve.
    this.pointersSearchIdleTimeout = setTimeout(function()
    {
        //Si les deux pointeurs ne sont pas dans la zone de recherche, on part.
        if(!_this.areBothPointersHere)
        {
            return;
        }
        
        //Si on est dans la mosaique.
        if(_this.currentMode == "MOSAIC")
        {
            //On passe en filtrage.
            _this.currentMode = "FILTER";
            _this.isMosaicFiltered = true;
            //On notifie.
            _this.removeNotifications();
            _this.filterSearch();
        }
        //Si on est dans la video ou dans la timeline.
        else if(_this.currentMode == "VIDEO" || _this.currentMode == "TIMELINE")
        {
            //On entre en recherche.
            _this.currentMode = "SEARCH";
            //On notifie.
            _this.removeNotifications();
            _this.searchSearch();
        }
        
        //Si on ne peut pas afficher l'aide.
        if(!_this.canNotifyHelp)
        {
            //On peut l'afficher après un laps de temps.
            _this.canNotifyHelpTimeout = setTimeout(function()
            {
                _this.canNotifyHelp = true;
            }, _this.config.timeoutCanNotifyHelp);
        }
    }, this.config.timeoutPointersIdle);
}

/*
 * Vérifie si les deux pointeurs sont dans la zone de recherche.
 * Est appelé dans le fichier :
 * client > fonction processMsg.
*/
Mosaic.prototype.checkForBothPointersHere = function()
{
    var _this = this;
    
    //Si le timeout de vérification n'a pas été lancé.
    if(!this.areBothPointersTimeoutLaunched)
    {
        //On le lance.
        this.areBothPointersHereTimeout = setTimeout(function()
        {
            //On considère après un laps de temps qu'au moins une des mains a quitté la zone sauf si on est notifié du contraire.
            _this.areBothPointersHere = false;
        }, this.config.timeoutAreBothPointersHere);
        
        //On spécifie qu'on l'a lancé.
        this.areBothPointersHereTimeoutLaunched = true;
    }
}

/*
 * Enlève la vérification de présence des deux pointeurs dans la zone de recherche.
 * Est appelé dans le fichier :
 * client > fonction processMsg.
*/
Mosaic.prototype.removeCheckForBothPointersHere = function()
{
    //On désactive le timeout.
    clearTimeout(this.areBothPointersHereTimeout);
    //On indique que la vérification n'est pas lancée.
    this.areBothPointersHereTimeoutLaunched = false;
}

/*
 * Vérifie si on se trouve sur la notification de recherche par gesture.
 * Est appelé dans le fichier :
 * pointers > fonctions pointersMosaicInteractions et pointersVideoInteractions.
*/
Mosaic.prototype.checkIfPointerIsOnSearchNotification = function(x, y, pointer)
{
    var _this = this;
    var notification_search = $('#notify_search_1gesture');
	
    //Si la notification de recherche existe (dans le player).
    if(notification_search.length > 0)
    {
        //Pictogramme actuel de la notification.
        var currentPicto = notification_search.css('background-image');
		
        //Si le pointeur est sur la notification.
        if(x > notification_search.position().left && x < (+notification_search.position().left + notification_search.width()) && y > notification_search.position().top && y < (+notification_search.position().top + notification_search.height()))
        {
            if(!this.alreadyOnNotification && ($('#spinner').length == 0 && !this.config.mouseInteractions || this.config.mouseInteractions) && !this.isTablet)
            {
                notification_search.css('background-image', currentPicto.replace('/big/' + (this.config.mouseInteractions ? 'MI/' : '') + 'valid/', '/big/' + (this.config.mouseInteractions ? 'MI/' : '') + 'hover/'));
                
                //On émet la requete de suppression de la notification.
                this.gestureDelRequested = true;
                
                //Si on est en interaction Kinect.
                if(!this.config.mouseInteractions)
                {
                    //On met le spinner gif sur le pointeur.
					if(pointer != null && pointer.length > 0)
					{
						//On modifie l'apparence du pointeur.
						pointer.css('background-image', 'url(./img/cursors/selector_gray.png)');
						var spinner = "<img id='spinner'></div>";
						$('body').append(spinner);
						$('#spinner').css(
						{
							position: 'absolute',
							top: pointer.position().top,
							left: pointer.position().left,
							width: 85,
							height: 85,
							'z-index': 600
						});
						$('#spinner').attr('src', './img/cursors/selector_anim.gif');
					}
                    
                    //Après un laps de temps sur la notification, on supprime la recherche et on revient au mode précédent.
                    this.removeNotificationByGestureTimeout = setTimeout(function()
                    {
						if(pointer.attr('id') == 'mainPanel')
						{
							pointer.css('background-image', 'url(./img/cursors/pointer.png)');
						}
						else
						{
							pointer.css('background-image', 'url(./img/cursors/pointer2.png)');
						}
						
                        //Si on est en recherche, on revient en video.
                        if(_this.currentMode == 'SEARCH')
                        {
                            _this.player.widgets[0].removeSearchByGesture();
                            _this.currentMode = 'VIDEO';
                        }
                        //Si on est en timeline, on revient en timeline.
                        else if(_this.currentMode == 'TIMELINE')
                        {
                            _this.player.widgets[0].removeSearchByGesture();
                            _this.currentMode = 'TIMELINE';
                        }
                        //Si on est en filtrage, on l'enlève.
                        else if(_this.currentMode == 'FILTER')
                        {
                            _this.removeFilter();
                        }
                        
                        _this.alreadyOnNotification = false;
                        _this.isCurrentlyInASearchByGesture = false;
						_this.gestureDelRequested = false;
                        
                        //Si on n'est ni en recherche, ni en filtrage, on enlève la gesture de recherche.
                        if(_this.currentMode != 'MOSAIC' && _this.currentMode != 'FILTER')
                        {
                            _this.currentSearchGesture[_this.centerId] = '';
                        }
                        
                        _this.canNotifyHelp = false;
                    }, this.config.timeoutRemoveNotificationByGesture);
                }
                
                this.alreadyOnNotification = true;
            }
            //On retourne vrai si on est sur la notification, et faux sinon.
            return true;
        }
        //S'il n'est pas sur la notification.
        else
        {
            //Si on était sur la notification, on remet la notification dans l'état où elle était avant qu'on soit dessus.
            if(this.alreadyOnNotification)
            {
                notification_search.css('background-image', currentPicto.replace('/big/' + (this.config.mouseInteractions ? 'MI/' : '') + 'hover/', '/big/' + (this.config.mouseInteractions ? 'MI/' : '') + 'valid/'));
                
                this.gestureDelRequested = false;
                
                clearTimeout(this.removeNotificationByGestureTimeout);
                this.alreadyOnNotification = false;
                $('#spinner').remove();
            }
            
            return false;
        }
    }
    
    return false;
}

/*
 * Si on se trouve sur la notification de recherche par gesture, on la supprime.
 * Est appelé dans les fichiers :
 * mosaic > onMouseDown.
 * zoomInteractions > unzoom.
*/
Mosaic.prototype.removeSearchNotificationIfOnIt = function(x, y)
{
    var _this = this;
    
    var notification_search = $('#notify_search_1gesture');
    
    //Si la notification de recherche existe (dans le player).
    if(notification_search.length > 0)
    {
        if(x > notification_search.position().left && x < (+notification_search.position().left + notification_search.width()) && y > notification_search.position().top && y < (+notification_search.position().top + notification_search.height()))
        {
            _this.removeNotifications();
            if(_this.currentMode == 'SEARCH')
            {
                _this.player.widgets[0].removeSearchByGesture();
                _this.currentMode = 'VIDEO';
                _this.currentSearchGesture[_this.centerId] = '';
            }
            else if(_this.currentMode == 'TIMELINE')
            {
                _this.player.widgets[0].removeSearchByGesture();
                _this.currentMode = 'TIMELINE';
                _this.currentSearchGesture[_this.centerId] = '';
            }
            else if(_this.currentMode == 'FILTER')
            {
                _this.removeFilter();
            }
            
            _this.alreadyOnNotification = false;
            _this.isCurrentlyInASearchByGesture = false;
            _this.curvesGesturesFound = false;
            _this.canDrawNextCurve = false;
            _this.isSearchByCurvesOn = false;
            _this.canNotifyHelp = false;
            
            //Si on est dans une vidéo, on laisse cette variable afin de ne pas dézoomer / bouger.
            if(_this.currentMode != 'VIDEO' && _this.currentMode != 'TIMELINE')
            {
                _this.gestureDelRequested = false;
            }
        }
    }
}

/*
 * Retourne vrai si la souris est sur la notification d'aide dans le mode d'interaction souris.
 * Est appelé dans les fichiers :
 * mosaic > onMouseMove.
*/
Mosaic.prototype.isOnHelpIcon = function(x, y)
{
	var helpIcon = $('#helpIcon');
	//S'il n'y a pas d'icone d'aide, on quitte.
	if(helpIcon.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > helpIcon.position().left && x < +helpIcon.position().left + helpIcon.width() + 2 * parseInt(helpIcon.css('margin-left')) && y > helpIcon.position().top && y < +helpIcon.position().top + helpIcon.height() + 2 * parseInt(helpIcon.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si la souris est sur la notification des crédits dans le mode d'interaction souris.
 * Est appelé dans les fichiers :
 * mosaic > onMouseMove.
*/
Mosaic.prototype.isOnCreditsIcon = function(x, y)
{
	var creditsIcon = $('#creditsIcon');
	//S'il n'y a pas d'icone des crédits, on quitte.
	if(creditsIcon.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > creditsIcon.position().left && x < +creditsIcon.position().left + creditsIcon.width() + 2 * parseInt(creditsIcon.css('margin-left')) && y > creditsIcon.position().top && y < +creditsIcon.position().top + creditsIcon.height() + 2 * parseInt(creditsIcon.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si le doigt est sur la notification de sortie dans le mode d'interaction tablettes.
*/
Mosaic.prototype.isOnExitIcon = function(x, y)
{
	var exitIcon = $('#exitIcon');
	//S'il n'y a pas d'icone d'aide, on quitte.
	if(exitIcon.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > exitIcon.position().left && x < +exitIcon.position().left + exitIcon.width() + 2 * parseInt(exitIcon.css('margin-left')) && y > exitIcon.position().top && y < +exitIcon.position().top + exitIcon.height() + 2 * parseInt(exitIcon.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si le doigt est sur la notification de retour vers la mosaïque dans le mode d'interaction tablettes.
*/
Mosaic.prototype.isOnHomeIcon = function(x, y)
{
	var homeIcon = $('#homeIcon');
	//S'il n'y a pas d'icone d'aide, on quitte.
	if(homeIcon.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > homeIcon.position().left && x < +homeIcon.position().left + homeIcon.width() + 2 * parseInt(homeIcon.css('margin-left')) && y > homeIcon.position().top && y < +homeIcon.position().top + homeIcon.height() + 2 * parseInt(homeIcon.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si le doigt est sur la notification de sortie de recherche dans le mode d'interaction tablettes.
*/
Mosaic.prototype.isOnSearchExitIcon = function(x, y)
{
	var searchExitIcon = $('#searchExitIcon');
	//S'il n'y a pas d'icone d'aide, on quitte.
	if(searchExitIcon.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > searchExitIcon.position().left && x < +searchExitIcon.position().left + searchExitIcon.width() + 2 * parseInt(searchExitIcon.css('margin-left')) && y > searchExitIcon.position().top && y < +searchExitIcon.position().top + searchExitIcon.height() + 2 * parseInt(searchExitIcon.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si le doigt est sur la notification de recherche dans le mode d'interaction tablettes.
*/
Mosaic.prototype.isOnSearchNotification = function(x, y)
{
	var searchNotification = $('#notify_search_1gesture');
	//S'il n'y a pas d'icone d'aide, on quitte.
	if(searchNotification.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > searchNotification.position().left && x < +searchNotification.position().left + searchNotification.width() + 2 * parseInt(searchNotification.css('margin-left')) && y > searchNotification.position().top && y < +searchNotification.position().top + searchNotification.height() + 2 * parseInt(searchNotification.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si la souris est sur la flèche haute dans le panneau des crédits.
*/
Mosaic.prototype.isOnCreditsUpArrow = function(x, y)
{
	var creditsNotification = $('#notify_credits'), upArrow = $('#credits_upArrow');
	//Si les crédits ne sont pas affichés, on retourne faux.
	if(creditsNotification.length <= 0)
	{
		return false;
	}
	
	var margin = parseInt(creditsNotification.css('margin-left'));
	
	//Si la flèche est visible et si la souris est dessus, on retourne true.
	if(upArrow.length > 0 && upArrow.css('opacity') == '1' && x > (+upArrow.position().left + margin) && x < (+upArrow.position().left + upArrow.width() + margin) && y > (+upArrow.position().top + margin) && y < (+upArrow.position().top + upArrow.height() + margin))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si la souris est sur la flèche basse dans le panneau des crédits.
*/
Mosaic.prototype.isOnCreditsDownArrow = function(x, y)
{
	var creditsNotification = $('#notify_credits'), downArrow = $('#credits_downArrow');
	//Si les crédits ne sont pas affichés, on retourne faux.
	if(creditsNotification.length <= 0)
	{
		return false;
	}
	
	//$('#aa').remove();
	
	var margin = parseInt(creditsNotification.css('margin-left'));
	
	//Si la flèche est visible et si la souris est dessus, on retourne true.
	if(downArrow.length > 0 && downArrow.css('opacity') == '1' && x > (+downArrow.position().left + margin) && x < (+downArrow.position().left + downArrow.width() + margin) && y > (+downArrow.position().top + margin) && y < (+downArrow.position().top + downArrow.height() + margin))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si la souris est sur une des flèches du panneau des crédits.
*/
Mosaic.prototype.isOnCreditsArrow = function(x, y)
{
	//Si la souris est sur une flèche des crédits, on retourne vrai.
	if(this.isOnCreditsUpArrow(x, y) || this.isOnCreditsDownArrow(x, y))
	{
		return true;
	}
	
	return false;
}

/*
 * Va a la page des crédits suivante.
*/
Mosaic.prototype.goToNextCreditsPage = function()
{
	console.log(this.creditsPageLength);
	//Si on est sur la dernière page, on quitte.
	if(this.creditsPageNumber + 1 >= this.creditsPageLength)
	{
		return;
	}
	
	this.creditsPageNumber++;
	this.goToCreditsPageN(this.creditsPageNumber);
}

/*
 * Va a la page des crédits précédente.
*/
Mosaic.prototype.goToPreviousCreditsPage = function()
{
	//Si on est sur la première page, on quitte.
	if(this.creditsPageNumber == 0)
	{
		return;
	}
	
	this.creditsPageNumber--;
	this.goToCreditsPageN(this.creditsPageNumber);
}

/*
 * Va à la page N des crédits.
*/
Mosaic.prototype.goToCreditsPageN = function(N)
{
	var notify_credits = $('#notify_credits'), arrowHeight = $('#credits_upArrow').height(), footer_height = $('#credits_footer').height(), margin = parseInt($('#notify_credits').css('margin-left'));
	
	//Si on est sur la première page, on cache la flèche haut.
	if(this.creditsPageNumber == 0)
	{
		$('#credits_upArrow, #credits_upArrow_mask').css(
		{
			opacity: 0
		});
		$('#credits_downArrow, #credits_downArrow_mask').css(
		{
			opacity: 1
		});
		$('#credits_container').css(
		{
			top: margin,
			height: notify_credits.height() - footer_height - arrowHeight
		});
	}
	//Si on est sur la dernière page, on cache la flèche bas.
	else if(this.creditsPageNumber + 1 >= this.creditsPageLength)
	{
		$('#credits_upArrow, #credits_upArrow_mask').css(
		{
			opacity: 1
		});
		$('#credits_downArrow, #credits_downArrow_mask').css(
		{
			opacity: 0
		});
		$('#credits_container').css(
		{
			top: +arrowHeight + margin,
			height: notify_credits.height() - footer_height - arrowHeight - margin
		});
	}
	//Si on est dans les autres pages, on affiche les deux flèches.
	else
	{
		$('#credits_upArrow, #credits_upArrow_mask').css(
		{
			opacity: 1
		});
		$('#credits_downArrow, #credits_downArrow_mask').css(
		{
			opacity: 1
		});
		$('#credits_container').css(
		{
			top: +arrowHeight + margin,
			height: notify_credits.height() - footer_height - 2 * arrowHeight - margin
		});
	}
	//console.log('in');
	//On déplace le texte des crédits.
	$('#credits_container').css(
	{
		top: -(($('#credits_container').height() - 2 * $('.credits_arrows').height()) * N - parseInt(notify_credits.css('margin-left')))// + this.column_gap)
	});
}

/*
 * Retourne vrai si la souris est sur la flèche haute dans le panneau d'aide.
*/
Mosaic.prototype.isOnHelpUpArrow = function(x, y)
{
	var helpNotification = $('#notify_help'), upArrow = $('#help_details_upArrow');
	//Si les crédits ne sont pas affichés, on retourne faux.
	if(helpNotification.length <= 0)
	{
		return false;
	}
	
	var margin = parseInt(helpNotification.css('margin-left'));
	
	//Si la flèche est visible et si la souris est dessus, on retourne true.
	if(upArrow.length > 0 && upArrow.css('opacity') == '1' && x > (+upArrow.position().left + margin) && x < (+upArrow.position().left + upArrow.width() + margin) && y > (+upArrow.position().top + margin) && y < (+upArrow.position().top + upArrow.height() + margin))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si la souris est sur la flèche basse dans le panneau d'aide.
*/
Mosaic.prototype.isOnHelpDownArrow = function(x, y)
{
	var helpNotification = $('#notify_help'), downArrow = $('#help_details_downArrow');
	//Si les crédits ne sont pas affichés, on retourne faux.
	if(helpNotification.length <= 0)
	{
		return false;
	}
	
	var margin = parseInt(helpNotification.css('margin-left'));
	
	//Si la flèche est visible et si la souris est dessus, on retourne true.
	if(downArrow.length > 0 && downArrow.css('opacity') == '1' && x > (+downArrow.position().left + margin) && x < (+downArrow.position().left + downArrow.width() + margin) && y > (+downArrow.position().top + margin) && y < (+downArrow.position().top + downArrow.height() + margin))
	{
		return true;
	}
	
	return false;
}

/*
 * Retourne vrai si la souris est sur une des flèches du panneau d'aide.
*/
Mosaic.prototype.isOnHelpArrow = function(x, y)
{
	//Si la souris est sur une flèche des crédits, on retourne vrai.
	if(this.isOnHelpUpArrow(x, y) || this.isOnHelpDownArrow(x, y))
	{
		return true;
	}
	
	return false;
}

/*
 * Va a la page d'aide suivante.
*/
Mosaic.prototype.goToNextHelpDetailsPage = function()
{
	// console.log(+this.helpDetailsPageNumber + 1  + ' >= ' + this.helpDetailsPageLength);
	//Si on est sur la dernière page, on quitte.
	if(this.helpDetailsPageNumber + 1 >= this.helpDetailsPageLength)
	{
		return;
	}
	
	this.helpDetailsPageNumber++;
	this.goToHelpDetailsPageN(this.helpDetailsPageNumber);
}

/*
 * Va a la page d'aide précédente.
*/
Mosaic.prototype.goToPreviousHelpDetailsPage = function()
{
	//Si on est sur la première page, on quitte.
	if(this.helpDetailsPageNumber == 0)
	{
		return;
	}
	
	this.helpDetailsPageNumber--;
	this.goToHelpDetailsPageN(this.helpDetailsPageNumber);
}

/*
 * Va à la page N des crédits.
*/
Mosaic.prototype.goToHelpDetailsPageN = function(N)
{
	var notify_help = $('#notify_help'), arrowHeight = $('#help_details_upArrow').height(), margin = parseInt($('#notify_help').css('margin-left')) + parseInt($('#notify_help').css('border-width'));
	
	//Si on est sur la première page, on cache la flèche haut.
	if(this.helpDetailsPageNumber == 0)
	{
		//On affiche la page principale.
		$('#help_search, #help_controls').css('opacity', '1');
		
		$('#help_details_upArrow').css(
		{
			opacity: 0
		});
		$('#help_details_downArrow').css(
		{
			opacity: 1
		});
		$('.help_details_containers').css(
		{
			top: margin,
			// height: notify_help.height() - arrowHeight
		});
	}
	//Si on est sur la dernière page, on cache la flèche bas.
	else if(this.helpDetailsPageNumber + 1 >= this.helpDetailsPageLength)
	{
		//On masque la page principale.
		$('#help_search, #help_controls').css('opacity', '0');
		
		$('#help_details_upArrow').css(
		{
			opacity: 1
		});
		$('#help_details_downArrow').css(
		{
			opacity: 0
		});
		$('.help_details_containers').css(
		{
			top: +arrowHeight + margin,
			// height: notify_help.height() - arrowHeight - margin
		});
	}
	//Si on est dans les autres pages, on affiche les deux flèches.
	else
	{
		//On masque la page principale.
		$('#help_search, #help_controls').css('opacity', '0');
		
		$('#help_details_upArrow').css(
		{
			opacity: 1
		});
		$('#help_details_downArrow').css(
		{
			opacity: 1
		});
		$('.help_details_containers').css(
		{
			top: +arrowHeight + margin,
			// height: notify_help.height() - 2 * arrowHeight - margin
		});
	}
	
	//On déplace le texte des crédits.
	$('.help_details_containers').css(
	{
		// top: -($('.help_details_containers').height() * N - margin)// + this.column_gap)
		top: $('#notify_help').height() - ($('#notify_help').height() * N - margin)// + this.column_gap)
	});
}

/*
 * Retourne vrai si le doigt est sur le bouton de lecture de video dans le mode d'interaction tablettes.
*/
Mosaic.prototype.isOnPlayButton = function(x, y)
{
	var playButton = $('#tabletPlayButton');
	//S'il n'y a pas d'icone d'aide, on quitte.
	if(playButton.length <= 0)
	{
		return;
	}
	
	//Si la souris est sur l'icone, on retourne true.
	if(x > playButton.position().left && x < +playButton.position().left + playButton.width() + 2 * parseInt(playButton.css('margin-left')) && y > playButton.position().top && y < +playButton.position().top + playButton.height() + 2 * parseInt(playButton.css('margin-left')))
	{
		return true;
	}
	
	return false;
}

/*
 * Change la couleur des pointeurs pendant le tracé d'une courbe.
*/
Mosaic.prototype.pointersGreen = function()
{
	$('#mainPointer').css('background-image', 'url(./img/cursors/pointerC.png)');
	$('#secondPointer').css('background-image', 'url(./img/cursors/pointer2C.png)');
}

Mosaic.prototype.minMax = function(x, y)
{
	if(x < this.minX) {this.minX = x;}
	if(x > this.maxX) {this.maxX = x;}
	if(y < this.minY) {this.minY = y;}
	if(y > this.maxY) {this.maxY = y;}
}