X-Git-Url: http://git.euphorik.ch/?p=euphorik.git;a=blobdiff_plain;f=js%2Fchat%2Fconversations.js;fp=js%2Fchat%2Fconversations.js;h=063220c578dbe5ce84c5174b2a25eb7ce8613c86;hp=0000000000000000000000000000000000000000;hb=e49a1c483f1751f129c0766d1061b3da44b60581;hpb=d6dcd0fd8af56bd4791aa4e621c2e5058033c37a diff --git a/js/chat/conversations.js b/js/chat/conversations.js new file mode 100644 index 0000000..063220c --- /dev/null +++ b/js/chat/conversations.js @@ -0,0 +1,416 @@ +// coding: utf-8 +// Copyright 2008 Grégory Burri +// +// This file is part of Euphorik. +// +// Euphorik is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Euphorik is distributed in the hope that it will be useful, +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Euphorik. If not, see . + +/*jslint laxbreak:true */ + +/** + * Représente l'ensemble des conversations affichés. + */ +euphorik.Conversations = function(client, formater, util, communication, fragment) { + this.client = client; + this.formater = formater; + this.util = util; + this.fragment = fragment; + + // un ensemble des messages (id) auquel l'utilisateur répond (vider après l'envoie du message courant) + this.messagesRepond = {}; + + this.conversations = []; // les conversations, la première représente la conversation principale + + this.nouvelleConversation(0); + + this.trollIdCourant = 0; + + this.comet = communication.createCometConnection("chat"); +}; + +// les messages auquels l'utilisateur répond et qui sont insérés dans le document XHTML +// ont leur id prefixé par cette valeur, cela permet de les distinguer des "vrais" messages +euphorik.Conversations.prototype.prefixIdMessage = "rep"; + +/** + * Permet de définir un message comme étant ou n'étant plus un message auquel l'utilisateur + * répond. + */ +euphorik.Conversations.prototype.toggleMessageRepond = function(mess) { + // est-ce que l'on répond déjà à ce message ? si oui alors on l'enlève de la liste + if (mess.id in this.messagesRepond) { + this.enleverMessageRepond(mess); + return; + } + + this.ajouterMessageRepond(mess); +}; + +/** + * Met à jour le fragment de l'url renseignant sur les conversations ouvertes. + */ +euphorik.Conversations.prototype.mettreAJourFragment = function() { + conv = []; + for(var i = 1; i < this.conversations.length; i++) { + conv.push(this.conversations[i].racine.id); + } + this.fragment.setVal("conv", conv); + this.fragment.write(); +}; + +/** + * Enlève tous les messages auquel l'utilisateur souhaite répondre. + */ +euphorik.Conversations.prototype.enleverMessagesRepond = function() { + var thisConversations = this; + + objectEach(this.messagesRepond, function(messId, mess) { + thisConversations.enleverMessageRepond(mess); + }); + + // on réinitialise pour être sur que tout est bien enlevé + this.messagesRepond = {}; + $("#conversations .message").removeClass("repondEnEvidence"); + $("form#posterMessage #repondA .messages").empty(); +}; + +/** + * Définit un message comme n'y répondant plus. + */ +euphorik.Conversations.prototype.enleverMessageRepond = function(mess) { + $(this.exprIdsPotentiels(mess)).removeClass("repondEnEvidence"); + $("#" + mess.getId(this.prefixIdMessage)).remove(); + delete this.messagesRepond[mess.id]; + this.rafraichireNombreMessagesRepond(); +}; + +/** + * Définit un message comme y répondant. + */ +euphorik.Conversations.prototype.ajouterMessageRepond = function(mess) { + var thisConversations = this; + + // est-ce que le message fait partie de la même conversation que les autres messages ? + // TODO : solution plus élégante pour prendre un mess parmis messagesRepond !? + var mess2; + for (mess2 in this.messagesRepond) { + if (this.messagesRepond.hasOwnProperty(mess2)) { + mess2 = this.messagesRepond[mess2]; + break; + } + } + + if (mess2 && mess2.racineId !== mess.racineId) { + this.util.messageDialog("Impossible de répondre à deux messages ne faisant pas partie de la même conversation"); + return; + } + + $("form#posterMessage #repondA .messages").append(mess.XHTML(undefined, this.prefixIdMessage)); + this.messagesRepond[mess.id] = mess; + + // ajout la classe 'repondEnEvidence' au message. comme il peut se trouver potentiellement dans + // chaque conversation on construit tous les id potentiels + $(mess.getId(this.prefixIdMessage) + ", " + this.exprIdsPotentiels(mess)).addClass("repondEnEvidence"); + + $("#" + mess.getId(this.prefixIdMessage)).click( + function() { + $(this).fadeOut("normal", function() { + thisConversations.enleverMessageRepond(mess); + $("form#posterMessage #repondA .messages").hide(); + }); + } + ); + this.rafraichireNombreMessagesRepond(); +}; + +/** + * Construit tous les id potentiels d'un message, renvoie par exemple : + * "conv9b28mess1h, conv9b2amess1h, conv9b32mess1h" + */ +euphorik.Conversations.prototype.exprIdsPotentiels = function(mess) { + var exprMess = ""; + this.conversations.each(function(i, conv) { + exprMess += (mess !== "" ? ", " : "") + "#" + mess.getId(conv.getId()); + }); + return exprMess; +}; + +/** + * Met à jour le nombre qui indique à l'utilisateur à combien de messages il répond. + */ +euphorik.Conversations.prototype.rafraichireNombreMessagesRepond = function() { + // TODO : ya pas mieux pour trouver le nombre d'objet ? + var nb = objectMemberCount(this.messagesRepond); + + $("#posterMessage #repondA .nb").text(nb); + + var boite = $("#posterMessage #repondA"); + if (nb > 0) { + boite.show(); + } else { + boite.hide(); + } +}; + +/** + * Affiche les messages auquel l'utilisateur souhaite répondre au sein des messages des conversations. + * Utilisé lorsqu'une conversation est extraite. + */ +euphorik.Conversations.prototype.afficherMessagesRepondConversations = function() { + var expr = ""; + objectEach(this.messagesRepond, function(messId, mess) { + expr += "#" + mess.getId() + ","; + }); + $(expr).addClass("repondEnEvidence"); +}; + +/** + * Crée un message JSON contenant le message demandant un rafraichissement. + */ +euphorik.Conversations.prototype.getJSONrafraichirMessages = function() { + var mess = { + "message_count" : euphorik.conf.nbMessageAffiche, + "main_page" : this.client.pagePrincipale, + "conversations" : this.getJSONConversations(), + "troll_id" : this.trollIdCourant + }; + + if (this.client.cookie) { + mess.cookie = this.client.cookie; + } + mess.last_message_id = this.conversations[0].idDernierMessageAffiche; + + return mess; +}; + +euphorik.Conversations.prototype.getJSONConversations = function() { + var thisConversations = this; + var clientConv = []; + + this.client.conversations.each(function(i, conv) { + clientConv.push({ + root : conv.root, + page : conv.page, + last_message_id : thisConversations.conversations[i + 1] ? thisConversations.conversations[i + 1].idDernierMessageAffiche : 0 + }); + }); + return clientConv; +}; + +/** + * Ajoute un ensemble de messages puis les affiches. + * @param elements un tableau d'éléments JSON représentant les messages, voir protocole.txt + * @param numConversation le numéro de la conversation auquel appartiennent les messages + * @return true si les messages on été ajoutés, false si la conversation n'existe pas et qu'il n'y a pas de message + */ +euphorik.Conversations.prototype.ajouterMessages = function(elements, numConversation) { + if (!elements.messages.length) { + return this.conversations[numConversation] !== undefined; + } + + for (var i = 0; i < elements.messages.length; i++) { + if (this.ajouterMessage(elements.messages[i], numConversation)) { + // si une nouvelle conversation a été créée alors on lui donne la racine + // TODO : ce block ne devrait pas se trouver ici mais dans "nouvelleConversation" + this.conversations[numConversation].setRacine(elements.first); + this.mettreAJourFragment(); + } + } + + this.flush(numConversation); + + // renseigne la conversation sur la page courante et si c'est la dernière + this.conversations[numConversation].setPage( + numConversation === 0 ? this.client.pagePrincipale : this.client.conversations[numConversation - 1].page, + elements.last_page + ); + + return true; +}; + +/** + * Création d'un nouveau message. + * Les message sont données dans l'ordre de leur id. + * @param element un element JSON représentant le message + * @param numConversation le numéro de la conversation, 0 = principale + * @return true si une nouvelle conversation a été créée sinon false + */ +euphorik.Conversations.prototype.ajouterMessage = function(element, numConversation) { + var message = + new euphorik.Message( + this.client, + this.formater, + element + ); + + var nouvelleConversation = false; + + if (!this.conversations[numConversation]) { + nouvelleConversation = true; + this.nouvelleConversation(numConversation); + } + + this.conversations[numConversation].ajouterMessage(message); + return nouvelleConversation; +}; + +euphorik.Conversations.prototype.nouvelleConversation = function(num) { + var thisConversations = this; + + this.conversations[num] = new euphorik.Conversation(this, num); + + this.conversations[num].setFunPage( + function(num) { // page suivante + thisConversations.client.pageSuivante(num - 1); + thisConversations.rafraichirMessages(true); + }, + function(num) { // page précédente + thisConversations.client.pagePrecedente(num - 1); + thisConversations.rafraichirMessages(true); + }, + function(num) { // retour à la page une + if (thisConversations.client.goPremierePage(num - 1)) { + thisConversations.rafraichirMessages(true); + } + } + ); + + this.ajusterLargeurConversations(); +}; + +/** + * Enlève une conversation. + */ +euphorik.Conversations.prototype.supprimerConversation = function(num) { + if (num <= 0 || num >= this.conversations.length) { + return; // la numéro 0 ne peut être supprimé + } + this.conversations[num].supprimer(); + + // les numéros sont réassigné + for (var i = num; i < this.conversations.length - 1; i++) { + this.conversations[i] = this.conversations[i+1]; + this.conversations[i].num -= 1; + } + this.conversations.pop(); + this.ajusterLargeurConversations(); + + this.client.supprimerConversation(num - 1); + + this.rafraichirMessages(true); + this.mettreAJourFragment(); +}; + +/** + * Ajuste la largeur des conversations en fonction de leur nombre. modifie l'attribut CSS 'width'. + */ +euphorik.Conversations.prototype.ajusterLargeurConversations = function() { + // TODO : trouver mieux ! + var largeurPourcent = (100 / this.conversations.length); + // obsolète !? + // le "- 0.01" evite que IE se chie dessus lamentablement et affiche les conversations les unes au dessus des autres + //if($.browser["msie"]) + // largeurPourcent -= 0.05 + + $("#conversations td").css("width", largeurPourcent + "%"); +}; + +/** + * Demande à toutes les conversations de se flusher (afficher les messages non-affichés). + */ +euphorik.Conversations.prototype.flushAll = function() { + for (var i = 0; i < this.conversations.length; i++) { + this.flush(i); + } +}; + +/** + * Demande à une conversation de se flusher. + */ +euphorik.Conversations.prototype.flush = function(numConv) { + this.conversations[numConv].flush(); +}; + +euphorik.Conversations.prototype.ouvrirConversation = function(racine) { + if (this.client.ajouterConversation(racine)) { + this.rafraichirMessages(true); + } else { + this.util.messageDialog("Cette conversation est déjà ouverte"); + } +}; + +euphorik.Conversations.prototype.viderMessages = function() { + this.conversations.each(function(i, conv) { + conv.viderMessages(); + }); +}; + +/** + * Met à jour les messages de manière continue. + * (AJAX-Comet-style proof) + * @param vider vide tous les messages avant d'afficher les nouveaux + */ +euphorik.Conversations.prototype.rafraichirMessages = function(vider) { + var thisConversations = this; + + vider = vider || false; + + if (vider) { + this.conversations.each(function(i, conv) { + conv.idDernierMessageAffiche = 0; + }); + } + + thisConversations.util.showWaitBar(); // pour faire patienter le user :) + + this.comet.waitEvent( + function() { return thisConversations.getJSONrafraichirMessages(); }, + { + "new_troll" : + function(data) { + thisConversations.trollIdCourant = data.troll_id; + $("#trollCourant .troll").html(thisConversations.formater.traitementComplet(data.content)).unbind("click").click( + function() { + thisConversations.ouvrirConversation(data.message_id); + } + ); + + $("#trollCourant .troll a[@rel*=lightbox]").lightBox(); + }, + "new_messages" : + function(data) { + + if (vider) { + thisConversations.viderMessages(); + } + + // ajoute les messages reçus à leur conversation respective + data.conversations.each(function(numConv, conv) { + if (!thisConversations.ajouterMessages(conv, numConv)) { + thisConversations.util.messageDialog("La conversation {" + thisConversations.client.conversations[numConv - 1].root.toString(36) + "} n'existe pas"); + thisConversations.client.supprimerConversation(numConv - 1); + } + }); + + if (vider) { + thisConversations.afficherMessagesRepondConversations(); + } + + vider = false; + + thisConversations.util.hideWaitBar(); + } + } + ); +};