2 // Copyright 2008 Grégory Burri
4 // This file is part of Euphorik.
6 // Euphorik is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
11 // Euphorik is distributed in the hope that it will be useful,
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with Euphorik. If not, see <http://www.gnu.org/licenses/>.
19 /*jslint laxbreak:true */
22 * Représente une conversation.
23 * Une conversation, au niveau XHTML, est formé de deux partie, le titre et les messages.
24 * Le titre comprend la navigation par page, un bouton pour la fermer, un bouton pour la plier, un bouton
25 * pour créer un lien ainsi que le premier message.
26 * @param conversations l'ensemble des conversations
27 * @param num le numéro de la conversation
29 euphorik
.Conversation = function(conversations
, num
) {
30 this.conversations
= conversations
;
32 // peut changer au cours de la vie de la conversation, n'est pas un id !
35 this.id
= Math
.floor(Math
.random() * 1000000).toString(36);
37 this.util
= this.conversations
.util
;
38 this.formateur
= this.conversations
.formateur
;
39 this.client
= this.conversations
.client
;
41 this.idDernierMessageAffiche
= 0;
42 this.racine
= undefined;
45 this.messagesParId
= {};
47 this.nbMessageMax
= euphorik
.conf
.nbMessageAffiche
; // Le nombre de message affiché par page
49 var messagesXHTML
= '<div class="messages"></div>';
50 var messageRacineXHTML
= '<div class="messageRacine"></div>';
51 var reverse
= this.client
.chatOrder
=== "reverse";
54 '<td class="conversation" id="' + this.getId() + '">' +
55 (reverse
? messagesXHTML : "") +
56 '<div class="titre">' +
57 (reverse
? messageRacineXHTML : "") +
59 (num
=== 0 ? '' : '<div class="fermer"></div><div class="lien"></div><div class="reduire"></div>') +
60 '<span class="next"><</span><span class="numPage">1</span><span class="prev">></span>' +
62 (reverse
? "" : messageRacineXHTML
) +
64 (reverse
? "" : messagesXHTML
) +
67 $("#conversations tr").append(XHTML
);
69 this.util
.infoBulle("Aller à la première page", $("#" + this.getId() + " .numPage"), euphorik
.Util
.positionBulleType
.haut
);
71 this.util
.infoBulle("Créer un lien vers la conversation", $("#" + this.getId() + " .lien"));
72 this.util
.infoBulle("Fermer la conversation", $("#" + this.getId() + " .fermer"));
77 * @racine un message représentant la racine de la conversation, vaut undefined pour la conversation générale
79 euphorik
.Conversation
.prototype.setRacine = function(racineElement
) {
80 this.racine
= new euphorik
.Message(this.client
, this.formateur
, racineElement
);
84 * Met à jour la racine, décide de l'afficher ou non.
85 * On l'affiche uniquement si le message racine n'est pas déjà affiché sur la liste des messages.
87 euphorik
.Conversation
.prototype.majRacine = function() {
92 if (!(this.racine
.id
in this.messagesParId
)) {
93 this.messagesParId
[this.racine
.id
] = this.racine
;
94 var element
= $(this.racine
.XHTML(true, this.getId()));
95 this.attacherEventsSurMessage(element
);
96 $("#" + this.getId() + " .titre .messageRacine").html(element
);
100 euphorik
.Conversation
.prototype.enleverMiseEnEvidence = function() {
101 $("#" + this.getId() + " .message").removeClass("cache");
104 euphorik
.Conversation
.prototype.colorerEntetes = function() {
105 var thisConversation
= this;
107 var messagesReponse
= "";
108 var messagesRepondu
= "";
109 var messagesProprietaire
= "";
110 this.messages
.each(function(i
, mess
) {
111 if (mess
.appartientAuClient
) {
112 messagesProprietaire
+= "#" + mess
.getId(thisConversation
.getId()) + ",";
113 } else if (mess
.clientARepondu
) {
114 messagesRepondu
+= "#" + mess
.getId(thisConversation
.getId()) + ",";
115 } else if (mess
.estUneReponse
) {
116 messagesReponse
+= "#" + mess
.getId(thisConversation
.getId()) + ",";
119 $(messagesReponse
).addClass("reponse");
120 $(messagesRepondu
).addClass("repondu");
121 $(messagesProprietaire
).addClass("proprietaire");
124 euphorik
.Conversation
.prototype.decolorerEntetes = function() {
125 $("#" + this.getId() + " .messages .message")
126 .removeClass("reponse")
127 .removeClass("repondu")
128 .removeClass("proprietaire");
132 * Défini la page courante et s'il l'on se trouve sur la dernière page.
133 * @pageCourante la page courante
134 * @dernierePage true si c'est la dernière page sinon false
136 euphorik
.Conversation
.prototype.setPage = function(pageCourante
, dernierePage
) {
137 $("#" + this.getId() + " .numPage").text(pageCourante
);
138 $("#" + this.getId() + " .next").css("display", pageCourante
=== 1 ? "none" : "inline");
139 $("#" + this.getId() + " .prev").css("display", dernierePage
? "none" : "inline");
143 * Evenement déclanché lors de l'insertion du lien de la conversation dans le message courant.
145 euphorik
.Conversation
.prototype.eventLien = function(fun
) {
146 var thisConversation
= this;
148 $("#" + this.getId() + " .titre .lien").click(
150 fun(thisConversation
.num
);
156 * Evenement déclanché lors de la fermeture de la conversation.
158 euphorik
.Conversation
.prototype.eventFermer = function(fun
) {
159 var thisConversation
= this;
161 $("#" + this.getId() + " .titre .fermer").click(
163 fun(thisConversation
.num
);
169 * @funNext appelé lorsque l'on passe à la page suivante (de 2 à 1 par exemple)
170 * @funPrev appelé lorsque l'on passe à la page précédente (de 1 à 2 par exemple)
171 * @funReset appelé lorsque l'on souhaite revenir à la page une
173 euphorik
.Conversation
.prototype.setFunPage = function(funNext
, funPrev
, funReset
) {
174 var thisConversation
= this;
176 $("#" + this.getId() + " .next").click(
177 function() { funNext(thisConversation
.num
); }
179 $("#" + this.getId() + " .prev").click(
180 function() { funPrev(thisConversation
.num
); }
182 $("#" + this.getId() + " .numPage").click(
183 function() { funReset(thisConversation
.num
); }
188 * Retourne l'id de la conversation sous la forme (par exemple) "conv3".
190 euphorik
.Conversation
.prototype.getId = function() {
191 return "conv" + this.id
;
194 euphorik
.Conversation
.prototype.ajouterMessage = function(message
) {
195 this.messages
.push(message
);
196 this.messagesParId
[message
.id
] = message
;
198 // enlève les messages exedentaires
199 if (this.messages
.length
> this.nbMessageMax
) {
200 delete this.messagesParId
[this.messages
.shift().id
];
205 * FIXME : méthode très lourde. ne serait-ce pas mieux de virer d'un coup l'élément conversation et d'en remettre un vide ?
207 euphorik
.Conversation
.prototype.viderMessages = function() {
209 this.messagesParId
= {};
210 this.idDernierMessageAffiche
= 0;
211 $("#" + this.getId() + " .messages .message").remove();
213 // enlève également la racine
214 $("#" + this.getId() + " .titre .messageRacine").empty();
217 euphorik
.Conversation
.prototype.idMessageFromString = function(idString
) {
218 return parseInt(idString
.substr(4 + this.getId().length
), 36);
222 * Après l'ajout d'un ou plusieurs message cette méthode est appelée afin
223 * d'afficher les messages non-affichés.
224 * FIXME : méthode super lourde, à optimiser.
226 euphorik
.Conversation
.prototype.flush = function() {
227 var thisConversation
= this;
228 var reverse
= this.client
.chatOrder
=== "reverse";
230 // est-ce que le prochain message est pair ? (permet d'alterner le style des messages)
231 var messagePair
= (this.idDernierMessageAffiche
=== 0 ? true :
232 ($("#" + this.getId() + " .messages div:" + (reverse
? "first" : "last")).attr("class").search("messagePair") === -1)
235 // construction de l'XHTML des messages
237 this.messages
.each(function(i
, mess
) {
238 if (mess
.id
> thisConversation
.idDernierMessageAffiche
) {
239 XHTML
+= mess
.XHTML(messagePair
, thisConversation
.getId());
240 messagePair
= !messagePair
;
246 // pour chaque nouveau message au niveau du document on lui assigne ses événements
247 DOM
.each(function() { thisConversation
.attacherEventsSurMessage(this); });
250 DOM
.prependTo("#" + this.getId() + " .messages");
252 DOM
.appendTo("#" + this.getId() + " .messages");
255 // enlève les messages exedentaires au niveau du document
256 var nbMessagesAffiche
= $("#" + this.getId() + " .messages .message").size();
257 if (nbMessagesAffiche
> this.nbMessageMax
) {
259 $("#" + this.getId() + " .messages .message").slice(this.nbMessageMax
, nbMessagesAffiche
).remove();
261 $("#" + this.getId() + " .messages .message").slice(0, nbMessagesAffiche
- this.nbMessageMax
).remove();
265 if (this.messages
.length
> 0) {
266 this.idDernierMessageAffiche
= this.messages
[this.messages
.length
-1].id
;
269 // met à jour la racine de la conversation
274 * Attache des événements à un message donné.
275 * @element le message du DOM
277 euphorik
.Conversation
.prototype.attacherEventsSurMessage = function(element
) {
279 var idMess
= this.idMessageFromString($(element
).attr("id"));
281 this.util
.infoBulle("Extraction de la conversation à partir de ce message", $(".extraire", element
));
282 this.util
.infoBulle("Extraction de la conversation complète", $(".extraireCompletement", element
));
284 var thisConversation
= this;
285 $(".lienConv", element
).click(
287 // FIXME : ya pas mieux ?
288 var racine
= $(event
.target
).text();
289 thisConversation
.conversations
.ouvrirConversation(parseInt(racine
.substring(1, racine
.length
- 1), 36));
296 if ($(event
.target
).is("a") || $(event
.target
).parents("#outilsBan").length
> 0) {
300 // extraction d'une conversation
301 if ($(event
.target
).is(".extraire")) {
302 thisConversation
.conversations
.ouvrirConversation(idMess
);
306 if ($(event
.target
).is(".extraireCompletement")) {
307 thisConversation
.conversations
.ouvrirConversation(thisConversation
.messagesParId
[idMess
].racineId
);
311 // met ou enlève la mise en evidence du message
312 thisConversation
.conversations
.toggleMessageRepond(thisConversation
.messagesParId
[idMess
]);
314 // donne le focus à la ligne de saisie
315 $("form input.message").focus();
319 // mise en évidence de la conversation
320 $(".entete", element
).hover(
322 thisConversation
.decolorerEntetes();
323 thisConversation
.afficherConversation(idMess
);
325 // quand on sort de l'entête du message la mise en évidence est enlevée
327 thisConversation
.enleverMiseEnEvidence();
328 thisConversation
.decolorerEntetes();
329 thisConversation
.colorerEntetes();
333 // est-ce que l'on affichage la date du message ?
334 if (thisConversation
.client
.viewTimes
) {
335 $(".dateComplete", element
).show();
337 $(".dateComplete", element
).hide();
340 $("a[@rel*=lightbox]", element
).lightBox();
342 // les outils de bannissement (uniquement pour les ekMaster)
343 if (thisConversation
.client
.ekMaster
) {
344 $(".pseudo", element
).hover(
346 var userId
= parseInt($(".id", this).text(), 10);
347 var pseudo
= $(this);
348 var h
= pseudo
.outerHeight();
349 var offset
= pseudo
.offset();
350 // TODO : calculer automatiquement la largeur plutôt que d'inscrire des valeurs en brut'
351 thisConversation
.util
.outilsBan
.css("top", offset
.top
- 2).css("left", offset
.left
- 2).height(h
< 16 ? 16 : h
).width(pseudo
.outerWidth() + 16 * 3 + 12 + 64).prependTo(this).show();
352 $("img", thisConversation
.util
.outilsBan
).unbind("click");
353 $("#slap", thisConversation
.util
.outilsBan
).click(
355 thisConversation
.client
.slap(userId
, $("#outilsBan input").val());
356 $("#outilsBan input").val("");
357 $("#outilsBan").hide();
360 $("#kick", thisConversation
.util
.outilsBan
).click(
362 thisConversation
.client
.kick(userId
, $("#outilsBan input").val());
363 $("#outilsBan input").val("");
364 $("#outilsBan").hide();
367 $("#ban", thisConversation
.util
.outilsBan
).click(
369 thisConversation
.client
.ban(userId
, $("#outilsBan input").val());
370 $("#outilsBan input").val("");
371 $("#outilsBan").hide();
376 $("#outilsBan", this).hide();
383 * Etablit une liste des messages à mettre en evidence et des messages à cacher.
384 * Puis applique un plan diabolique.
385 * @param id l'id du message
387 euphorik
.Conversation
.prototype.afficherConversation = function(id
) {
388 var thisConversation
= this;
390 var message
= this.messagesParId
[id
];
395 var mess
= message
.getConversation(this);
397 // FIXME : cet appel est très lent
398 $("#" + this.getId() + " .messages .message").each(
401 var statut
= mess
[thisConversation
.idMessageFromString(jq
.attr("id"))];
403 jq
.addClass("cache");
405 jq
.removeClass("cache");
407 // "repondu" et "reponse" sont prioritaitres à "proprietaire"
408 // contrairement à la vue normale (sans mise en évidence d'une conversation)
410 jq
.addClass("repondu");
413 jq
.addClass("reponse");
416 jq
.addClass("proprietaire");
425 * Supprime une conversation.
426 * Ne l'enlève pas du DOM.
428 euphorik
.Conversation
.prototype.supprimer = function() {
429 $("#" + this.getId()).remove();