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
;
195 * Après avoir créé un message celui ci est ajouté à une conversation via cette méthode.
197 euphorik
.Conversation
.prototype.ajouterMessage = function(message
) {
198 this.messages
.push(message
);
199 this.messagesParId
[message
.id
] = message
;
201 // enlève le message exedentaire si nécessaire
202 if (this.messages
.length
> this.nbMessageMax
) {
203 delete this.messagesParId
[this.messages
.shift().id
];
206 // met à jour le membre 'estReponduPar' des messages de la conversation
207 for (var i
= 0; i
< this.messages
.length
- 1; i
++) {
208 var autreMess
= this.messages
[i
];
209 if (autreMess
.id
in message
.repondA
) {
210 autreMess
.estReponduPar
[message
.id
] = true;
216 * 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 ?
218 euphorik
.Conversation
.prototype.viderMessages = function() {
220 this.messagesParId
= {};
221 this.idDernierMessageAffiche
= 0;
222 $("#" + this.getId() + " .messages .message").remove();
224 // enlève également la racine
225 $("#" + this.getId() + " .titre .messageRacine").empty();
228 euphorik
.Conversation
.prototype.idMessageFromString = function(idString
) {
229 return parseInt(idString
.substr(4 + this.getId().length
), 36);
233 * Après l'ajout d'un ou plusieurs message cette méthode est appelée afin
234 * d'afficher les messages non-affichés.
235 * FIXME : méthode super lourde, à optimiser.
237 euphorik
.Conversation
.prototype.flush = function() {
238 var thisConversation
= this;
239 var reverse
= this.client
.chatOrder
=== "reverse";
241 // est-ce que le prochain message est pair ? (permet d'alterner le style des messages)
242 var messagePair
= (this.idDernierMessageAffiche
=== 0 ? true :
243 ($("#" + this.getId() + " .messages div:" + (reverse
? "first" : "last")).attr("class").search("messagePair") === -1)
246 // construction de l'XHTML des messages
248 this.messages
.each(function(i
, mess
) {
249 if (mess
.id
> thisConversation
.idDernierMessageAffiche
) {
250 XHTML
+= mess
.XHTML(messagePair
, thisConversation
.getId());
251 messagePair
= !messagePair
;
257 // pour chaque nouveau message au niveau du document on lui assigne ses événements
258 DOM
.each(function() { thisConversation
.attacherEventsSurMessage(this); });
261 DOM
.prependTo("#" + this.getId() + " .messages");
263 DOM
.appendTo("#" + this.getId() + " .messages");
266 // enlève les messages exedentaires au niveau du document
267 var nbMessagesAffiche
= $("#" + this.getId() + " .messages .message").size();
268 if (nbMessagesAffiche
> this.nbMessageMax
) {
270 $("#" + this.getId() + " .messages .message").slice(this.nbMessageMax
, nbMessagesAffiche
).remove();
272 $("#" + this.getId() + " .messages .message").slice(0, nbMessagesAffiche
- this.nbMessageMax
).remove();
276 if (this.messages
.length
> 0) {
277 this.idDernierMessageAffiche
= this.messages
[this.messages
.length
-1].id
;
280 // met à jour la racine de la conversation
285 * Attache des événements à un message donné.
286 * @element le message du DOM
288 euphorik
.Conversation
.prototype.attacherEventsSurMessage = function(element
) {
290 var idMess
= this.idMessageFromString($(element
).attr("id"));
292 this.util
.infoBulle("Extraction de la conversation à partir de ce message", $(".extraire", element
));
293 this.util
.infoBulle("Extraction de la conversation complète", $(".extraireCompletement", element
));
295 var thisConversation
= this;
296 $(".lienConv", element
).click(
298 // FIXME : ya pas mieux ?
299 var racine
= $(event
.target
).text();
300 thisConversation
.conversations
.ouvrirConversation(parseInt(racine
.substring(1, racine
.length
- 1), 36));
307 if ($(event
.target
).is("a") || $(event
.target
).parents("#outilsBan").length
> 0) {
311 // extraction d'une conversation
312 if ($(event
.target
).is(".extraire")) {
313 thisConversation
.conversations
.ouvrirConversation(idMess
);
317 if ($(event
.target
).is(".extraireCompletement")) {
318 thisConversation
.conversations
.ouvrirConversation(thisConversation
.messagesParId
[idMess
].racineId
);
322 // met ou enlève la mise en evidence du message
323 thisConversation
.conversations
.toggleMessageRepond(thisConversation
.messagesParId
[idMess
]);
325 // donne le focus à la ligne de saisie
326 $("form input.message").focus();
330 // mise en évidence de la conversation
331 $(".entete", element
).hover(
333 thisConversation
.decolorerEntetes();
334 thisConversation
.afficherConversation(idMess
);
336 // quand on sort de l'entête du message la mise en évidence est enlevée
338 thisConversation
.enleverMiseEnEvidence();
339 thisConversation
.decolorerEntetes();
340 thisConversation
.colorerEntetes();
344 // est-ce que l'on affichage la date du message ?
345 if (thisConversation
.client
.viewTimes
) {
346 $(".dateComplete", element
).show();
348 $(".dateComplete", element
).hide();
351 $("a[@rel*=lightbox]", element
).lightBox();
353 // les outils de bannissement (uniquement pour les ekMaster)
354 if (thisConversation
.client
.ekMaster
) {
355 $(".pseudo", element
).hover(
357 var userId
= parseInt($(".id", this).text(), 10);
358 var pseudo
= $(this);
359 var h
= pseudo
.outerHeight();
360 var offset
= pseudo
.offset();
361 // TODO : calculer automatiquement la largeur plutôt que d'inscrire des valeurs en brut'
362 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();
363 $("img", thisConversation
.util
.outilsBan
).unbind("click");
364 $("#slap", thisConversation
.util
.outilsBan
).click(
366 thisConversation
.client
.slap(userId
, $("#outilsBan input").val());
367 $("#outilsBan input").val("");
368 $("#outilsBan").hide();
371 $("#kick", thisConversation
.util
.outilsBan
).click(
373 thisConversation
.client
.kick(userId
, $("#outilsBan input").val());
374 $("#outilsBan input").val("");
375 $("#outilsBan").hide();
378 $("#ban", thisConversation
.util
.outilsBan
).click(
380 thisConversation
.client
.ban(userId
, $("#outilsBan input").val());
381 $("#outilsBan input").val("");
382 $("#outilsBan").hide();
387 $("#outilsBan", this).hide();
394 * Etablit une liste des messages à mettre en evidence et des messages à cacher.
395 * Puis applique un plan diabolique.
396 * @param id l'id du message
398 euphorik
.Conversation
.prototype.afficherConversation = function(id
) {
399 var thisConversation
= this;
401 var message
= this.messagesParId
[id
];
406 var mess
= message
.getConversation(this);
408 // FIXME : cet appel est très lent
409 $("#" + this.getId() + " .messages .message").each(
412 var statut
= mess
[thisConversation
.idMessageFromString(jq
.attr("id"))];
413 if (statut
=== undefined) {
414 jq
.addClass("cache");
416 jq
.removeClass("cache");
418 // "repondu" et "reponse" sont prioritaitres à "proprietaire"
419 // contrairement à la vue normale (sans mise en évidence d'une conversation)
421 jq
.addClass("repondu");
424 jq
.addClass("reponse");
427 jq
.addClass("proprietaire");
436 * Supprime une conversation.
437 * Ne l'enlève pas du DOM.
439 euphorik
.Conversation
.prototype.supprimer = function() {
440 $("#" + this.getId()).remove();