float: left;
width: 100%;
}
-#page.minichat #conversations div.message {
- border-left-width: 5px;
- border-left-style: solid;
- border-color: transparent;
- padding-right: 5px;
- cursor: pointer;
- min-height: 18px;
- line-height: 18px;
-}
#page.minichat #conversations div.messageImpair {
background-color: #fbeede;
}
filter: alpha(opacity = 30);
zoom: 1
}
-#page.minichat #conversations div.reponse {
- border-color: #bd7a11
-}
-#page.minichat #conversations div.repondu {
- border-color: #b711bd
-}
-#page.minichat #conversations div.proprietaire {
- border-color: #bd1129
-}
-#page.minichat #conversations div.systeme {
- background-color: #f0df95
-}
#page.minichat #conversations .titre {
text-align: center;
padding-right: 8px;
}
/***** Les messages *****/
+#page.minichat #conversations div.message {
+ padding-right: 5px;
+ cursor: pointer;
+ min-height: 18px;
+ line-height: 18px;
+}
+#page.minichat #conversations div.message.reponse .entete {
+ background-color: #bf2911
+}
+#page.minichat #conversations div.message.reponse .delimitationEntete,
+#page.minichat #conversations div.message.reponse .repondA {
+ background-image: url(../../img/css1/fleche_reponse.png);
+}
+#page.minichat #conversations div.message.repondu .entete {
+ background-color: #84196c
+}
+#page.minichat #conversations div.message.repondu .delimitationEntete,
+#page.minichat #conversations div.message.repondu .repondA {
+ background-image: url(../../img/css1/fleche_repondu.png);
+}
+#page.minichat #conversations div.message.proprietaire .entete {
+ background-color: #31732f
+}
+#page.minichat #conversations div.message.proprietaire .delimitationEntete,
+#page.minichat #conversations div.message.proprietaire .repondA {
+ background-image: url(../../img/css1/fleche_proprietaire.png);
+}
+#page.minichat #conversations div.message.systeme {
+ background-color: #f0df95
+}
#page.minichat div.message a {
font-weight: bold;
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="744.09448819"
+ height="1052.3622047"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="couleurs entetes messages css1.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.4"
+ inkscape:cx="131.32911"
+ inkscape:cy="504.74238"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="652"
+ inkscape:window-height="667"
+ inkscape:window-x="0"
+ inkscape:window-y="133" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ style="opacity:1;fill:#31732f;fill-opacity:1;fill-rule:evenodd;stroke:#0d00a8;stroke-width:0.34887081;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3153"
+ width="100.18613"
+ height="75.186127"
+ x="115.62122"
+ y="547.2691" />
+ <rect
+ style="opacity:1;fill:#841919;fill-opacity:1;fill-rule:evenodd;stroke:#0d00a8;stroke-width:0.34887081;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3155"
+ width="100.18613"
+ height="75.186127"
+ x="116.3355"
+ y="465.48337" />
+ <rect
+ style="opacity:1;fill:#bf2911;fill-opacity:1;fill-rule:evenodd;stroke:#0d00a8;stroke-width:0.34887081;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3157"
+ width="100.18613"
+ height="75.186127"
+ x="117.76408"
+ y="383.69766" />
+ <rect
+ style="opacity:1;fill:#84196c;fill-opacity:1;fill-rule:evenodd;stroke:#0d00a8;stroke-width:0.34887081;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="rect3159"
+ width="100.18613"
+ height="75.186127"
+ x="116.3355"
+ y="629.05481" />
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="49.285713"
+ y="507.79028"
+ id="text3181"><tspan
+ sodipodi:role="line"
+ id="tspan3183"
+ x="49.285713"
+ y="507.79028">référence</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="58.356026"
+ y="424.84149"
+ id="text3185"><tspan
+ sodipodi:role="line"
+ id="tspan3187"
+ x="58.356026"
+ y="424.84149">réponse</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="22.215401"
+ y="586.97449"
+ id="text3189"><tspan
+ sodipodi:role="line"
+ id="tspan3191"
+ x="22.215401"
+ y="586.97449">mon message</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans"
+ x="57.225166"
+ y="670.19867"
+ id="text3193"><tspan
+ sodipodi:role="line"
+ id="tspan3195"
+ x="57.225166"
+ y="670.19867">répondu</tspan></text>
+ </g>
+</svg>
/**
* classe permettant de gérer les événements (push serveur).
+ * l'information envoyé est sous la forme :
+ * {
+ * "action" : "wait_event"
+ * "page" : <page>
+ * [..]
+ * }
+ * l'information reçu est sous la forme :
+ * {
+ * "reply" : <reply>
+ * }
* @page la page
*/
function PageEvent(page, util)
/**
* Attend un événement lié à la page.
* @funSend une fonction renvoyant les données json à envoyer
- * @funReceive une fonction qui accepte un paramètre correspondant au données reçues
+ * @funsReceive est un objet comprenant les fonctions à appeler en fonction du "reply"
+ * les fonctions acceptent un paramètre correspondant au données reçues.
+ * exemple : {"new_message" : function(data){ ... }}
*/
-PageEvent.prototype.waitEvent = function(funSend, funReceive)
+PageEvent.prototype.waitEvent = function(funSend, funsReceive)
{
this.stopAttenteCourante()
this.attenteCourante = jQuery.ajax({
type: "POST",
url: "request",
- dataType: "json",
+ dataType: "json",\r
+ timeout: 300000, // timeout de 5min. Gros HACK pas beau. FIXME problème décrit ici : http://groups.google.com/group/jquery-en/browse_thread/thread/8724e64af3333a76
data: this.util.jsonVersAction(dataToSend),
success:
function(data)
{
;; dumpObj(data)
- funReceive(data)
+ funsReceive[data["reply"]](data)
// rappel de la fonction dans 100 ms
- setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funReceive) }, 100)
+ setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funsReceive) }, 100)
},
error:
function(XMLHttpRequest, textStatus, errorThrown)
{
- setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funReceive) }, 1000)
+ ;; console.log("Connexion perdue dans waitEvent")
+ setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funsReceive) }, 1000)
}
})
}
/**
* Si un stopAttenteCourante survient un peu n'importe quand il faut imédiatement arreter de boucler.
*/
-PageEvent.prototype.waitEvent2 = function(funSend, funReceive)
+PageEvent.prototype.waitEvent2 = function(funSend, funsReceive)
{
if (this.stop)
return
- this.waitEvent(funSend, funReceive)
+ this.waitEvent(funSend, funsReceive)
}
///////////////////////////////////////////////////////////////////////////////////////////////////
-// coding: utf-8\r
-// Copyright 2008 Grégory Burri\r
-//\r
-// This file is part of Euphorik.\r
-//\r
-// Euphorik is free software: you can redistribute it and/or modify\r
-// it under the terms of the GNU General Public License as published by\r
-// the Free Software Foundation, either version 3 of the License, or\r
-// (at your option) any later version.\r
-//\r
-// Euphorik is distributed in the hope that it will be useful,\r
-// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
-// GNU General Public License for more details.\r
-//\r
-// You should have received a copy of the GNU General Public License\r
+// 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,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// 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 <http://www.gnu.org/licenses/>.
+//
+// La page d'administation, ne peut être accédée que par les ekMaster (admins)
function PageAdmin(client, formateur, util)
{
this.pageEvent = new PageEvent("admin", this.util)
+ // le timer qui rappelle periodiquement le rafraichissement des IP bannies
this.timeoutIDmajIPs = null
}
+/**
+ * Interface des pages.
+ */
PageAdmin.prototype.contenu = function()
{
- return '<h1>Trolls</h1>\
+ return '\
+ <h1>Trolls</h1>\
<p>Un troll est un sujet à débat, en général une question, affiché sur la page principale.</p>\
<p>Chaque semaine un troll est choisi au hasard parmis les trolls proposés et devient le troll de la semaine.</p>\
<form action="" id="nouveauTroll">\
<div id="ips"></div>'
}
+/**
+ * Interface des pages.
+ */
PageAdmin.prototype.charger = function()
{
$("#page form#nouveauTroll").submit(function(){return false})
var thisPage = this
+ // la liste des trolls proposés par les ekMasters
this.trolls = new Trolls(this.client, this.util, this.formateur)
+
this.waitEvent()
this.majIPs()
)
}
+/**
+ * Interface des pages.
+ */
PageAdmin.prototype.decharger = function()
{
this.pageEvent.stopAttenteCourante()
+
+ // supprime le rafraichissement période des ips
+ if (this.timeoutIDmajIPs)
+ clearTimeout(this.timeoutIDmajIPs)
}
+/**
+ * Post un troll, le contenu est lu à partir de "input.troll".
+ */
PageAdmin.prototype.posterTroll = function()
{
var thisPageAdmin = this
}
// rafraichissement toutes les minutes (je sais c'est mal)
- // le problème est le rafraichissement des temps restant de bannissement qui doit êtrew fait du coté client
+ // le problème est le rafraichissement des temps restant de bannissement qui doit être fait du coté client
thisPageAdmin.timeoutIDmajIPs = setTimeout(function(){ thisPageAdmin.majIPs() }, 60 * 1000)
}
}
)
}
+/**
+ * Débannie une ip donnée.
+ */
PageAdmin.prototype.deban = function(ip)
{
var thisPageAdmin = this
function(data)
{
;; dumpObj(data)
- switch(data["reply"])
+ if(data["reply"] == "error")
{
- case "error" :
- thisPageAdmin.util.messageDialogue(data["error_message"])
- break
- case "ok" :
- // obsolète : plus besoin
- /* thisPageAdmin.majIPs() */
- break
+ thisPageAdmin.util.messageDialogue(data["error_message"])
}
}
}
)
}
+/**
+ * Attente d'événement de la part du serveur.
+ */
PageAdmin.prototype.waitEvent = function()
{
var thisPageAdmin = this
this.pageEvent.waitEvent(
function() { return { "last_troll" : thisPageAdmin.trolls.dernierTroll }},
- function(data)
{
- switch (data["reply"])
- {
- case "troll_added" :
- thisPageAdmin.trolls.ajouterTrollEvent(data)
- break
- case "troll_modified" :
- thisPageAdmin.trolls.modifierTrollEvent(data)
- break
- case "troll_deleted" :
- thisPageAdmin.trolls.supprimerTrollEvent(data)
- break
- case "banned_ips_refresh" :
- thisPageAdmin.majIPs()
- break
- case "error" :
+ "troll_added" : function(data){ thisPageAdmin.trolls.ajouterTrollEvent(data) },
+ "troll_modified" : function(data){ thisPageAdmin.trolls.modifierTrollEvent },
+ "troll_deleted" : function(data){ thisPageAdmin.trolls.supprimerTrollEvent },
+ "banned_ips_refresh" : function(data){ thisPageAdmin.majIPs() },
+ "error" :
+ function(data)
+ {
thisTrolls.util.messageDialogue(data["error_message"])
- break
- }
+ }
}
)
}
///////////////////////////////////////////////////////////////////////////////////////////////////
-
+/**
+ * Représente un troll, pas grand chose finalement.
+ */
function Troll(content, author)
{
this.content = content
/**
* 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 => bool}
+ * @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] = true
+ 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] = true
+ messagesEnEvidence[messages.messages[i].id] = messages.messages[i].auteurId == this.auteurId ? 1 : 2
+ var premierNiveau = true
var f = function(tabIds)
{
for(var id in tabIds)
var message = messages.messagesParId[id]
if (message != undefined)
{
- messagesEnEvidence[id] = true
+ messagesEnEvidence[id] = message.auteurId == thisMessage.auteurId ? 1 : ( premierNiveau ? 3 : 0 )
+ premierNiveau = false
f (message.repondA)
}
}
this.num = num // peut changer au cours de la vie de la conversation
this.id = Math.floor(Math.random() * 1000000).toString(36)
- this.messageOver = null // le message sur lequel se trouve le curseur
this.util = util
this.formateur = formateur
this.client = client
this.util.infoBulle("Créer un lien vers la conversation", $("#conversations #" + this.getId() + " .lien"))
this.util.infoBulle("Fermer la conversation", $("#conversations #" + this.getId() + " .fermer"))
}
-
- // enlève la mise en évidence pour la conversation
- $("#conversations #" + this.getId()).hover(
- function(){},
- function(event)
- {
- thisConversation.enleverMiseEnEvidence()
- }
- )
}
/**
Conversation.prototype.enleverMiseEnEvidence = function()
{
$("#conversations .message").removeClass("cache")
- this.messageOver = null
+}
+
+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 += "#mess" + this.messages[i].id.toString(36) + ","
+ else if (this.messages[i].estUneReponse)
+ messagesReponse += "#mess" + this.messages[i].id.toString(36) + ","
+ else if (this.messages[i].clientARepondu)
+ messagesRepondu += "#mess" + this.messages[i].id.toString(36) + ","
+ }
+ $(messagesReponse).addClass("reponse")
+ $(messagesRepondu).addClass("repondu")
+ $(messagesProprietaire).addClass("proprietaire")
+}
+
+Conversation.prototype.decolorerEntetes = function()
+{
+ $("#" + this.getId() + " .message")
+ .removeClass("reponse")
+ .removeClass("repondu")
+ .removeClass("proprietaire")
}
/**
$("input.message").val(tag + " " + valCourant)
thisConversation.util.setCaretToEnd($("form input.message")[0])
}
- )
-
- // Q : pourquoi pas un .hover ?
- // R : simplement pour éviter que lorsqu'un message arrive cela n'affecte la conversation actuellement mise en évidence (uniquement pour Firefox)
- $(".entete", this).mousemove(
- function(e)
+ )\r
+ \r
+ // mise en évidence de la conversation
+ $(".entete", this).hover(\r
+ function()\r
{
- if (this !== thisConversation.messageOver)
- {
- thisConversation.afficherConversation(idMess)
- thisConversation.messageOver = this
- }
- }
- ).hover(
- function(){},
+ thisConversation.decolorerEntetes()\r
+ thisConversation.afficherConversation(idMess)\r
+ },
// quand on sort de l'entête du message la mise en évidence est enlevée
function()
{
- thisConversation.enleverMiseEnEvidence()
+ thisConversation.enleverMiseEnEvidence()
+ thisConversation.decolorerEntetes()
+ thisConversation.colorerEntetes()
}
)
}
function()
{
var jq = $(this)
- if (!mess.hasOwnProperty(parseInt(jq.attr("id").substr(4), 36)))
+ var statut = mess[parseInt(jq.attr("id").substr(4), 36)]
+ if (statut == undefined)
jq.addClass("cache")
- else
+ else
+ {
jq.removeClass("cache")
+ switch (statut)
+ {
+ case 1 :
+ jq.addClass("proprietaire")
+ break;
+ case 2 :
+ jq.addClass("reponse")
+ break;
+ case 3 :
+ jq.addClass("repondu")
+ break;
+ }
+ }
}
)
}
this.pageEvent.waitEvent(
function() { return thisMessages.getJSONrafraichirMessages() },
- function(data)
- {
- if (vider)
- {
- thisMessages.viderMessages()
- vider = false
- }
-
- // en fonction du message
- switch(data["reply"])
- {
- case "new_troll" :
+ {
+ "new_troll" :
+ function(data)
+ {
thisMessages.trollIdCourant = data["troll_id"]
$("#trollCourant .troll").html(thisMessages.formateur.traitementComplet(data["content"])).unbind("click").click(
function()
)
$("#trollCourant .troll a[@rel*=lightbox]").lightBox()
- break
-
- case "new_messages" :
+ },
+ "new_messages" :
+ function(data)
+ {
+ if (vider)
+ {
+ thisMessages.viderMessages()
+ vider = false
+ }
// ajoute les messages reçus à leur conversation respective
for (var numConv = 0; numConv < data["conversations"].length; numConv++)
{
thisMessages.client.supprimerConversation(numConv - 1)
}
}
- break
- }
+ }
}
)
}
--- /dev/null
+% 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,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% 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 <http://www.gnu.org/licenses/>.
+%
+% Module avec plein de bordel utile à l'intérieur
+% @author G.Burri
+
+-module(euphorik_common).
+-export([serialize_ip/1, unserialize_ip/1]).
+
+
+serialize_ip(undefined) ->
+ "<unknown IP>";
+serialize_ip(IP) ->
+ lists:flatten(io_lib:format("~w.~w.~w.~w", tuple_to_list(IP))).
+
+
+unserialize_ip(IP) ->
+ case io_lib:fread("~d.~d.~d.~d", IP) of
+ {ok, [A, B, C, D], []} -> {A, B, C, D};
+ _ -> erreur
+ end.
\r
loop() ->
- % on attend une minute de plus pour prevenir une dérive nétive
+ % on attend une minute de plus pour prevenir une dérive négative
timer:sleep(1000 * trunc(temps_prochaine_election() + 60)),
euphorik_bd:elire_troll(),\r
euphorik_daemon:loop().
% Après 60 minutes de connexion, le client doit donc reétablir une connexion
after 1000 * 60 * 60 ->
timeout
- end.
+ end.
% Attent un événement concernant la page admin