/*
* 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 : mosaic.js
 * 
 * Auteur : alexandre.bastien@iri.centrepompidou.fr
 * 
 * Fonctionnalités : Définit la "classe" mosaïque et définit des fonctions d'intéractions.
 */

/*
 * 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.
 */
function mosaic(config, default_conf)
{
	//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.imgs = [];
	this.ids = [];
	this.fillingIds = [];
	this.currentRandomVideoIdx = 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;
	this.fullscreen;
	this.canMoveToNeighbour;
	this.helpDisplayed;
	
	//Type de marqueur recherché dans la mosaïque (en mode filter).
	this.filterSearchedType = "";
	
	//Mode actuel.
	this.currentMode = "MOSAIC";
	//Snapshot sur lequel on a zoomé.
	this.previousZoomedSN;
	//Son ID.
	this.previousId;
	//Largeur de la marge pour le centrage vertical de la mosaïque.
	this.MPTop_margin;
	this.top_margin;

	//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;
	
	//Snapshots a afficher.
	this.snapshotsToShow = 1;
	
	//Lecteur.
	this.player;
	//Annotations (pour les marqueurs los d'un filtrage).
	this.annotations = [];
	
	//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);
}

/*
 * Méthode d'affichage de la mosaïque.
 * Génère une matrice de imgs.
 */
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(
	{
		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.previousZoomedSN = '';
		this.previousPrezoomDiv = '';
		this.fullscreen = false;
		this.canMoveToNeighbour = false;
		this.helpDisplayed = false;
		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>';
			}
		}
		
		return str;
	}
	else
	{
		alert("Le nombre d'images a afficher doit être divisible par la longueur de la mosaïque !");
	}
}

/*
 * Permet de raffraichir la mosaïque.
 */
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 = 
    //On met à jour la mosaïque.
    $('#mainPanel').html(createMosaic);
    //On récupère la taille des bordures.
    this.marginWidth = $('.snapshotDivs').css('margin-bottom');
    this.marginWidth = parseFloat(this.marginWidth)*2;
    //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.snapshotHeight = this.snapshotWidth*9/16;
    $('.snapshotDivs').css('width', this.snapshotWidth).css('height', this.snapshotHeight).css('margin', this.marginWidth/2);
    
    this.height = $('#mainPanel').innerHeight();
    //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 affiche les notifications.
	this.notifySelectionSearchMosaicFull();
	
	$('.snapshotDivs').mouseenter(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]);
		var source = sourceManager.remoteSource({url: _this.urls[i], serializer: IriSP.serializers.ldt});
		source.onLoad(function()
		{
			globalAnnotations.addElements(source.getAnnotations());
			fichiersCharges++;
			if (fichiersCharges == nbFichiers)
			{
				// instructions à exécuter quand tout est chargé
				_this.annotations = globalAnnotations;
				console.log('annotations loaded');
				_this.init();
			}
		});
	}
}

/*
 * Charge les paramètres du Front. Local (true/false) est le mode de chargement des données.
*/
mosaic.prototype.loadParameters = function(file_path)
{
	var _this = this;
	
	var supposedToBeInt = ['length', 'imagesToShow', 'totalImages', 'timePrezoom', 'timePreUnzoom', 'timeZoom', 'timeUnzoom', 'timeNeighbourGlowing', 'timeNeighbourUnglowing', 'timeMovingToNeighbour', 'timeSearchFade', 'timeNotifyFade', 'timeFilterFade', 'timeFilling', 'zoomedMargin'];
	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))
				{
					_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
			{
				_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');
		}
	});
}

/*
 * 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).
*/
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('ids', this.fillingIds[this.currentRandomVideoIdx]);
	
	this.previousZoomedSN.fadeTo(this.config['timePrezoom'], 1, function()
	{
		_this.zoom();
		_this.currentRandomVideoIdx++;
	});
}

/*
 * Remplissage de la mosaïque en fonction du nombre d'images à afficher.
*/
mosaic.prototype.showNImages = function(n)
{
	if(n > 1)
	{
		this.currentMode = "INCOMING-" + n;
		this.unzoom();
	}
	
	for(var i = 0 ; i < n ; i++)
	{
		if($('#snapshotDiv-' + i).css('opacity') < 1)
		{
			$('#snapshotDiv-' + i).fadeTo(this.config['timeFilling'], 1);
		}
	}
}

/*
 * Affiche les pointeurs.
*/
mosaic.prototype.addPointers = function()
{
	var _this = this;
	
	var mainPointer = '<div id="mainPointer" class="pointers"></div>';
	$('body').append(mainPointer);
	$('body').mousemove(function(evt)
	{
		$('#mainPointer').css(
		{
			top: evt.pageY - $('#mainPointer').height()/2,
			left: evt.pageX - $('#mainPointer').width()/2
		});
	});
}

/*
 * Gère les événements de contrôle dans la mosaïque.
*/
mosaic.prototype.manageControlEvents = function(event)
{
	console.log('manage');
	//Sinon si on a appuyé sur 'g' ou 'G'.
	if(event.which == 103 || event.which == 71)
	{
		//Si on est déjà en recherche par courbes.
		if(this.currentMode == "SEARCH" || this.currentMode == "FILTER")
		{
			//On quitte cette recherche.
			this.leaveSearch();
			//Si on était en mode recherche.
			if(this.currentMode == "SEARCH")
			{
				//On revient dans la vidéo.
				this.currentMode = "VIDEO";
			}
			else
			{
				//Sinon c'est qu'on était dans la mosaïque.
				this.currentMode = "MOSAIC";
			}
		}
		else
		{
			//Si on est en plein écran.
			if(this.fullscreen)
			{
				//On entre en mode recherche.
				this.currentMode = "SEARCH";
			}
			//Sinon.
			else
			{
				//On entre en mode filtrage.
				this.currentMode = "FILTER";
			}
			
			this.startSearch();
		}
	}
	//Si c'est a ou A.
	else if(event.which == 65 || event.which == 97)
	{
		if(!this.helpDisplayed)
		{
			this.notifyHelp();
		}
		else
		{
			this.removeHelp();
		}
	}
	//Si c'est v ou V.
	else if(event.which == 86 || event.which == 118)
	{
		this.notifySearchMarkers('run;jump;fall');
	}
	//Si c'est b ou B.
	else if(event.which == 66 || event.which == 98)
	{
		this.removeSearchMarkers();
	}
	//Si c'est k ou K.
	else if(event.which == 75 || event.which == 107)
	{
		this.searchFilter('circle');
	}
	//Si c'est l ou L.
	else if(event.which == 76 || event.which == 108)
	{
		this.removeFilter();
	}
	//Si on a appuié sur la touche 'q' ou 'Q';
	else if(event.which == 113 || event.which == 81)
	{
		this.unzoom();
	}
	else if(event.which == 111 || event.which == 79)
	{
		if(this.snapshotsToShow > this.imagesToShow)
		{
			this.snapshotsToShow = this.imagesToShow;
		}
		else
		{
			this.snapshotsToShow += 5;
		}
		
		this.showNImages(this.snapshotsToShow);
	}
}

/*
 * Zoom sur la position d'une image, 1ère partie. Durant le laps de temps de time ms, l'utilisateur a le choix de zoomer sur une autre image.
 * Après ce laps de temps, l'image zoom complétement et il n'est plus possible de sélectionner une autre image par pointage.
 */
mosaic.prototype.preZoom = function(snapshot)
{
    if(this.fullscreen)
	{
        return;
	}
	
	//On enlève les notifications initiales si elles existent.
	this.removeSelectionSearchMosaicFull();
	
    //Mosaïque.
    var _this = this;
    //Dimensions de la mosaïque.
    var h = this.height, w = this.width;
    //Longueur en images, nombre d'images et taille de bordure de la mosaïque.
    var len = this.config['length'], imgs = this.config['imagesToShow'], margin = this.marginWidth;
    //Dimensions et position d'un snapshot dans la mosaïque.
    var snHeight = this.snapshotHeight, snWidth = this.snapshotWidth;
    var sTop = snapshot.position().top, sLeft = snapshot.position().left;
    var prezoomPercentage = this.config['prezoomPercentage'];
    
    //ID de l'image actuelle.
    var currentId = $('img', snapshot).attr('id');
    
    //Si un zoom est déjà en cours, on ne zoom sur rien d'autre en attendant que ce snapshot ai dézoomé en cas de mouseleave.
    if(this.zoomed)
	{
        if($('#preZoomContainer-' + currentId) != $(this) && this.previousZoomedSN != '' && this.previousId != '')
		{
            this.preUnzoom();
		}
        else
		{
            return;
		}
	}
    
    //On indique qu'on a zoomé et on spécifie le snapshot sur lequel on a zoomé.
    this.zoomed = true;
    this.previousZoomedSN = snapshot;
    this.previousId = currentId;
    
    //On récupère les attributs de l'image.
    var fakeImg = $('img', snapshot);
    //On forme la balise de la fausse image et on passe son url pour les grands snapshots.
    fakeImg = '<img id="fake-' + currentId + '" class="snapshots" src="' + fakeImg.attr('src').replace('-little/', '/') + '" />';
    //On génère un faux snapshot identique au précédent et qu'on va coller dessus.
    var fakeSnapshot = '<div id="prezoomContainer-' + currentId + '" class="prezoomContainers"><div id="prezoomSnapshot-' + currentId + '" class="snapshotDivs">' + fakeImg + '</div></div>';
    
    //On l'ajoute à la mosaïque.
    $('#mainPanel').append(fakeSnapshot);
    //On modifie ses attributs.
    $('#fake-' + currentId).load(function()
    {
        $('#prezoomContainer-' + currentId).css('display', 'block');
        $('#prezoomContainer-' + currentId).css('top', sTop).css('left', sLeft).css('width', (snWidth + margin)).css('height', (snHeight + margin));
        $('#prezoomSnapshot-' + currentId).css('width', (snWidth)).css('height', (snHeight));
        
        //Dimensions et coordonnées initiales du div sur lequel on zoom.
        var initialDivWidth = $('#prezoomContainer-' + currentId).width(), initialDivHeight = $('#prezoomContainer-' + currentId).height();
        var initialDivTop = $('#prezoomContainer-' + currentId).position().top, initialDivLeft = $('#prezoomContainer-' + currentId).position().left;
        //Dimensions et coordonnées finales du div.
        var finalDivWidth = initialDivWidth * (prezoomPercentage+1), diffWidth = finalDivWidth - initialDivWidth, finalDivHeight = initialDivHeight + diffWidth;
        var finalDivTop = (initialDivTop - (finalDivHeight - snHeight)/2), finalDivLeft = (initialDivLeft - (finalDivWidth - snWidth)/2);
        
        //CAS PARTICULIER pour la position du snapshot zoomé : les bordures.
        if(finalDivTop < 0)
		{
            finalDivTop = -margin;
		}
        if(finalDivTop + finalDivHeight > h)
		{
            finalDivTop = h - finalDivHeight;
		}
        if(finalDivLeft < 0)
		{
            finalDivLeft = 0;
		}
        if(finalDivLeft + finalDivWidth + margin*2 > w)
		{
            finalDivLeft = w - finalDivWidth - margin*2;
		}
        
        ////Code de debug.
        ////CAUTION////
        /*var red = '<div id="red"></div>';
        if($('#red') != null || $('#red') != undefined)
            $('body').append(red);
        $('#red').css('background-color', '#FF0000').css('position', 'absolute').css('top', '0px').css('left', '0px').css('width', '100px').css('height', '100px');
        $('#red').css('top', finalDivTop).css('left', finalDivLeft).css('width', finalDivWidth).css('height', finalDivHeight);*/
        //alert("initial : " + initialDivWidth + " " + initialDivHeight + " ; final : " + finalDivWidth + " " + finalDivHeight);
        ////CAUTION////
        
        //On prézoom le div en le centrant sur le milieu du snapshot pointé.
        $('#prezoomSnapshot-' + currentId).animate(
        {
            width: finalDivWidth + margin,
            height: finalDivHeight - margin*2,
            top: finalDivTop + margin,
            left: finalDivLeft + margin
        }, _this.config['timePrezoom']);
        $('#prezoomContainer-' + currentId).animate(
        {
            width: finalDivWidth + margin*2,
            height: finalDivHeight - margin,
            top: finalDivTop + margin,
            left: finalDivLeft
        }, _this.config['timePrezoom'], function()
		{
			_this.notifyPointMosaicPrezoom();
		});
    });
    
    //Si on clique sur le snapshot prézoomé, on enclenche un zoom total sur ce snapshot.
    $('#prezoomContainer-' + currentId).click(function ()
    {
        if(this.previousZoomedSN != '')
		{
            _this.zoom();
		}
    });
}

/*
 * Dézoome sur la position de l'image. Il est à noter que ce dézoome diffère du dézoom global dans la mesure où celui-ci ne concerne que l'image sur laquelle on a zoomé.
 */
mosaic.prototype.preUnzoom = function()
{
    //Si on n'a pas zoomé, on quitte la fonction.
    if(!this.zoomed)
	{
        return;
	}
	
	this.removePointMosaicPrezoom();
    
    //On spécifie la marge afin de centrer le prédézoom.
    var margin = this.marginWidth;
    //ID du snapshot précédemment pointé.
    var id = this.previousId;
    //On ne zoom plus.
    this.zoomed = false;
    //On rétrécit le snapshot de prézoom, puis on le supprime en donnant l'illusion qu'il s'agissait du véritable snapshot, alors qu'en fait c'était un clone.
    $('#prezoomSnapshot-' + id).animate(
    {
        width: this.snapshotWidth,
        height: this.snapshotHeight,
        top: this.previousZoomedSN.position().top,
        left: this.previousZoomedSN.position().left
    }, this.config['preUnzoomTime']);
    $('#prezoomContainer-' + id).animate(
    {
        width: this.snapshotWidth + margin,
        height: this.snapshotHeight + margin,
        top: this.previousZoomedSN.position().top,
        left: this.previousZoomedSN.position().left
    }, this.config['preUnzoomTime'], function(){ $(this).remove(); this.zoomed = false; });
}


/*
 * Zoom d'un snapshot en plein écran.
 */
mosaic.prototype.zoom = function()
{
    var _this = this;
    
    //Si la mosaïque est en pleine écran, pas la peine de zoomer.
    if(this.currentMode == "VIDEO" || this.currentMode == "SEARCH")
	{
        return;
	}
	
	this.removePointMosaicPrezoom();
    
    //On prend les attributs nécessaires au calculs.
    var margin = this.marginWidth, len = this.config['length'], imgs = this.config['imagesToShow'], zoomedMargin = this.config['zoomedMargin'];
	var zoomPercentage = this.config['zoomPercentage'];
    var initMPWidth = this.previousZoomedSN.width() * len + margin*len, initMPHeight = this.previousZoomedSN.height() * (imgs / len) + margin*(imgs / len);
    var newMPWidth = initMPWidth * len + zoomedMargin * (len), newMPHeight = initMPHeight * (imgs / len) + zoomedMargin * ((imgs / len));
    var newPreMPWidth = initMPWidth * len * zoomPercentage + zoomedMargin * (len), newPreMPHeight = initMPHeight * (imgs / len) * zoomPercentage + zoomedMargin * ((imgs / len));
    
    //Dimensions et coordonnées initiales du div sur lequel on zoom.
    var initialDivWidth = this.previousZoomedSN.width(), initialDivHeight = this.previousZoomedSN.height();
    var initialDivTop = this.previousZoomedSN.position().top, initialDivLeft = this.previousZoomedSN.position().left;
    //Dimensions et coordonnées finales du div.
    var finalDivWidth = initialDivWidth * (zoomPercentage+1), finalDivHeight = initialDivHeight * (zoomPercentage+1);
    var newZoomTop = -this.previousZoomedSN.position().top*(newPreMPHeight/initMPHeight) - zoomedMargin/2 + (initMPHeight - initMPHeight * zoomPercentage)/2, newZoomLeft = -this.previousZoomedSN.position().left*(newPreMPWidth/initMPWidth) - zoomedMargin/2 + (initMPWidth - initMPWidth * zoomPercentage)/2;
    var newSnWidth = initMPWidth * zoomPercentage, newSnHeight = initMPHeight * zoomPercentage;
    
    this.preUnzoom(this);
    /*SINGULARITE*/
    this.fullscreen = true;
    
    //On passe l'image du snapshot pointé en HD.
    var zoomedImg = $('img', this.previousZoomedSN);
    var src = zoomedImg.attr('src');
    zoomedImg.attr('src', src.replace('-little/', '/'));
    
    //On récupère son ID.
    var tab, zoomedImgId;
    tab = _this.previousId.split('-');
    zoomedImgId = tab[1];
	
	//On donne les dimensions des snapshots.
	$('.snapshotDivs').animate(
	{
		width: newSnWidth,
		height: newSnHeight,
		margin: zoomedMargin/2 + 'px',
	}, this.config['zoomTime']);
	
	if(this.currentMode != 'NO-USER')
	{
		//Les snapshots baissent alors en opacité, donnant l'impression qu'ils sont grisés.
		$('.snapshotDivs').animate(
		{
			opacity: '0.4'
		}, this.config['zoomTime']);
		//Le snapshot du milieu revient à une opacité optimale, ce qui attire l'attention de l'utilisateur.
		$(this.previousZoomedSN).animate(
		{
			opacity: '1'
		}, this.config['zoomTime']);
	}
	
    //On zoome sur la mosaïque.
    $('#mainPanel').animate(
    {
        width: newPreMPWidth,
        height: newPreMPHeight,
        top: newZoomTop,
        left: newZoomLeft
    }, this.config['zoomTime'], function()
    {
        //On charge les interactions avec les voisins.
        _this.centerId = zoomedImgId;
		
		if(_this.currentMode != "NO-USER")
		{
			_this.currentMode = 'VIDEO';
			_this.listenToNeighbours();
		}
		
		_this.snTop = (zoomedImg.position().top + newZoomTop + _this.MPTop_margin), _this.snLeft = (zoomedImg.position().left + newZoomLeft);
		_this.snWidth = newSnWidth + 1, _this.snHeight = newSnHeight + 1;
		
		_this.notifyTopVideo = newZoomTop;
		_this.notifyLeftVideo = newZoomLeft;
		
		_this.loadPlayer(_this.snTop, _this.snLeft, _this.snWidth, _this.snHeight, newZoomTop, newZoomLeft);
    });
}

/*
 * Chargement du player basé sur le metadataplayer.
*/
mosaic.prototype.loadPlayer = function(newZoomTop, newZoomLeft, newSnWidth, newSnHeight, zoomTop, zoomLeft)
{
	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);
	
	if(this.currentMode == 'NO-USER')
	{
		this.player.onLoad(function()
		{
			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 != undefined)
			{
				_this.player.popcorn.listen('pause', function()
				{
					_this.unzoom();
				});
			}
		});
	}
}

/*
 * Retour à la taille normale de la mosaïque.
 */
mosaic.prototype.unzoom = function()
{
    //Si on n'est pas en plein écran, on quitte.
	console.log("'" + this.currentMode + "'");
    if(this.currentMode != "SEARCH" && this.currentMode != "VIDEO" && this.currentMode != "NO-USER" && this.currentMode.indexOf("INCOMING") == -1)
	{
        return;
	}
	
	this.snTop = 0;
	this.snLeft = 0;
	this.Width = 0;
	this.snHeight = 0;
    
    //On charge les attributs nécessaires aux calculs.
    var sWidth = this.snapshotWidth, sHeight = this.snapshotHeight;
    var mpWidth = this.width, mpHeight = this.height;
    var _this = this;
    
    //On passe le snapshot sur lequel on a zoomé en SD.
    var zoomedImg = $('img', this.previousZoomedSN);
    var src = zoomedImg.attr('src');
    zoomedImg.attr('src', src.replace('snapshots/', 'snapshots-little/'));
	
	_this.player.widgets[0].freePlayer();
	$('.LdtPlayer').remove();
	$('body').append('<div class="LdtPlayer" id="LdtPlayer"></div>');
	_this.reaffectKeyPress();
    
    //On rend leur opacité aux snapshots. Qui ne sont alors plus grisés.
    $('.snapshotDivs').animate(
    {
        width: sWidth,
        height: sHeight,
        margin: this.marginWidth/2 + 'px'
    }, this.config['unzoomTime']);
	
	if(this.currentMode != 'NO-USER')
	{
		if(this.currentMode.indexOf("INCOMING") == -1)
		{
			$('.snapshotDivs').animate(
			{
				opacity: '1'
			}, this.config['unzoomTime']);
		}
	}
	else
	{
		console.log('init');
		this.previousZoomedSN.fadeTo(this.config['unzoomTime'], 0, function()
		{
			_this.init();
		});
	}
	
    //On dézoom sur la mosaïque.
    $('#mainPanel').animate(
    {
        width: mpWidth,
        height: mpHeight,
        top: '0px',
        left: '0px'
    }, this.config['unzoomTime'], function()
    {
        //On n'est plus en plein écran, et on ne peut plus se déplacer vers le prochain voisin.
        _this.fullscreen = false;
        _this.canMoveToNeighbour = false;
		
		if(_this.currentMode != 'NO-USER')
		{
			//On revient en mode MOSAIC.
			_this.currentMode = 'MOSAIC';
			//On ne permet plus le déplacement vers les voisins.
			$('.snapshotDivs').unbind('mouseover', function()
			{
				_this.selectNeighbour();
			});
			//On remet les notifications initiales.
			_this.notifySelectionSearchMosaicFull();
		}
    });
}

/*
 * Affecte les listeners mouseenter aux voisins lors d'une vue en plein écran.
 */
mosaic.prototype.listenToNeighbours = function()
{
    ////TEST
    //$('.test').empty();
    var _this = this;
    
	this.canMoveToNeighbour = false;
    var currentLine = Math.floor(this.centerId / this.config['length']), currentColumn = this.centerId % this.config['length'];
    var zoomedImg = $('img', this.previousZoomedSN);
    
    //On cherche l'ID des voisins.
    //Si le voisin de gauche est sur la même ligne, on n'est pas sur la bordure de gauche.
    this.neighboursIds[0] = (currentColumn > 0) ? (this.centerId - 1) : -1;
    //Si le voisin de droite est sur la même ligne, on n'est pas sur la bordure de droite.
    this.neighboursIds[1] = (currentColumn < this.config['length']) ? (+this.centerId + 1) : -1;
    //Si le voisin du haut est sur la même colonne, on n'est pas sur la bordure du haut.
    this.neighboursIds[2] = (currentLine > 0) ? (this.centerId - this.config['length']) : -1;
    //Si le voisin du bas est sur la même colonne, on n'est pas sur la bordure du bas.
    this.neighboursIds[3] = (currentLine < (this.config['imagesToShow'] / this.config['length'])) ? (+this.centerId + this.config['length']) : -1;
    
	//ID du cadre voisin.
	var preId;
	
    for(var i = 0 ; i < this.neighboursIds.length ; i++)
    {
        if(this.neighboursIds[i] != -1)
        {
			preId = '#neighbourFrameBorder-' + this.neighboursIds[i];
            //On permet le déplacement vers les voisins.
            // $('#snapshotDiv-' + this.neighboursIds[i] + ', ' + preId + '-left,' + preId + '-right,' + preId + '-up,' + preId + '-down').mouseenter(mos.selectNeighbour);
			
            $('#snapshotDiv-' + this.neighboursIds[i]).mouseover(function()
			{
				_this.selectNeighbour($(this));
			});
        }
    }
}

/*
 * Change la coloration d'une bordure où on se positionne lors d'une vue en plein écran.
 */
mosaic.prototype.selectNeighbour = function(neighbour)
{
    ////TEST
    //$('.test').append(mos.currentMode + " " + $(this).attr('id') + " " + 'snapshotDiv-' + mos.centerId + ',');
	var _this = this;
	
    //Si on est en mode VIDEO (plein écran) ET si le snapshot pointé est un voisin.
    
    if((this.currentMode == 'VIDEO') && (neighbour.attr('id') != 'snapshotDiv-' + this.centerId))
    {
        //On crée le cadre qui va être superposé au voisin.
        //On le colle au voisin.
		var tab = neighbour.attr('id').split('-');
		var snapshotId = tab[1];
        var neighbourFrame = '';
		var marginValue = parseFloat(neighbour.css('margin'));
		
		neighbourFrame += '<div class="neighbourFrame" id="neighbourFrame-' + snapshotId + '"><div class="neighbourImgBg" id="neighbourImgBg-' + snapshotId + '"><div class="neighbourImg" id="neighbourImg-' + snapshotId + '"></div></div></div>';
		
        $('#mainPanel').append(neighbourFrame);
		
		//On positionne le div de background juste au niveau du voisin.
        $('#neighbourFrame-' + snapshotId).css(
		{
			'top': (+neighbour.position().top + marginValue),
			'left': (+neighbour.position().left + marginValue),
			'width': neighbour.width(),
			'height': neighbour.height()
		});
		//On positionne le div de background noir juste au niveau de l'image du voisin.
        $('#neighbourImgBg-' + snapshotId).css(
		{
			'top': marginValue,
			'left': marginValue,
			'width': neighbour.width() - marginValue*2,
			'height': neighbour.height() - marginValue*2,
		});
		//On met par dessus le div de l'image clonée du voisin.
		$('#neighbourImg-' + snapshotId).css(
		{
			'top': 0,
			'left': 0,
			'width': neighbour.width() - marginValue*2,
			'height': neighbour.height() - marginValue*2,
			'background-image': 'url("' + $('img', neighbour).attr('src') + '")',
			'background-size': neighbour.width() + 'px ' + neighbour.height() + 'px',
			'background-position': -marginValue + 'px ' + -marginValue + 'px',
			'opacity': '0.4'
		});
		
		var fId = '#neighbourFrame-' + snapshotId;
		
		$(fId).animate(
        {
            //On le fait apparaître.
            opacity: '1'
        }, _this.config['timeNeighbourGlowing'], function()
        {
            //On peut désormais se déplacer vers ce voisin.
            _this.canMoveToNeighbour = true;
        });
		//Lorsqu'on quitte un des snapshots (bien entendu le voisin en question), on retire le cadre.
		//Si on clique sur le voisin ou son cadre, on passe au voisin suivant.
		$(fId).mouseout(function()
		{
			_this.deselectNeighbour($(this))
		}).click(function()
		{
			_this.moveToNeighbour($(this))
		});
    }
}

/*
 * Change la coloration d'une bordure quittée lors d'une vue en plein écran.
 */
mosaic.prototype.deselectNeighbour = function(neighbour)
{
    ////TEST
    //$('.test').append('un,');
	
    //On ne peut plus se déplacer vers les voisins.
    this.canMoveToNeighbour = false;
    
	//On récupère le voisin.
	var neighbourFrame = neighbour;
	
    //Si on est en mode VIDEO.
    if(this.currentMode == 'VIDEO')
    {
        //On le fait disparaître progressivement.
        neighbourFrame.animate(
        {
            opacity: '0'
        }, this.config['timeNeighbourUnglowing'], function()
        {
            //Une fois invisible, on le supprime.
            neighbourFrame.remove();
        });
    }
}

/*
 * Lors d'une vue en plein écran, on se déplace vers le voisin dont l'id a été spécifié dans la fonction appelante.
 */
mosaic.prototype.moveToNeighbour = function(neighbour)
{
	var _this = this;
    //Si on ne peut pas se déplacer vers les voisins, on quitte.
    if(!_this.canMoveToNeighbour)
	{
        return;
	}
    
    //On obtient l'ID de destination.
    var tab = neighbour.attr('id').split('-');
    var destinationId = tab[1];
    
    //On charge les attributs nécessaires aux calculs.
	var length = _this.config['length'];
    var MPCurrentTop = $('#mainPanel').position().top, MPCurrentLeft = $('#mainPanel').position().left;
    var divideCoeffTop = Math.floor(destinationId / length) == 0 ? 1 : Math.floor(destinationId / length);
    var divideCoeffLeft = destinationId % length == 0 ? 1 : destinationId % length;
    var neighbourFrameTop = $('#snapshotDiv-' + destinationId).position().top, neighbourFrameLeft = $('#snapshotDiv-' + destinationId).position().left;
    
    //On définit pour le déplacement vertical s'il est nécessaire de se déplacer en haut ou en bas.
    if(_this.previousZoomedSN.position().top > neighbourFrameTop)
        MPCurrentTop += Math.abs(neighbourFrameTop - _this.previousZoomedSN.position().top);
    else if(_this.previousZoomedSN.position().top < neighbourFrameTop)
        MPCurrentTop -= Math.abs(neighbourFrameTop - _this.previousZoomedSN.position().top);
    //On définit pour le déplacement horizontal s'il est nécessaire de se déplacer à gauche ou à droite.
    if(_this.previousZoomedSN.position().left > neighbourFrameLeft)
        MPCurrentLeft += Math.abs(neighbourFrameLeft - _this.previousZoomedSN.position().left);
    else if(_this.previousZoomedSN.position().left < neighbourFrameLeft)
        MPCurrentLeft -= Math.abs(neighbourFrameLeft - _this.previousZoomedSN.position().left);
    
    //On passe le snapshot de destination en HD.
    var destinationImg = $('#snapshot-' + destinationId);
    var destinationImgSrc = destinationImg.attr('src');
    destinationImg.attr('src', destinationImgSrc.replace('snapshots-little/', 'snapshots/'));
    
    //On passe l'ancien snapshot en SD.
    var currentImgSrc = $('img', _this.previousZoomedSN).attr('src');
    $('img', _this.previousZoomedSN).attr('src', currentImgSrc.replace('snapshots/', 'snapshots-little/'));
    
    //On obtient l'ID du div de coloration du snapshot vers lequel on se déplace afin de le supprimer.
    var neighbourFrame = neighbour;
    var tab = neighbourFrame.attr('id').split('-');
    _this.centerId = tab[1];
    $(this).css('opacity', '0');
    neighbourFrame.remove();
    
	_this.player.widgets[0].freePlayer();
	$('.LdtPlayer').remove();
	$('body').append('<div class="LdtPlayer" id="LdtPlayer"></div>');
	_this.reaffectKeyPress();
	
    //On grise le snapshot qu'on vient de quitter.
    _this.previousZoomedSN.animate(
    {
        opacity: '0.4'
    });
    
    //On se déplace.
    $('#mainPanel').animate(
    {
        top: MPCurrentTop,
        left: MPCurrentLeft
    }, _this.config['timeMovingToNeighbour'], function()
    {
        //On fait apparaître le snapshot vers lequel on s'est déplacé.
        $('#snapshotDiv-' + destinationId).animate(
        {
            opacity: '1'
        }, _this.config['zoomTime'], function()
        {
            //On recharge les voisins.
            $('.snapshotDivs').unbind('mouseenter', _this.selectNeighbour);
            _this.previousZoomedSN = $('#snapshotDiv-' + _this.centerId);
            _this.listenToNeighbours();
			
			_this.notifyTopVideo = MPCurrentTop;
			_this.notifyLeftVideo = MPCurrentLeft;
			
			_this.loadPlayer((destinationImg.position().top + MPCurrentTop + _this.MPTop_margin), (destinationImg.position().left + MPCurrentLeft), destinationImg.width(), destinationImg.height(), MPCurrentTop, MPCurrentLeft);
        });
    });
}

/*
 * Déchargement du contenu de la mosaïque pour le chargement de la mosaïque locale.
 */
mosaic.prototype.unload = function()
{
    //On supprime les event listeners des objets de la mosaïque.
    $('.snapshotDivs').unbind();
    $('.snapshots').unbind();
    $('.prezoomContainers').unbind();
    //On supprime physiquement les objets.
    $('#mainPanel').empty();
}

/*
 * Centre verticalement un snapshot.
 */
/*function verticalCenterImg(mosaic, img)
{
    //On récupère sa hauteur.
    var image_height = img.height();
    //Calcule la marge du haut de chaque div pour le centrage.
    if(mosaic.top_margin == undefined)
        mosaic.top_margin = (mosaic.snapshotHeight > image_height) ? (mosaic.snapshotHeight - image_height)/2 : (image_height - mosaic.snapshotHeight)/2;
    //On centre le snapshot.
    img.css('margin-top', mosaic.top_margin).css('margin-bottom', mosaic.top_margin);
}*/

/*
 * 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)
    {
        return false;
    }
    for (var i=0; i<this .length; i++)
    {
        if (this[i]!==compareTo[i])
        {
            return false;
        }
    }
    return true;
}

/*
 * Charge les vidéos, les snapshots et les annotations depuis un fichier json.
*/
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;
				console.log('ids : ' + _this.ids[i]);
				i++;
			});
		});
		console.log('rdy');
		_this.loadMosaic();
	});
}

/*
 * Affecte une vidéo au tableau des vidéos selon son id
*/
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;
		}
	}
}

/*
 * Lance une recherche par courbes.
 */
mosaic.prototype.startSearch = function()
{
	var top, left, width, height, margin_top, inMosaic;
	//Si on est dans le cas d'un filtrage de mosaïque.
	if(this.currentMode == "FILTER")
	{
		var mainPanel = $('#mainPanel');
		top = mainPanel.position().top;
		left = mainPanel.position().left;
		width = mainPanel.width();
		height = mainPanel.height();
		margin_top = this.MPTop_margin;
		inMosaic = true;
	}
	//Sinon si c'est une recherche dans la vidéo.
	else if(this.currentMode == "SEARCH")
	{
		top = this.snTop;
		left = this.snLeft;
		width = this.snWidth;
		height = this.snHeight;
		margin_top = '0px';
		inMosaic = false;
	}
	
	this.searchCanvas = new searchCanvas(top, left, width, height, margin_top, this.timeSearchFade, inMosaic);
	this.searchCanvas.create();
}

/*
 * Quitte une recherche par courbes.
 */
mosaic.prototype.leaveSearch = function()
{
	this.searchCanvas.leaveSearch();
}

/* ===============================================
 *												   *
 *		      ZONE DES NOTIFICATIONS			   *
 *												   *
   =============================================== */

/*
 * Affiche la notification de sélection/recherche lorsque la mosaique est complète.
*/
mosaic.prototype.notifySelectionSearchMosaicFull = function()
{
	if(this.currentMode == "NO-USER" || this.currentMode.indexOf("INCOMING") == -1)
	{
		return;
	}
	
	//On spécifie les notifications en div.
	var notification_selection = "<div id='notify_selection' class='notifications'></div>";
	var notification_search = "<div id='notify_search' class='notifications'></div>";
	
	//On les ajoute à la mosaïque.
	$('#mainPanel').append(notification_selection + notification_search);

	//On calcule leurs coordonnées et dimensions.
	var notify_width = $('.notifications').width(), notify_height = $('.notifications').height();
	var notify_margin = parseInt($('.notifications').css('margin'));
	var selection_left = $(window).width() / 2 - (notify_width * 2 + notify_margin * 3) / 2;
	var search_left = selection_left + notify_width + notify_margin;
	
	//On les positionne.
	$('#notify_selection').css(
	{
		left: selection_left
	});
	$('#notify_search').css(
	{
		left: search_left
	});
	
	//On les fait apparaître.
	$('.notifications').css(
	{
		opacity: "0.9"
	});
}

/*
 * Supprime la notification de sélection/recherche lorsque la mosaique est complète.
*/
mosaic.prototype.removeSelectionSearchMosaicFull = function()
{
	$('#notify_selection, #notify_search').remove();
}

/*
 * Affiche la notification de maintient du pointage lors d'une phase de prézoom.
*/
mosaic.prototype.notifyPointMosaicPrezoom = function()
{
	if($('#notify_point').length > 0 || this.currentMode == "NO-USER" || this.currentMode.indexOf("INCOMING") == -1)
	{
		return;
	}
	
	//On spécifie les notifications en div.
	var notification_point = "<div id='notify_point' class='notifications'></div>";
	
	//On les ajoute à la mosaïque.
	$('#mainPanel').append(notification_point);
	
	//On calcule leurs coordonnées et dimensions.
	var notify_width = $('.notifications').width(), notify_height = $('.notifications').height();
	var notify_margin = parseInt($('.notifications').css('margin'));
	var point_left = $(window).width() / 2 - (notify_width) / 2 - notify_margin;
	
	//On les positionne.
	$('#notify_point').css(
	{
		left: point_left
	});
	
	//On les fait apparaître.
	$('.notifications').css(
	{
		opacity: "0.9"
	});
}

/*
 * Supprime la notification de maintient du pointage.
*/
mosaic.prototype.removePointMosaicPrezoom = function()
{	
	$('#notify_point').remove();
}

/*
 * Affiche l'aide.
*/
mosaic.prototype.notifyHelp = function()
{
	if(this.helpDisplayed)
	{
		return;
	}
	
	this.removeSelectionSearchMosaicFull();
	this.removePointMosaicPrezoom();
	
	this.helpDisplayed = true;
	
	var search_2hands_tab = ['no_motion', 'right_angle', 'contact', 'grand_jete', 'circle', 'screw', 'arc', 'rythme', 'slow', 'up_down', 'wave', 'wheel'];
	var search_body_tab = ['bend', 'fall', 'jump', 'hello', 'knee_up'];
	var controls_1hand_tab = ['selection'];
	
	//On spécifie les notifications en div.
	var search_title = "<div id='search_title'></div>";
	var search_img = "<div id='search_img' class='notify_imgs'></div>";
	var search_2hands_text = "<div id='search_2hands_text'></div>";
	var search_2hands_imgs = "<div id='search_2hands_imgs' class='notify_imgs_big'>";
	
	for(var i = 0 ; i < search_2hands_tab.length ; i++)
	{
		search_2hands_imgs += "<div id='2hands_" + search_2hands_tab[i] + "' class='notify_imgs_small'></div>";
	}
	search_2hands_imgs += "</div>";
	
	var search_body_text = "<div id='search_body_text'></div>";
	var search_body_imgs = "<div id='search_2hands_imgs' class='notify_imgs'>"
	
	for(var i = 0 ; i < search_body_tab.length ; i++)
	{
		search_body_imgs += "<div id='body_" + search_body_tab[i] + "' class='notify_imgs_small'></div>";
	}
	search_body_imgs += "</div>";
	
	var controls_title = "<div id='controls_title'></div>";
	var controls_img = "<div id='controls_img' class='notify_imgs'></div>";
	var controls_1hand_text = "<div id='controls_1hand_text'></div>";
	var controls_1hand_imgs = "<div id='controls_1hand_imgs' class='notify_imgs'>";
	
	for(var i = 0 ; i < controls_1hand_tab.length ; i++)
	{
		controls_1hand_imgs += "<div id='1hand_" + controls_1hand_tab[i] + "' class='notify_imgs_small'></div>";
	}
	controls_1hand_imgs += "</div>";
	
	var help_search = "<div id='help_search'>" + search_title + search_img + search_2hands_text + search_2hands_imgs + search_body_text + search_body_imgs + "</div>";
	var help_controls = "<div id='help_controls'>" + controls_title + controls_img + controls_1hand_text + controls_1hand_imgs + "</div>";
	
	var notification_help = "<div id='notify_help'>" + help_search + "<div id='help_sep'></div>" + help_controls + "</div>";
	
	//On les ajoute à la mosaïque.
	$('body').append(notification_help);
	
	//On calcule leurs coordonnées et dimensions.
	var notify_width = $(window).width(), notify_height = $(window).height();
	var notify_margin = parseInt($('#notify_help').css('margin'));
	var notify_ = 10;
	
	//On les positionne.
	$('#notify_help').css(
	{
		left: "0px",
		top: "0px",
		width: notify_width - notify_margin * 2,
		height: notify_height - notify_margin * 2
	});
	
	var search_width = $('#help_search').width();
	
	$('#search_title').html('Recherche');
	$('#search_2hands_text').html('Gestes à effectuer avec les deux mains');
	$('#search_body_text').html('Gestes à effectuer avec le corps entier');
	
	for(var i = 0 ; i < search_2hands_tab.length ; i++)
	{
		$("#2hands_" + search_2hands_tab[i]).css("background-image", "url('./pictos/help/" + search_2hands_tab[i] + ".png')");
		//console.log("url('../../pictos/help/" + search_2hands_tab[i] + ".png')");
	}
	
	for(var i = 0 ; i < search_body_tab.length ; i++)
	{
		$("#body_" + search_body_tab[i]).css("background-image", "url('./pictos/help/" + search_body_tab[i] + ".png')");
		//console.log("url('../../pictos/help/" + search_2hands_tab[i] + ".png')");
	}
	
	$('#controls_title').html('Contrôles');
	$('#controls_1hand_text').html('Gestes à effectuer avec une seule main');
	
	for(var i = 0 ; i < controls_1hand_tab.length ; i++)
	{
		$("#1hand_" + controls_1hand_tab[i]).css("background-image", "url('./pictos/help/" + controls_1hand_tab[i] + ".png')");
		//console.log("url('../../pictos/help/" + search_2hands_tab[i] + ".png')");
	}
	
	//On les fait apparaître.
	$('#notify_help').css(
	{
		opacity: "1"
	});
	
	$('.notify_imgs_big').css(
	{
		opacity: "1"
	});
}

/*
 * Supprime l'aide.
*/
mosaic.prototype.removeHelp = function()
{
	if(!this.helpDisplayed)
	{
		return;
	}
	
	var _this = this;
	
	$('#notify_help').fadeOut(this.timeNotifyFade, function()
	{
		_this.helpDisplayed = false;
		$('#notify_help').remove();
	});
}

/*
 * Affiche les types de marqueurs correspondants à ce qu'on a commencé à tracer lors d'une recherche.
*/
 mosaic.prototype.notifySearchMarkers = function(markersStr)
{
	if($('.notifications_inSearch_container').length > 0)
	{
		return;
	}
	
	console.log(markersStr);
	
	var markersList = markersStr.split(new RegExp(';'));
	
	var notification_search_markers = "<div class='notifications_inSearch_container'>";
	
	//On spécifie les notifications en div.
	for(var i = 0 ; i < markersList.length ; i++)
	{
		notification_search_markers += "<div class='notifications_inSearch' style='background-image: url(./pictos/big/normal/" + markersList[i] + ".png);'></div>";
	}
	
	notification_search_markers += "</div>";
	
	//On les ajoute à la mosaïque.
	$('#mainPanel').append(notification_search_markers);
	
	//On calcule leurs coordonnées et dimensions.
	var notify_width = $('.notifications_inSearch_container').width(), notify_height = $('.notifications_inSearch_container').height();
	var notify_margin = parseInt($('.notifications_inSearch').css('margin'));
	var point_left = $(window).width() / 2 - (notify_width) / 2 - notify_margin;
	var point_top = 0;
	
	if(this.currentMode == "VIDEO" || this.currentMode == "SEARCH")
	{
		point_top = -this.notifyTopVideo,
		point_left = -this.notifyLeftVideo + ($(window).width() - notify_width) / 2
	}
	
	//On les positionne.
	$('.notifications_inSearch_container').css(
	{
		left: point_left,
		top: point_top
	});
	
	//On les fait apparaître.
	$('.notifications_inSearch').css(
	{
		opacity: "0.9"
	});
}

/*
 * Supprime la notification de maintient du pointage.
*/
mosaic.prototype.removeSearchMarkers = function()
{	
	$('.notifications_inSearch_container').remove();
}

/*
 * Effectuer un filtrage de la mosaïque par rapport à un type de marqueurs.
*/
mosaic.prototype.searchFilter = function(type)
{
	var _this = this;
	
	if(this.currentMode == "MOSAIC")
	{
		this.currentMode = "FILTER";
		
		if(this.annotations.length > 0)
		{
			var gestureNumberByVideo = new Object();
			var maxAnnotationNumber = 0;
			// for(var i = 0 ; i < this.config['imagesToShow'] ; i++)
			for(var i = 0 ; i < this.annotations.length ; i++)
			{
				var current = this.annotations[i];
				if(current.annotationType.contents.title == type)
				{
					if(gestureNumberByVideo[current.source.url] == undefined || gestureNumberByVideo[current.source.url] == '')
					{
						gestureNumberByVideo[current.source.url] = 0;
					}
					
					gestureNumberByVideo[current.source.url]++;
				}
			}
			
			for(var i = 0 ; i < this.urls.length ; i++)
			{
				if(gestureNumberByVideo[this.urls[i]] == undefined || gestureNumberByVideo[this.urls[i]] == '')
				{
					gestureNumberByVideo[this.urls[i]] = 0;
				}
			}
			
			//On récupère la vidéo qui score le nombre d'occurences de la gesture le plus haut.
			for(i in gestureNumberByVideo)
			{	
				// console.log(i + " " + gestureNumberByVideo[i]);
				if(maxAnnotationNumber < gestureNumberByVideo[i])
				{
					maxAnnotationNumber = gestureNumberByVideo[i];
				}
			}
			
			var snMargin = parseInt($('.snapshotDivs').css('margin'));
			
			//On affiche l'opacité résultante pour chaque vidéo.
			for(i in gestureNumberByVideo)
			{
				//Opacité conventionelle.
				var opacity = gestureNumberByVideo[i] / maxAnnotationNumber;
				// console.log('opacity b : ' + opacity + ' for ' + gestureNumberByVideo[i]);
				//Ce qui est à zéro le restera (par conséquent le snapshot associé sera invisible).
				if(opacity > 0)
				{
					//On réhausse l'opacité de 50%.
					opacity = this.scaleIntervals(0., 1., 0.5, 1., opacity);
				}
				
				var filterIndex = this.getIdxFromMetadata(i);
				
				if(filterIndex >= 0)
				{
					console.log('#snapshotDiv-' + filterIndex + " " + _this.config['timeFilterFade'] + " " + opacity);
					$('#snapshotDiv-' + filterIndex).fadeTo(_this.config['timeFilterFade'], opacity);
					
					if(opacity == 0)
					{
						var filteredSnapshot = $('#snapshotDiv-' + filterIndex);
						
						if(filteredSnapshot.length > 0)
						{
							var hider = '<div id="filterHider-' + filterIndex + '" class="filterHiders"></div>';
							$('#mainPanel').append(hider);
							
							$('#filterHider-' + filterIndex).css(
							{
								width: +filteredSnapshot.width() + 4 * snMargin,
								height: +filteredSnapshot.height() + 4 * snMargin,
								top: filteredSnapshot.position().top - snMargin,
								left: filteredSnapshot.position().left - snMargin
							});
						}
					}
				}
			}
		}
	}
}

/*
 * Passe une valeur de l'intervalle [A, B] à l'intervalle [C, D].
*/
mosaic.prototype.scaleIntervals = function(A, B, C, D, val)
{
	return (D - C + A) * val + (C - A);
}

/*
 * Retourne l'index d'un snapshot en fonction de ses metadonnées.
*/
mosaic.prototype.getIdxFromMetadata = function(metadata)
{
	var _this = this;
	
	for(idx in this.urls)
	{
		if(this.urls[idx] == metadata)
		{
			for(id in this.ids)
			{
				if(this.ids[id] == idx)
				{
					return id;
				}
			}
		}
	}
	
	return -1;
}

/*
 * Enlève une recherche par filtre.
*/
mosaic.prototype.removeFilter = function()
{
	if(this.currentMode == "FILTER")
	{
		this.currentMode = "MOSAIC";
		
		var _this = this;
		
		$('.filterHiders').remove();
		$('.snapshotDivs').fadeTo(_this.config['timeFilterFade'], 1);
	}
}

/*
 * Rebind keypress pour body.
*/
mosaic.prototype.reaffectKeyPress = function()
{
	var _this = this;
	
	$('body').keypress(function (event)
	{
		_this.manageControlEvents(event);
	});
}