diff -r fcf435874395 -r 45c889eae324 front_idill/src/mosaic/js/mosaic.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/front_idill/src/mosaic/js/mosaic.js Fri Apr 27 14:38:23 2012 +0200 @@ -0,0 +1,596 @@ +/* +* This file is part of the TraKERS\Front IDILL package. +* +* (c) IRI +* +* 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(len, imgToShow, zoomPercentage, prezoomPercentage, zoomedMargin) +{ + //S'il s'agit d'un rectangle. + if(imgToShow % len == 0) + { + //Longueur horizontale. + this.length = len; + //Nombre d'images dans la mosaïque. + this.imagesToShow = imgToShow; + //Tableaux des urls des vidéos, des snapshots et de leur position dans la mosaïque. + this.urls = []; + this.imgs = []; + this.ids = []; + //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; + + //Temps d'intéractions/d'animations. + this.preZoomTime; + this.preUnzoomTime; + this.zoomTime; + this.unzoomTime; + this.timeNeighbourGlowing; + this.timeNeighbourUnglowing; + this.timeMovingToNeighbour; + + //Booléens permettant ou non certaines intéractions selon le contexte. + this.zoomed; + this.fullscreen; + this.canMoveToNeighbour; + + //Mode actuel. + this.currentMode; + //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; + //Pourcentage d'agrandissement lors d'un prézoom et d'un zoom. + this.prezoomPercentage = prezoomPercentage; + this.zoomPercentage = zoomPercentage; + //Espacement des snapshots après un zoom. + this.zoomedMargin = zoomedMargin; + //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; + } + else + { + //Affiche un message d'erreur. + } +} + +/* + * Méthode d'affichage de la mosaïque. + * Génère une matrice de imgs. + */ +mosaic.prototype.createMosaic = function() +{ + this.previousZoomedSN = ''; + this.previousPrezoomDiv = ''; + this.fullscreen = false; + this.canMoveToNeighbour = false; + var str = ''; + + if(this.imgs.length >= this.imagesToShow) + { + for(var i = 0 ; i < this.imagesToShow ; i++) + { + //On charge les images de petite taille pour ne pas surcharger la mosaïque lors de l'affichage global. + str += '
'; + } + } + + return str; +} + +/* + * Permet de raffraichir la mosaïque. + */ +mosaic.prototype.loadMosaic = function(imgsTab) +{ + //On affecte les chemins vers les images à la mosaïque. + this.imgs = imgsTab; + this.previousZoomedSN; + //this.width = + //On met à jour la mosaïque. + $('#mainPanel').html(this.createMosaic()); + //On récupère la taille des bordures. + this.marginWidth = $('.snapshotDivs').css('margin-bottom'); + this.marginWidth = parseFloat(mos.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.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); +} + +/* + * 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; + //Mosaïque. + var mosaic = 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.length, imgs = this.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.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 = ''; + //On génère un faux snapshot identique au précédent et qu'on va coller dessus. + var fakeSnapshot = '
' + fakeImg + '
'; + + //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 = '
'; + 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.preZoomTime); + $('#prezoomContainer-' + currentId).animate( + { + width: finalDivWidth + margin*2, + height: finalDivHeight - margin, + top: finalDivTop + margin, + left: finalDivLeft + }, this.preZoomTime); + }); + + //Si on clique sur le snapshot prézoomé, on enclenche un zoom total sur ce snapshot. + $('#prezoomContainer-' + currentId).click(function () + { + if(this.previousZoomedSN != '') + mosaic.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; + + //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.preUnzoomTime); + $('#prezoomContainer-' + id).animate( + { + width: this.snapshotWidth + margin, + height: this.snapshotHeight + margin, + top: this.previousZoomedSN.position().top, + left: this.previousZoomedSN.position().left + }, this.preUnzoomTime, function(){ $(this).remove(); this.zoomed = false; }); +} + + +/* + * Zoom d'un snapshot en plein écran. + */ +mosaic.prototype.zoom = function() +{ + var mos = this; + + //Si la mosaïque est en pleine écran, pas la peine de zoomer. + if(this.fullscreen) + return; + + //On prend les attributs nécessaires au calculs. + var margin = this.marginWidth, len = this.length, imgs = this.imagesToShow; + var initMPWidth = this.previousZoomedSN.width() * len + margin*len, initMPHeight = this.previousZoomedSN.height() * (imgs / len) + margin*(imgs / len); + var newMPWidth = initMPWidth * len + this.zoomedMargin * (len), newMPHeight = initMPHeight * (imgs / len) + this.zoomedMargin * ((imgs / len)); + var newPreMPWidth = initMPWidth * len * this.zoomPercentage + this.zoomedMargin * (len), newPreMPHeight = initMPHeight * (imgs / len) * this.zoomPercentage + this.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 * (this.zoomPercentage+1), finalDivHeight = initialDivHeight * (this.zoomPercentage+1); + var newZoomTop = -this.previousZoomedSN.position().top*(newPreMPHeight/initMPHeight) - this.zoomedMargin/2 + (initMPHeight - initMPHeight * this.zoomPercentage)/2 + 'px', newZoomLeft = -this.previousZoomedSN.position().left*(newPreMPWidth/initMPWidth) - this.zoomedMargin/2 + (initMPWidth - initMPWidth * this.zoomPercentage)/2 + 'px'; + var newSnWidth = initMPWidth * this.zoomPercentage, newSnHeight = initMPHeight * this.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 = mos.previousId.split('-'); + zoomedImgId = tab[1]; + + //Les snapshots baissent alors en opacité, donnant l'impression qu'ils sont grisés. + $('.snapshotDivs').animate( + { + width: newSnWidth, + height: newSnHeight, + margin: this.zoomedMargin/2 + 'px', + opacity: '0.4' + }, this.zoomTime); + //Le snapshot du milieu revient à une opacité optimale, ce qui attire l'attention de l'utilisateur. + $(this.previousZoomedSN).animate( + { + opacity: '1' + }, this.zoomTime); + //On zoome sur la mosaïque. + $('#mainPanel').animate( + { + width: newPreMPWidth, + height: newPreMPHeight, + top: newZoomTop, + left: newZoomLeft + }, this.zoomTime, function() + { + //On charge les interactions avec les voisins. + mos.centerId = zoomedImgId; + mos.listenToNeighbours(); + mos.currentMode = 'VIDEO'; + /*mos.unload(); + mos.localMos.loadLocalMosaic(newZoomTop, newZoomLeft, newSnWidth, newSnHeight, mos.imgs, tab[1]);*/ + }); +} + +/* + * Retour à la taille normale de la mosaïque. + */ +mosaic.prototype.unzoom = function() +{ + //Si on n'est pas en plein écran, on quitte. + if(!this.fullscreen) + return; + + //On charge les attributs nécessaires aux calculs. + var sWidth = this.snapshotWidth, sHeight = this.snapshotHeight; + var mpWidth = this.width, mpHeight = this.height; + var mos = this; + + //On passe le snapshot sur lequel on a zoomé en LD. + var zoomedImg = $('img', this.previousZoomedSN); + var src = zoomedImg.attr('src'); + zoomedImg.attr('src', src.replace('snapshots/', 'snapshots-little/')); + + //On rend leur opacité aux snapshots. Qui ne sont alors plus grisés. + $('.snapshotDivs').animate( + { + width: sWidth, + height: sHeight, + margin: this.marginWidth/2 + 'px', + opacity: '1' + }, this.unzoomTime); + //On dézoom sur la mosaïque. + $('#mainPanel').animate( + { + width: mpWidth, + height: mpHeight, + top: '0px', + left: '0px' + }, this.unzoomTime, function() + { + //On n'est plus en plein écran, et on ne peut plus se déplacer vers le prochain voisin. + mos.fullscreen = false; + mos.canMoveToNeighbour = false; + //On revient en mode MOSAIC. + mos.currentMode = 'MOSAIC'; + //On ne permet plus le déplacement vers les voisins. + $('.snapshotDivs').unbind('mouseenter', mos.changeNeighbourColor); + }); +} + +/* + * Affecte les listeners mouseenter aux voisins lors d'une vue en plein écran. + */ +mosaic.prototype.listenToNeighbours = function() +{ + ////TEST + //$('.test').empty(); + this.canMoveToNeighbour = false; + var currentLine = Math.floor(this.centerId / this.length), currentColumn = this.centerId % this.length; + var zoomedImg = $('img', this.previousZoomedSN); + var mos = this; + + //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.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.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.imagesToShow / this.length)) ? (+this.centerId + this.length) : -1; + + for(var i = 0 ; i < this.neighboursIds.length ; i++) + { + if(this.neighboursIds[i] != -1) + { + //On permet le déplacement vers les voisins. + $('#snapshotDiv-' + this.neighboursIds[i]).mouseenter(mos.changeNeighbourColor); + } + } +} + +/* + * Change la coloration d'une bordure où on se positionne lors d'une vue en plein écran. + */ +mosaic.prototype.changeNeighbourColor = function() +{ + ////TEST + //$('.test').append(mos.currentMode + " " + $(this).attr('id') + " " + 'snapshotDiv-' + mos.centerId + ','); + + //Si on est en mode VIDEO (plein écran) ET si le snapshot pointé est un voisin. + if((mos.currentMode == 'VIDEO') && ($(this).attr('id') != 'snapshotDiv-' + mos.centerId)) + { + //On crée le div cyan qui va être superposé au voisin. + var cyanDiv = '
'; + //On le colle au voisin. + $('#mainPanel').append(cyanDiv); + $('#cyan-' + $(this).attr('id')).css('top', $(this).position().top).css('left', $(this).position().left).css('width', $(this).width()).css('height', $(this).height()).css('margin', $(this).css('margin')).animate( + { + //On le fait apparaître. + opacity: '0.4' + }, timeNeighbourUnglowing, function() + { + //On peut désormais se déplacer vers ce voisin. + mos.canMoveToNeighbour = true; + }).mouseleave(mos.unchangeNeighbourColor).click(mos.moveToNeighbour); + } +} + +/* + * Change la coloration d'une bordure quittée lors d'une vue en plein écran. + */ +mosaic.prototype.unchangeNeighbourColor = function() +{ + ////TEST + //$('.test').append('un,'); + + //On ne peut plus se déplacer vers les voisins. + mos.canMoveToNeighbour = false; + + //Si on est en mode VIDEO. + if(mos.currentMode == 'VIDEO') + { + //On obtient le div de coloration superposé au voisin. + var cyanDiv = $(this); + + //On le fait disparaître progressivement. + $(this).animate( + { + opacity: '0' + }, timeNeighbourGlowing, function() + { + //Une fois invisible, on le supprime. + cyanDiv.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() +{ + //Si on ne peut pas se déplacer vers les voisins, on quitte. + if(!mos.canMoveToNeighbour) + return; + + //On obtient l'ID de destination. + var tab = $(this).attr('id').split('-'); + var destinationId = tab[2]; + + //On charge les attributs nécessaires aux calculs. + var MPCurrentTop = $('#mainPanel').position().top, MPCurrentLeft = $('#mainPanel').position().left; + var divideCoeffTop = Math.floor(destinationId / mos.length) == 0 ? 1 : Math.floor(destinationId / mos.length); + var divideCoeffLeft = destinationId % mos.length == 0 ? 1 : destinationId % mos.length; + var cyanTop = $(this).position().top, cyanLeft = $(this).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(mos.previousZoomedSN.position().top > cyanTop) + MPCurrentTop += Math.abs(cyanTop - mos.previousZoomedSN.position().top); + else if(mos.previousZoomedSN.position().top < cyanTop) + MPCurrentTop -= Math.abs(cyanTop - mos.previousZoomedSN.position().top); + //On définit pour le déplacement horizontal s'il est nécessaire de se déplacer à gauche ou à droite. + if(mos.previousZoomedSN.position().left > cyanLeft) + MPCurrentLeft += Math.abs(cyanLeft - mos.previousZoomedSN.position().left); + else if(mos.previousZoomedSN.position().left < cyanLeft) + MPCurrentLeft -= Math.abs(cyanLeft - mos.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 LD. + var currentImgSrc = $('img', mos.previousZoomedSN).attr('src'); + $('img', mos.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 cyan = $(this); + var tab = cyan.attr('id').split('-'); + mos.centerId = tab[2]; + $(this).css('opacity', '0'); + cyan.remove(); + + //On grise le snapshot qu'on vient de quitter. + mos.previousZoomedSN.animate( + { + opacity: '0.4' + }); + + //On se déplace. + $('#mainPanel').animate( + { + top: MPCurrentTop, + left: MPCurrentLeft + }, timeMovingToNeighbour, function() + { + //On fait apparaître le snapshot vers lequel on s'est déplacé. + $('#snapshotDiv-' + destinationId).animate( + { + opacity: '1' + }, mos.zoomTime, function() + { + //On recharge les voisins. + $('.snapshotDivs').unbind('mouseenter', mos.changeNeighbourColor); + mos.previousZoomedSN = $('#snapshotDiv-' + mos.centerId); + mos.listenToNeighbours(); + }); + }); +} + +/* + * 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