// 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 .
function PageMinichat(client, formateur, util)
{
this.nom = "minichat"
this.client = client
this.formateur = formateur
this.util = util
// permet d'éviter d'envoyer plusieurs messages simultanément en pressant
// rapidement sur "enter" par exemple
this.envoieMessageEnCours = false
this.regexMessageTagMatch = /\{.*?\}>/g
this.regexMessageTagReplace = /^(.*?\{.*?\}>)*/
}
PageMinichat.prototype.contenu = function()
{
// le fait que tout soit collé est fait exprès, permet d'éviter d'avoir des espaces supplémentaires entre les spans'
var formulaireXHTML = '
'
var trollXHTML = 'Troll de la semaine :
'
//var titreXHTML = '
'
//var messagesXHTML = '
'
var conversationXHTML = ''
if (this.client.chatOrder == "reverse")
return trollXHTML + formulaireXHTML + conversationXHTML
else
return trollXHTML + conversationXHTML + formulaireXHTML
}
PageMinichat.prototype.classes = function()
{
return this.client.chatOrder == "reverse" ? "orderReverse" : "orderChrono"
}
PageMinichat.prototype.charger = function()
{
thisPage = this
$("form input.pseudo").val(this.client.pseudo)
// cet appel ne doit pas être fait avant l'appel à 'charger'
this.conversations = new Conversations(this.client, this.formateur, this.util)
this.conversations.rafraichirMessages(true)
this.util.setCaretToEnd($("form input.message")[0])
// les outils de bannissement (uniquement pour les ekMaster)
if (this.client.ekMaster)
{
this.util.outilsBan = $(
'' +
'' +
'' +
'' +
'' +
'' +
''
)
this.util.infoBulle("Slap", $("#slap", this.util.outilsBan))
this.util.infoBulle("Kick (" + euphorik.conf.tempsKick + "min)", $("#kick", this.util.outilsBan))
this.util.infoBulle("Ban (" + euphorik.conf.tempsBan / 24 / 60 + " jours)", $("#ban", this.util.outilsBan))
this.util.infoBulle("La raison", $("input", this.util.outilsBan))
}
this.util.infoBulle("Ouvrir la conversation liée au troll de la semaine", $("#trollCourant .troll"))
this.util.infoBulle("Cliquer sur les messages pour les enlevers de la liste",
$("form#posterMessage #repondA").hover(
function() { thisPage.util.afficherBoite($(".messages", this), $(this), positionTypeX.centre, positionTypeY.bas) },
function() { $(".messages", this).hide() }
).click(
function(e)
{
if ($(e.target).is(".nb"))
thisPage.conversations.enleverMessagesRepond()
}
),
euphorik.Util.positionBulleType.droite
)
//
$("body").append('')
// affichage des smiles
$("#smiles").append(this.formateur.getSmilesHTML()).children().each(
function(i)
{
var opacityBase = $(this).css("opacity")
$(this).click(
function()
{
thisPage.util.replaceSelection($("form#posterMessage input.message")[0], thisPage.formateur.smiles[$(this).attr("class")][0].source.replace(/\\/g, ""))
}
).hover(
function() { $(this).animate({opacity: 1}, 200) },
function() { $(this).animate({opacity: opacityBase}, 200) }
)
}
)
$("form button.smiles").hover(
// affichage de la boite présentant les smiles
function(e){ thisPage.util.afficherBoite($("#smiles"), $(e.target), positionTypeX.centre, positionTypeY.basRecouvrement) },
function(){}
)
$("#smiles").hover(
function(){},
function()
{
$("#smiles").hide()
}
)
//
// événements
var nouveauMessage =
function()
{
// captcha anti bot
if ($("form input.captcha").val() != "") return
thisPage.envoyerMessage(
$("form input.pseudo").val(),
$("form input.message").val()
)
$("form input.message").focus()
}
$("form").keypress(
function(e)
{
if (e.which == 13) // return
nouveauMessage()
}
)
$("form button.return").click(nouveauMessage)
// interdiction de submiter le formulaire
$("form").submit(function(){ return false})
$("input.pseudo").click(
function()
{
var input = $("input.pseudo")[0]
if (input.value == euphorik.conf.pseudoDefaut)
input.value = ""
}
)
}
PageMinichat.prototype.decharger = function()
{
this.conversations.pageEvent.stopAttenteCourante()
$("body #smiles").remove()
}
PageMinichat.prototype.getJSONMessage = function(pseudo, message)
{
var repondA = []
for (var id in this.conversations.messagesRepond)
repondA.push(parseInt(id)) // FIXME : une propriété ne peut pas être de type int ?
return {
"header" : { "action" : "put_message", "version" : euphorik.conf.versionProtocole },
"cookie" : this.client.cookie,
"nick" : pseudo,
"content" : message,
"answer_to" : repondA
}
}
PageMinichat.prototype.envoyerMessage = function(pseudo, message)
{
var thisPageMinichat = this
// (un pseudo vide est autorisé)
pseudo = this.formateur.filtrerInputPseudo(pseudo)
if (pseudo == euphorik.conf.nickDefaut)
{
this.util.messageDialogue("Le pseudo ne peut pas être " + euphorik.conf.nickDefaut)
return
}
message = message.trim()
if (message == "")
{
this.util.messageDialogue("Le message est vide")
return
}
if (!this.client.authentifie())
if (!this.client.enregistrement())
{
this.util.messageDialogue("login impossible")
return
}
this.client.pseudo = pseudo
// évite le double post
if (this.envoieMessageEnCours)
{
this.util.messageDialogue("Message en cours d'envoie...")
return
}
this.envoieMessageEnCours = true
jQuery.ajax(
{
url : "request",
type: "POST",
data : this.util.jsonVersAction(this.getJSONMessage(pseudo, message)),
dataType : "json",
beforeSend : function(xmlHttpRequest)
{
// TODO : ça marche ça ??
xmlHttpRequest.setRequestHeader("X-Requested-With", "")
},
success : function(data, textStatus)
{
if(data["reply"] == "ok")
{
// met à jour la classe des messages auquel repond celui ci (c'est un peu de la triche) TODO : ya mieux ?
for (var messId in thisPageMinichat.conversations.messagesRepond)
{
for (var j = 0; j < thisPageMinichat.conversations.conversations.length; j++)
{
var mess = thisPageMinichat.conversations.conversations[j].messagesParId[messId]
if (mess != undefined)
mess.clientARepondu = true
}
// TODO : ca sert à qque chose ?
//$("#conversations div#" + thisPageMinichat.conversations.messagesRepond[messId].getId()).addClass("repondu")
}
$("form input.message").val("")
thisPageMinichat.conversations.enleverMessagesRepond()
}
else if (data["reply"] == "error")
{
thisPageMinichat.util.messageDialogue(data["error_message"])
}
thisPageMinichat.envoieMessageEnCours = false
},
error : function()
{
thisPageMinichat.envoieMessageEnCours = false
}
}
)
}
///////////////////////////////////////////////////////////////////////////////////////////////////
function Reponse(id, pseudo, login)
{
this.id = id
this.pseudo = pseudo
this.login = login
if (this.pseudo == undefined)
this.pseudo = ""
if (this.login == undefined)
this.login = ""
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Représente un message.
*/
function Message(client, formateur, element)
{
this.client = client
this.formateur = formateur
this.id = element["id"]
this.auteurId = element["user_id"]
this.racineId = element["root"]
this.date = element["date"]
this.pseudo = element["nick"]
this.login = element["login"]
this.contenu = element["content"]
this.appartientAuClient = element["owner"]
this.clientARepondu = element["answered"]
this.estUneReponse = element["is_a_reply"]
this.systeme = element["system"] // est-ce un message 'système' ?
this.setRepondA(element["answer_to"]) // un ensemble de reponse (voir Reponse) indexé par l'id du message de la reponses
this.ekMaster = element["ek_master"]
this.degreeOstentatoire = element["ostentatious_master"]
}
/**
* @param pre est un prefix permettant de créer un Id différent pour deux mêmes messages.
* Cela est utile pour afficher plusieurs mêmes messages au sein d'un document XHTML.
* voir également la fonction 'XHTML()'.
*/
Message.prototype.getId = function(pre)
{
if (pre == undefined)
pre = ""
return pre + "mess" + this.id.toString(36)
}
/**
*
*/
Message.prototype.setRepondA = function(repondAJSON)
{
var thisMessage = this
this.repondA = {}
for(var i = 0; i < repondAJSON.length; i++)
{
thisMessage.repondA[repondAJSON[i]["id"]] = new Reponse(
repondAJSON[i]["id"],
repondAJSON[i]["nick"],
repondAJSON[i]["login"]
)
}
}
/**
* Renvoie les messages faisant partie d'une conversation.
* @param messages l'ensemble des messages de la conversation
* @return les id des messages qui ont été mis en evidence sous la forme
* d'un hash (object) {id => 0 | 1 | 2 | 3}. 1 : proprietaire, 2 : reponse directe, 3 : message repondu
*/
Message.prototype.getConversation = function(messages)
{
var thisMessage = this
// les messages faisant partie de la conversation
var messagesEnEvidence = {}
messagesEnEvidence[this.id] = 1
// recherche les réponses (O(n))
for (var i = 0; i < messages.messages.length; i++)
if (messages.messages[i].repondA.hasOwnProperty(this.id))
messagesEnEvidence[messages.messages[i].id] = 2
// parcours en
var f = function(tabIds, premierNiveau)
{
for(var id in tabIds)
{
// si le message (id) a déjà été traité
if (messagesEnEvidence[id] != undefined && !premierNiveau)
continue
var message = messages.messagesParId[id]
if (message != undefined)
{
messagesEnEvidence[id] = premierNiveau ? 3 : (message.auteurId == thisMessage.auteurId ? 1 : 0)
f (message.repondA, false)
}
}
}
f(this.repondA, true)
return messagesEnEvidence
}
/**
* Renvoie le message sous la forme de code XHTML (string) prêt à être inséré dans un document.
* Aucun callback n'est affecté.
*/
Message.prototype.XHTML = function(messagePair, pre)
{
if (messagePair == undefined)
messagePair = true
if (pre == undefined)
pre = ""
// construit l'identifiant de la personne
var identifiant =
this.client.nickFormat == "nick" || this.login == "" ? this.formateur.traitementComplet(this.pseudo) :
(this.client.nickFormat == "login" ? this.formateur.traitementComplet(this.login) :
this.formateur.traitementComplet(this.pseudo) + "(" + this.formateur.traitementComplet(this.login) +")" )
var XHTMLrepondA = ""
var debut = true
for (var id in this.repondA)
{
if (!debut) XHTMLrepondA += ", "
XHTMLrepondA += this.formateur.traitementComplet(this.repondA[id].pseudo)
debut = false
}
if (XHTMLrepondA != "")
XHTMLrepondA = "" + XHTMLrepondA + ""
return "" +
"
" +
"[" + this.date + "]" +
"" + this.auteurId + "" + identifiant + "" +
XHTMLrepondA +
"
" + this.formateur.traitementComplet(this.contenu, this.pseudo) + "" +
"
"
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Représente une conversation.
* Une conversation, au niveau XHTML, est formé de deux partie, le titre et les messages.
* Le titre comprend la navigation par page, un bouton pour la fermer, un bouton pour la plier, un bouton
* pour créer un lien ainsi que le premier message.
* @param conversations l'ensemble des conversations
* @param num le numéro de la conversation
*/
function Conversation(conversations, num)
{
this.conversations = conversations
this.num = num // peut changer au cours de la vie de la conversation, n'est pas un id !
this.id = Math.floor(Math.random() * 1000000).toString(36)
this.util = this.conversations.util
this.formateur = this.conversations.formateur
this.client = this.conversations.client
this.idDernierMessageAffiche = 0
this.racine = undefined
this.messages = []
this.messagesParId = {}
this.nbMessageMax = euphorik.conf.nbMessageAffiche // Le nombre de message affiché par page
var messagesXHTML = ''
var messageRacineXHTML = ''
var reverse = this.client.chatOrder == "reverse"
var XHTML =
'' +
(reverse ? messagesXHTML : "") +
'' +
(reverse ? messageRacineXHTML : "") +
' ' +
(num == 0 ? '' : ' ') +
' <1>' +
' ' +
(reverse ? "" : messageRacineXHTML) +
' ' +
(reverse ? "" : messagesXHTML) +
' | '
$("#conversations tr").append(XHTML)
this.util.infoBulle("Aller à la première page", $("#" + this.getId() + " .numPage"), euphorik.Util.positionBulleType.haut)
if (num != 0)
{
this.util.infoBulle("Créer un lien vers la conversation", $("#" + this.getId() + " .lien"))
this.util.infoBulle("Fermer la conversation", $("#" + this.getId() + " .fermer"))
}
}
/**
* @racine un message représentant la racine de la conversation, vaut undefined pour la conversation générale
*/
Conversation.prototype.setRacine = function(racineElement)
{
this.racine = new Message(this.client, this.formateur, racineElement)
}
/**
* Met à jour la racine, décide de l'afficher ou non.
* On l'affiche uniquement si le message racine n'est pas déjà affiché sur la liste des messages.
*/
Conversation.prototype.majRacine = function()
{
if (this.racine == undefined)
return
if (!(this.racine.id in this.messagesParId))
{
this.messagesParId[this.racine.id] = this.racine
var element = $(this.racine.XHTML(true, this.getId()))
this.attacherEventsSurMessage(element)
$("#" + this.getId() + " .titre .messageRacine").html(element)
}
}
Conversation.prototype.enleverMiseEnEvidence = function()
{
$("#" + this.getId() + " .message").removeClass("cache")
}
Conversation.prototype.colorerEntetes = function()
{
var messagesReponse = ""
var messagesRepondu = ""
var messagesProprietaire = ""
for (var i = 0; i < this.messages.length; i++)
{
if (this.messages[i].appartientAuClient)
messagesProprietaire += "#" + this.messages[i].getId(this.getId()) + ","
else if (this.messages[i].clientARepondu)
messagesRepondu += "#" + this.messages[i].getId(this.getId()) + ","
else if (this.messages[i].estUneReponse)
messagesReponse += "#" + this.messages[i].getId(this.getId()) + ","
}
$(messagesReponse).addClass("reponse")
$(messagesRepondu).addClass("repondu")
$(messagesProprietaire).addClass("proprietaire")
}
Conversation.prototype.decolorerEntetes = function()
{
$("#" + this.getId() + " .messages .message")
.removeClass("reponse")
.removeClass("repondu")
.removeClass("proprietaire")
}
/**
* Défini la page courante et s'il l'on se trouve sur la dernière page.
* @pageCourante la page courante
* @dernierePage true si c'est la dernière page sinon false
*/
Conversation.prototype.setPage = function(pageCourante, dernierePage)
{
$("#" + this.getId() + " .numPage").text(pageCourante)
$("#" + this.getId() + " .next").css("display", pageCourante == 1 ? "none" : "inline")
$("#" + this.getId() + " .prev").css("display", dernierePage ? "none" : "inline")
}
/**
* Evenement déclanché lors de l'insertion du lien de la conversation dans le message courant.
*/
Conversation.prototype.eventLien = function(fun)
{
var thisConversation = this
$("#" + this.getId() + " .titre .lien").click(
function()
{
fun(thisConversation.num)
}
)
}
/**
* Evenement déclanché lors de la fermeture de la conversation.
*/
Conversation.prototype.eventFermer = function(fun)
{
var thisConversation = this
$("#" + this.getId() + " .titre .fermer").click(
function()
{
fun(thisConversation.num)
}
)
}
/**
* @funNext appelé lorsque l'on passe à la page suivante (de 2 à 1 par exemple)
* @funPrev appelé lorsque l'on passe à la page précédente (de 1 à 2 par exemple)
* @funReset appelé lorsque l'on souhaite revenir à la page une
*/
Conversation.prototype.setFunPage = function(funNext, funPrev, funReset)
{
var thisConversation = this
$("#" + this.getId() + " .next").click(
function() { funNext(thisConversation.num) }
)
$("#" + this.getId() + " .prev").click(
function() { funPrev(thisConversation.num) }
)
$("#" + this.getId() + " .numPage").click(
function() { funReset(thisConversation.num) }
)
}
/**
* Retourne l'id de la conversation sous la forme (par exemple) "conv3".
*/
Conversation.prototype.getId = function()
{
return "conv" + this.id
}
Conversation.prototype.ajouterMessage = function(message)
{
this.messages.push(message)
this.messagesParId[message.id] = message
if (this.messages.length > this.nbMessageMax)
delete this.messagesParId[this.messages.shift().id]
}
/**
* 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 ?
*/
Conversation.prototype.viderMessages = function()
{
this.messages = []
this.messagesParId = {}
this.idDernierMessageAffiche = 0
$("#" + this.getId() + " .messages .message").remove()
// enlève également la racine
$("#" + this.getId() + " .titre .messageRacine").empty()
}
Conversation.prototype.idMessageFromString = function(idString)
{
return parseInt(idString.substr(4 + this.getId().length), 36)
}
/**
* Après l'ajout d'un ou plusieurs message cette méthode est appelée afin
* d'afficher les messages non-affichés.
* FIXME : méthode super lourde, à optimiser.
*/
Conversation.prototype.flush = function()
{
var thisConversation = this
var reverse = this.client.chatOrder == "reverse"
// est-ce que le prochain message est pair ? (permet d'alterner le style des messages)
var messagePair = (this.idDernierMessageAffiche == 0 ? true :
($("#" + this.getId() + " .messages div:" + (reverse ? "first" : "last")).attr("class").search("messagePair") == -1)
)
// construction de l'XHTML des messages
var XHTML = ""
for (var i = 0; i < this.messages.length; i++)
if (this.messages[i].id > this.idDernierMessageAffiche)
{
XHTML += this.messages[i].XHTML(messagePair, this.getId())
messagePair = !messagePair
}
var DOM = $(XHTML)
// pour chaque nouveau message au niveau du document on crée ses événements
DOM.each(function() { thisConversation.attacherEventsSurMessage(this) })
if (reverse)
DOM.prependTo("#" + this.getId() + " .messages")
else
DOM.appendTo("#" + this.getId() + " .messages")
// enlève les messages exedentaires
var nbMessagesAffiche = $("#" + this.getId() + " .messages .message").size()
if (nbMessagesAffiche > this.nbMessageMax)
{
if (reverse)
$("#" + this.getId() + " .messages .message").slice(this.nbMessageMax, nbMessagesAffiche).remove()
else
$("#" + this.getId() + " .messages .message").slice(0, nbMessagesAffiche - this.nbMessageMax).remove()
}
if (this.messages.length > 0)
this.idDernierMessageAffiche = this.messages[this.messages.length-1].id
// met à jour la racine de la conversation
this.majRacine()
}
Conversation.prototype.attacherEventsSurMessage = function(element)
{
// l'id du message
var idMess = this.idMessageFromString($(element).attr("id"))
this.util.infoBulle("Extraction de la conversation à partir de ce message", $(".extraire", element))
this.util.infoBulle("Extraction de la conversation complète", $(".extraireCompletement", element))
var thisConversation = this
$(".lienConv", element).click(
function(event)
{
// FIXME : ya pas mieux ?
var racine = $(event.target).text()
thisConversation.conversations.ouvrirConversation(parseInt(idString.substring(1, racine.length - 1), 36))
return false
}
)
$(element).click(
function(event)
{
if ($(event.target).is("a") || $(event.target).parents("#outilsBan").length > 0) return
// extraction d'une conversation
if ($(event.target).is(".extraire"))
{
thisConversation.conversations.ouvrirConversation(idMess)
return
}
if ($(event.target).is(".extraireCompletement"))
{
thisConversation.conversations.ouvrirConversation(thisConversation.messagesParId[idMess].racineId)
return
}
// met ou enlève la mise en evidence du message
thisConversation.conversations.toggleMessageRepond(thisConversation.messagesParId[idMess])
// donne le focus à la ligne de saisie
$("form input.message").focus()
}
)
// mise en évidence de la conversation
$(".entete", element).hover(
function()
{
thisConversation.decolorerEntetes()
thisConversation.afficherConversation(idMess)
},
// quand on sort de l'entête du message la mise en évidence est enlevée
function()
{
thisConversation.enleverMiseEnEvidence()
thisConversation.decolorerEntetes()
thisConversation.colorerEntetes()
}
)
if (thisConversation.client.viewTimes)
$(".dateComplete", element).show()
else
$(".dateComplete", element).hide()
$("a[@rel*=lightbox]", element).lightBox()
// les outils de bannissement (uniquement pour les ekMaster)
if (thisConversation.client.ekMaster)
$(".pseudo", element).hover(
function(e)
{
var userId = parseInt($(".id", this).text())
var pseudo = $(this)
var h = pseudo.outerHeight()
var offset = pseudo.offset()
// TODO : calculer automatiquement la largeur plutôt que d'inscrire des valeurs en brut'
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()
$("img", thisConversation.util.outilsBan).unbind("click")
$("#slap", thisConversation.util.outilsBan).click(
function()
{
thisConversation.client.slap(userId, $("#outilsBan input").val())
$("#outilsBan input").val("")
$("#outilsBan").hide()
}
)
$("#kick", thisConversation.util.outilsBan).click(
function()
{
thisConversation.client.kick(userId, $("#outilsBan input").val())
$("#outilsBan input").val("")
$("#outilsBan").hide()
}
)
$("#ban", thisConversation.util.outilsBan).click(
function()
{
thisConversation.client.ban(userId, $("#outilsBan input").val())
$("#outilsBan input").val("")
$("#outilsBan").hide()
}
)
},
function()
{
$("#outilsBan", this).hide()
}
)
}
/**
* Etablit une liste des messages à mettre en evidence et des messages à cacher.
* Puis applique un plan diabolique.
* @param id l'id du message
*/
Conversation.prototype.afficherConversation = function(id)
{
var thisConversation = this
var message = this.messagesParId[id]
if (message == undefined) return
var mess = message.getConversation(this)
// FIXME : cet appel est très lent
$("#" + this.getId() + " .messages .message").each(
function()
{
var jq = $(this)
var statut = mess[thisConversation.idMessageFromString(jq.attr("id"))]
if (statut == undefined)
jq.addClass("cache")
else
{
jq.removeClass("cache")
switch (statut)
{
// "repondu" et "reponse" sont prioritaitres à "proprietaire"
// contrairement à la vue normale (sans mise en évidence d'une conversation)
case 3 :
jq.addClass("repondu")
break;
case 2 :
jq.addClass("reponse")
break;
case 1 :
jq.addClass("proprietaire")
break;
}
}
}
)
}
/**
* Supprime une conversation.
*/
Conversation.prototype.supprimer = function()
{
$("#" + this.getId()).remove()
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Représente l'ensemble des conversations affichés.
*/
function Conversations(client, formateur, util)
{
this.client = client
this.formateur = formateur
this.util = util
// un ensemble des messages (id) auquel l'utilisateur répond (vider après l'envoie du message courant)
this.messagesRepond = {}
this.conversations = new Array() // les conversations, la première représente la conversation principale
this.nouvelleConversation(0)
this.trollIdCourant = 0
this.pageEvent = new PageEvent("chat", this.util)
}
// les messages insérés dans le document XHTML on leur id prefixé par cette valeur
// cela permet de les distinguer des "vrais" messages
Conversations.prototype.prefixIdMessage = "rep"
/**
* Permet de définir un message comme étant ou n'étant plus un message auquel l'utilisateur
* répond.
*/
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)
}
/**
* Enlève tous les messages auquel l'utilisateur souhaite répond.
*/
Conversations.prototype.enleverMessagesRepond = function()
{
for (var messId in this.messagesRepond)
this.enleverMessageRepond(this.messagesRepond[messId])
// 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.
*/
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.
*/
Conversations.prototype.ajouterMessageRepond = function(mess)
{
var thisMessages = 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){ break; }
mess2 = this.messagesRepond[mess2]
if (mess2 != undefined && mess2.racineId != mess.racineId)
{
this.util.messageDialogue("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(){
thisMessages.enleverMessageRepond(mess)
$("form#posterMessage #repondA .messages").hide()
})
}
)
this.rafraichireNombreMessagesRepond()
}
/**
* Construit tous les id potentiels d'un message, renvoie par exemple :
* "conv9b28mess1h, conv9b2amess1h, conv9b32mess1h"
*/
Conversations.prototype.exprIdsPotentiels = function(mess)
{
var exprMess = ""
for(var i = 0; i < this.conversations.length; i++)
{
exprMess += (mess != "" ? ", " : "") + "#" + mess.getId(this.conversations[i].getId())
}
return exprMess
}
/**
* Met à jour le nombre qui indique à l'utilisateur à combien de messages il répond.
*/
Conversations.prototype.rafraichireNombreMessagesRepond = function()
{
// TODO : ya pas mieux pour trouver le nombre d'objet ?
var nb = 0
for (var m in this.messagesRepond)
nb += 1
$("#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.
*/
Conversations.prototype.afficherMessagesRepondConversations = function()
{
var expr = ""
for(var messId in this.messagesRepond)
expr += "#" + this.messagesRepond[messId].getId() + ","
$(expr).addClass("repondEnEvidence")
}
/**
* Crée un message JSON contenant le message demandant un rafraichissement.
*/
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 != null) mess["cookie"] = this.client.cookie
mess["last_message_id"] = this.conversations[0].idDernierMessageAffiche
return mess
}
Conversations.prototype.getJSONConversations = function()
{
var clientConv = []
for (var i = 0; i < this.client.conversations.length; i++)
{
clientConv.push(
{
root : this.client.conversations[i].root,
page : this.client.conversations[i].page,
last_message_id : this.conversations[i + 1] == undefined ? 0 : this.conversations[i + 1].idDernierMessageAffiche
}
)
}
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
*/
Conversations.prototype.ajouterMessages = function(elements, numConversation)
{
if (elements["messages"].length == 0)
return this.conversations[numConversation] != undefined
for (var i = 0; i < elements["messages"].length; i++)
// si une nouvelle conversation a été créée alors on lui donne la racine
if(this.ajouterMessage(elements["messages"][i], numConversation))
this.conversations[numConversation].setRacine(elements["first"])
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
*/
Conversations.prototype.ajouterMessage = function(element, numConversation)
{
var thisMessages = this
// pas d'utilisation de jquery pour des raisons de performance
var message = new Message(
this.client,
this.formateur,
element
)
var nouvelleConversation = false
if (this.conversations[numConversation] == null)
{
nouvelleConversation = true
this.nouvelleConversation(
numConversation,
function(num) // fermeture de la conversation
{
thisMessages.supprimerConversation(num)
},
function(num) // insertion du lien vers la conversation
{
thisPage.util.replaceSelection(
$("form#posterMessage input.message")[0],
"{" + thisMessages.client.conversations[num-1].root.toString(36) + "}"
)
}
)
}
this.conversations[numConversation].ajouterMessage(message)
return nouvelleConversation
}
Conversations.prototype.nouvelleConversation = function(num, funFermer, funLien)
{
var thisMessages = this
this.conversations[num] = new Conversation(this, num)
if (funFermer != undefined)
this.conversations[num].eventFermer(funFermer)
if (funLien != undefined)
this.conversations[num].eventLien(funLien)
this.conversations[num].setFunPage(
function(num) // page suivante
{
thisMessages.client.pageSuivante(num - 1)
thisMessages.rafraichirMessages(true)
},
function(num) // page précédente
{
thisMessages.client.pagePrecedente(num - 1)
thisMessages.rafraichirMessages(true)
},
function(num) // retour à la page une
{
if (thisMessages.client.goPremierePage(num - 1))
thisMessages.rafraichirMessages(true)
}
)
this.ajusterLargeurConversations()
}
/**
* Enlève une conversation.
*/
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()
// décalage TODO : supprimer le dernier élément
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)
}
/**
* Ajuste la largeur des conversations en fonction de leur nombre. modifie l'attribut CSS 'width'.
*/
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).
*/
Conversations.prototype.flushAll = function()
{
for (var i = 0; i < this.conversations.length; i++)
this.flush(i)
}
/**
* Demande à une conversation de se flusher.
*/
Conversations.prototype.flush = function(numConv)
{
this.conversations[numConv].flush()
}
Conversations.prototype.ouvrirConversation = function(racine)
{
if (this.client.ajouterConversation(racine))
this.rafraichirMessages(true)
else
this.util.messageDialogue("Cette conversation est déjà ouverte")
}
Conversations.prototype.viderMessages = function()
{
for (var i = 0; i < this.conversations.length; i++)
this.conversations[i].viderMessages()
}
/**
* Met à jour les messages de manière continue.
* (AJAX-Comet-style proof)
* @param vider vide tous les messages avant d'afficher les nouveaux
*/
Conversations.prototype.rafraichirMessages = function(vider)
{
var thisMessages = this
if (vider == undefined)
vider = false
if (vider)
for (var i = 0; i < this.conversations.length; i++)
this.conversations[i].idDernierMessageAffiche = 0
this.pageEvent.waitEvent(
function() { return thisMessages.getJSONrafraichirMessages() },
{
"new_troll" :
function(data)
{
thisMessages.trollIdCourant = data["troll_id"]
$("#trollCourant .troll").html(thisMessages.formateur.traitementComplet(data["content"])).unbind("click").click(
function()
{
thisMessages.ouvrirConversation(data["message_id"])
}
)
$("#trollCourant .troll a[@rel*=lightbox]").lightBox()
},
"new_messages" :
function(data)
{
if (vider)
thisMessages.viderMessages()
// ajoute les messages reçus à leur conversation respective
for (var numConv = 0; numConv < data["conversations"].length; numConv++)
{
if (!thisMessages.ajouterMessages(data["conversations"][numConv], numConv))
{
thisMessages.util.messageDialogue("La conversation {" + thisMessages.client.conversations[numConv -1].root.toString(36) + "} n'existe pas")
thisMessages.client.supprimerConversation(numConv - 1)
}
}
if (vider)
thisMessages.afficherMessagesRepondConversations()
vider = false
}
}
)
}