MOD avancement dans la Grande Restructuration
[euphorik.git] / js / euphorik.js
index a3b0acf..b93498f 100755 (executable)
-// 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
-// along with Euphorik.  If not, see <http://www.gnu.org/licenses/>.\r
-\r
-/**\r
-  * Contient la base javascript pour le site euphorik.ch.\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/>.
+
+/**
+  * Contient la base javascript pour le site euphorik.ch.
   * Chaque page possède son propre fichier js nommé "page<nom de la page>.js".
   * Auteur : GBurri
   * Date : 6.11.2007
   */
   * Chaque page possède son propre fichier js nommé "page<nom de la page>.js".
   * Auteur : GBurri
   * Date : 6.11.2007
   */
-  
 
 
-/**
-  * La configuration.\r
-  * Normalement 'const' à la place de 'var' mais non supporté par IE7.
-  */\r
-var conf = {\r
-   nbMessageAffiche : 40, // (par page)
-   pseudoDefaut : "<nick>",\r
-   tempsAffichageMessageDialogue : 4000, // en ms\r
-   smiles : {   \r
-      "smile" : [/:\)/g, /:-\)/g],  \r
-      "bigsmile" : [/:D/g, /:-D/g],\r
-      "clin" : [/;\)/g, /;-\)/g],\r
-      "cool" : [/8\)/g, /8-\)/g],\r
-      "eheheh" : [/:P/g, /:-P/g],
-      "lol" : [/\[-lol\]/g],
-      "spliff" : [/\[-spliff\]/g],\r
-      "oh" : [/:o/g, /:O/g],
-      "heink" : [/\[-heink\]/g],
-      "hum" : [/\[-hum\]/g],
-      "boh" : [/\[-boh\]/g],
-      "sniff" : [/:\(/g, /:-\(/g],
-      "triste" : [/\[-triste\]/g],\r
-      "pascontent" : [/>\(/g, /&gt;\(/g],\r
-      "argn" : [/\[-argn\]/g],
-      "redface" : [/\[-redface\]/g],\r
-      "bunny" : [/\[-lapin\]/g],\r
-      "chat" : [/\[-chat\]/g],\r
-      "renne" : [/\[-renne\]/g],\r
-      "star" : [/\[-star\]/g],\r
-      "kirby" : [/\[-kirby\]/g],
-      "slurp" : [/\[-slurp\]/g],
-      "agreed" : [/\[-agreed\]/g],
-      "dodo" : [/\[-dodo\]/g],
-      "bn" : [/\[-bn\]/g]\r
-   }\r
-}\r
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-\r
-String.prototype.trim = function()\r
-{\r
-       return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");\r
-}\r
+// tout euphorik est contenu dans cet objet
+var euphorik = {}
+// ;; euphorik.include = 
+// ;; euphorik.include = function(f) { var s = document.createElement('script'); s.type = 'text/javascript'; s.src = "js/" + f + ".js"; document.getElementsByTagName('head')[0].appendChild(s); }
+
+
+// version jQuery : function(f) { jQuery.ajax({async : false, url : "js/" + f + ".js", dataType : "script"}) }
+// mais comme il n'est pas encore chargé...
+;; euphorik.include = function(f) {
+;;    var req, url = 'js/' + f + '.js'
+;;    if  (window.XMLHttpRequest) {
+;;       req = new XMLHttpRequest(); req.open("GET", url, false /* async */); req.send(null);
+;;    } else if (window.ActiveXObject) {    
+;;       req = new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') != -1) ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP");
+;;       if (req) { req.open("GET", url, false); req.send(); }
+;;    }
+;;    if (req!==false) { if (req.status==200) { window.eval(req.responseText); } else if (req.status==404) { alert("erreur de chargement (404) de : " + url) } }
+;; } 
+
+;; euphorik.include("jquery")
+;; euphorik.include("jquery.lightbox")
+;; euphorik.include("md5")
+;; euphorik.include("json2")
+
+;; euphorik.include("conf")
+;; euphorik.include("util")\r
+;; euphorik.include("formateur")\r
+;; euphorik.include("pages")
+
+;; euphorik.include("pageMinichat")
+;; euphorik.include("pageAdmin")
+;; euphorik.include("pageProfile")
+;; euphorik.include("pageRegister")
+;; euphorik.include("pageAbout")
 \r
 \r
-String.prototype.ltrim = function()\r
-{\r
-       return this.replace(/^\s+/, "");\r
-}\r
-\r
-String.prototype.rtrim = function()\r
-{\r
-       return this.replace(/\s+$/, "");\r
-}\r
-\r
-///////////////////////////////////////////////////////////////////////////////////////////////////\r
 \r
 \r
+// tout un tas d'améliorations des objets javascript ;)\r
 /**\r
 /**\r
-  * Cette classe regroupe des fonctions utilitaires (helpers).
-  * @formateur est permet de formater les messages affichés à l'aide de messageDialogue (facultatif)\r
-  */
-function Util(formateur)
-{
-   $("#info .fermer").click(function(){
-      $("#info").slideUp(50) 
-   })
-   
-   this.formateur = formateur
-}
-
-var messageType = {informatif: 0, question: 1, erreur: 2}
-
-/**
-  * Affiche une boite de dialogue avec un message à l'intérieur.
-  * @param message le message (string)\r
-  * @param type voir 'messageType'. par défaut messageType.informatif\r
-  * @param les boutons sous la forme d'un objet ou les clefs sont les labels des boutons\r
-  *        et les valeurs les fonctions executées lorsqu'un bouton est activé.
-  * @param formate faut-il formaté le message ? true par défaut
-  */
-Util.prototype.messageDialogue = function(message, type, boutons, formate)
-{
-   var thisUtil = this
-
-   if (type == undefined)
-      type = messageType.informatif
-      
-   if (formate == undefined)
-      formate = true
-
-   if (this.timeoutMessageDialogue != undefined)
-      clearTimeout(this.timeoutMessageDialogue)
-      
-   var fermer = function(){$("#info").slideUp(100)}
-   fermer()   
-   
-   $("#info .message").html(thisUtil.formateur == undefined || !formate ? message : thisUtil.formateur.traitementComplet(message))
-   switch(type)
-   {
-      case messageType.informatif : $("#info #icone").attr("class", "information"); break
-      case messageType.question : $("#info #icone").attr("class", "interrogation"); break
-      case messageType.erreur : $("#info #icone").attr("class", "exclamation"); break
-   }   
-   $("#info .boutons").html("")
-   for (var b in boutons)
-      $("#info .boutons").append("<div>" + b + "</div>").find("div:last").click(boutons[b]).click(fermer)
-   
-   $("#info").slideDown(200)
-   this.timeoutMessageDialogue = setTimeout(fermer, conf.tempsAffichageMessageDialogue)   
-}\r
-
-/**
-  * Utilisé pour l'envoie de donnée avec la méthode ajax de jQuery.
-  */
-Util.prototype.jsonVersAction = function(json)
-{
-   return {action : JSON.stringify(json) }
-}
-\r
-Util.prototype.md5 = function(chaine)\r
-{\r
-   return hex_md5(chaine)\r
-}
-\r
-// pompé de http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130\r
-Util.prototype.setSelectionRange = function(input, selectionStart, selectionEnd)
-{\r
-   if (input.setSelectionRange)
-   {\r
-      input.focus()\r
-      input.setSelectionRange(selectionStart, selectionEnd)\r
-   }\r
-   else if (input.createTextRange)
-   {\r
-      var range = input.createTextRange()\r
-      range.collapse(true)\r
-      range.moveEnd('character', selectionEnd)\r
-      range.moveStart('character', selectionStart)\r
-      range.select()\r
-   }\r
-}
-\r
-Util.prototype.setCaretToEnd = function(input)
-{\r
-   this.setSelectionRange(input, input.value.length, input.value.length)\r
-}\r
-Util.prototype.setCaretToBegin = function(input)
-{\r
-   this.setSelectionRange(input, 0, 0)\r
-}\r
-Util.prototype.setCaretToPos = function(input, pos)
-{\r
-   this.setSelectionRange(input, pos, pos)\r
-}\r
-Util.prototype.selectString = function(input, string)
-{\r
-   var match = new RegExp(string, "i").exec(input.value)\r
-   if (match)
-   {\r
-      this.setSelectionRange (input, match.index, match.index + match[0].length)\r
-   }\r
-}\r
-Util.prototype.replaceSelection = function(input, replaceString) {\r
-   if (input.setSelectionRange)
-   {\r
-      var selectionStart = input.selectionStart\r
-      var selectionEnd = input.selectionEnd\r
-      input.value = input.value.substring(0, selectionStart) + replaceString + input.value.substring(selectionEnd)
-      \r
-      if (selectionStart != selectionEnd) // has there been a selection\r
-         this.setSelectionRange(input, selectionStart, selectionStart + replaceString.length)\r
-      else // set caret\r
-         this.setCaretToPos(input, selectionStart + replaceString.length)\r
-   }\r
-   else if (document.selection)
-   {\r
-      var range = document.selection.createRange();\r
-      if (range.parentElement() == input)
-      {\r
-         var isCollapsed = range.text == ''\r
-         range.text = replaceString\r
-         if (!isCollapsed)
-         {
-            // there has been a selection\r
-            // it appears range.select() should select the newly \r
-            // inserted text but that fails with IE\r
-            range.moveStart('character', -replaceString.length);\r
-            range.select();\r
-         }\r
+  * Pour chaque propriété de l'objet execute f(p, v) ou p est le nom de la propriété et v sa valeur.\r
+  * Ne parcours pas les propriétés des prototypes.\r
+  */\r
+object.prototype.each = function(f) {\r
+   for (var b in boutons) {\r
+      if (boutons.hasOwnProperty(b)) {\r
+         f(b, boutons[b])\r
       }\r
    }\r
       }\r
    }\r
-}
-
-Util.prototype.rot13 = function(chaine)
-{
-   var ACode = 'A'.charCodeAt(0)
-   var aCode = 'a'.charCodeAt(0)
-   var MCode = 'M'.charCodeAt(0)
-   var mCode = 'm'.charCodeAt(0)
-   var ZCode = 'Z'.charCodeAt(0)
-   var zCode = 'z'.charCodeAt(0)
-
-   var f = function(ch, pos) {
-      if (pos == ch.length)
-         return ""
-      
-      var c = ch.charCodeAt(pos);
-      return String.fromCharCode(
-         c +
-         (c >= ACode && c <= MCode || c >= aCode && c <= mCode ? 13 :
-         (c > MCode && c <= ZCode || c > mCode && c <= zCode ? -13 : 0))
-      ) + f(ch, pos + 1)
-   }
-   return f(chaine, 0)
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-function Pages()
-{
-   this.pageCourante = null
-   this.pages = {}
-}
-
-Pages.prototype.ajouterPage = function(page)
-{
-   page.pages = this // la magie des langages dynamiques : le foutoire
-   this.pages[page.nom] = page
-}
-
-Pages.prototype.afficherPage = function(nomPage, forcerChargement)
-{\r
-   if (forcerChargement == undefined) forcerChargement = false\r
-
-   var page = this.pages[nomPage]
-   if (page == undefined || (!forcerChargement && page == this.pageCourante)) return
-   
-   if (this.pageCourante != null && this.pageCourante.decharger)
-      this.pageCourante.decharger()
-  
-   $("#menu li").removeClass("courante")
-   $("#menu li." + nomPage).addClass("courante")
-      
-   this.pageCourante = page
-   $("#page").html(this.pageCourante.contenu()).removeClass().addClass(this.pageCourante.nom)
-   
-   if (this.pageCourante.charger)
-      this.pageCourante.charger()
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
-  * Classe permettant de formater du texte par exemple pour la substitution des liens dans les
-  * message par "[url]".
-  * TODO : améliorer l'efficacité des méthods notamment lié au smiles.
-  */
-function Formateur()
-{
-   this.smiles = conf.smiles\r
-   this.protocoles = "http|https|ed2k"\r
-   \r
-   this.regexUrl = new RegExp("(?:(?:" + this.protocoles + ")://|www\\.)[^ ]*", "gi")\r
-   this.regexImg = new RegExp("^.*?\\.(gif|jpg|png|jpeg|bmp|tiff)$", "i")\r
-   this.regexDomaine = new RegExp("^(?:(?:" + this.protocoles + ")://|www\\.).*?([^/.]+\\.[^/.]+)(?:$|/).*$", "i")\r
-   this.regexTestProtocoleExiste = new RegExp("^(?:" + this.protocoles + ")://.*$", "i")\r
-   this.regexNomProtocole = new RegExp("^(.*?)://")
-}
-
-/**
-  * Formate un pseudo saise par l'utilisateur.
-  * @param pseudo le pseudo brut
-  * @return le pseudo filtré
-  */
-Formateur.prototype.filtrerInputPseudo = function(pseudo)
-{
-   return pseudo.replace(/{|}/g, "").trim()
-}
-\r
-Formateur.prototype.getSmilesHTML = function()\r
-{\r
-   var XHTML = ""\r
-   for (var sNom in this.smiles)\r
-   {\r
-      XHTML += "<img class=\"" + sNom + "\" src=\"img/smileys/" + sNom + ".gif\" alt =\"" + sNom + "\" />"\r
-   }\r
-   return XHTML\r
 }\r
 
 }\r
 
-/**
-  * Formatage complet d'un texte.
-  * @M le message
-  * @pseudo facultatif, permet de contruire le label des images sous la forme : "<Pseudo> : <Message>"
-  */
-Formateur.prototype.traitementComplet = function(M, pseudo)
-{
-   return this.traiterLiensConv(this.traiterSmiles(this.traiterURL(this.traiterWikiSyntaxe(this.remplacerBalisesHTML(M)), pseudo)))
+String.prototype.trim = function() {
+       return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");
 }
 
 }
 
-/**
-  * Transforme les liens en entités clickables.
-  * Un lien vers une conversation permet d'ouvrire celle ci, elle se marque comme ceci dans un message :
-  * "{5F}" ou 5F est la racine de la conversation.
-  * Ce lien sera transformer en <span class="lienConv">{5F}</span> pouvant être clické pour créer la conv 5F.
-  */
-Formateur.prototype.traiterLiensConv = function(M)
-{
-   return M.replace(
-      /\{\w+\}/g,
-      function(lien)
-      {
-         return "<span class=\"lienConv\">" + lien + "</span>"
-      }
-   )
-}
-\r
-/**\r
-  * FIXME : Cette méthode est attrocement lourde ! A optimiser.
-  * moyenne sur échantillon : 234ms\r
-  */
-Formateur.prototype.traiterSmiles = function(M)
-{  
-   for (var sNom in this.smiles)
-   {
-      ss = this.smiles[sNom]
-      for (var i = 0; i < ss.length; i++)       
-         M = M.replace(ss[i], "<img src=\"img/smileys/" + sNom + ".gif\" alt =\"" + sNom + "\"  />")
-   }
-   return M
+String.prototype.ltrim = function() {
+       return this.replace(/^\s+/, "");
 }
 
 }
 
-Formateur.prototype.remplacerBalisesHTML = function(M)
-{
-   return M.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
-}
-
-Formateur.prototype.traiterURL = function(M, pseudo)
-{
-   thisFormateur = this
-         
-   var traitementUrl = function(url)
-   {    \r
-      // si ya pas de protocole on rajoute "http://"\r
-      if (!thisFormateur.regexTestProtocoleExiste.test(url))\r
-         url = "http://" + url
-      var extension = thisFormateur.getShort(url)
-      return "<a " + (extension[1] ? "title=\"" + (pseudo == undefined ? "" : thisFormateur.traiterPourFenetreLightBox(pseudo, url) + ": ") +  thisFormateur.traiterPourFenetreLightBox(M, url) + "\"" + " rel=\"lightbox\"" : "") + " href=\"" + url + "\" >[" + extension[0] + "]</a>"
-   }
-   return M.replace(this.regexUrl, traitementUrl)
-}
-
-/**
-  * Formatage en utilisant un sous-ensemble des règles de mediwiki.
-  * par exemple ''italic'' devient <i>italic</i>
-  */
-Formateur.prototype.traiterWikiSyntaxe = function(M)
-{
-   return M.replace(
-      /'''(.*?)'''/g,
-      function(texte, capture)
-      {
-         return "<b>" + capture + "</b>"
-      }
-   ).replace(
-      /''(.*?)''/g,
-      function(texte, capture)
-      {
-         return "<i>" + capture + "</i>"
-      }
-   )
-}
-\r
-/**\r
-  * Renvoie une version courte de l'url.\r
-  * par exemple : http://en.wikipedia.org/wiki/Yakov_Smirnoff devient wikipedia.org\r
-  */
-Formateur.prototype.getShort = function(url)
-{\r
-      var estUneImage = false
-      var versionShort = null
-      var rechercheImg = this.regexImg.exec(url)
-      
-      if (rechercheImg != null)\r
-      {
-         versionShort = rechercheImg[1].toLowerCase()\r
-         if (versionShort == "jpeg") versionShort = "jpg" // jpeg -> jpg\r
-         estUneImage = true\r
-      }\r
-      else\r
-      {\r
-         var rechercheDomaine = this.regexDomaine.exec(url)\r
-         if (rechercheDomaine != null && rechercheDomaine.length >= 2)\r
-            versionShort = rechercheDomaine[1]\r
-         else\r
-         {\r
-            var nomProtocole = this.regexNomProtocole.exec(url)\r
-            if (nomProtocole != null && nomProtocole.length >= 2)\r
-               versionShort = nomProtocole[1]\r
-         }\r
-      }\r
-      
-      return [versionShort == null ? "url" : versionShort, estUneImage]
- }
-/**
-  * Traite les pseudo et messages à être affiché dans le titre d'une image visualisé avec lightbox.
-  */
-Formateur.prototype.traiterPourFenetreLightBox = function(M, urlCourante)
-{
-   thisFormateur = this
-   var traitementUrl = function(url)
-   {
-      return "[" + thisFormateur.getShort(url)[0] + (urlCourante == url ? "*" : "") + "]"
-   }
-   \r
-   return this.remplacerBalisesHTML(M).replace(this.regexUrl, traitementUrl)
+String.prototype.rtrim = function() {
+       return this.replace(/\s+$/, "");
 }
 
 
 }
 
 
@@ -453,38 +96,46 @@ var statutType = {
    // mode déconnecté, ne peut pas poster de message
    deconnected : 2
 }
    // mode déconnecté, ne peut pas poster de message
    deconnected : 2
 }
-\r
-function Client(util)\r
+
+function Client(util)
 {
    this.util = util
 {
    this.util = util
-   \r
+   
    this.cookie = null
    this.regexCookie = new RegExp("^cookie=([^;]*)")
    
    this.cookie = null
    this.regexCookie = new RegExp("^cookie=([^;]*)")
    
-   // données personnels\r
+   // données personnels
    this.resetDonneesPersonnelles()
    
    this.resetDonneesPersonnelles()
    
-   this.setStatut(statutType.deconnected)\r
+   this.setStatut(statutType.deconnected)
+   
+   // si true alors chaque modification du client est mémorisé sur le serveur
+   this.autoflush = $.browser["opera"]
 }
 }
-\r
-Client.prototype.resetDonneesPersonnelles = function()\r
+
+Client.prototype.resetDonneesPersonnelles = function()
 {
 {
-   this.id = 0\r
-   this.pseudo = conf.pseudoDefaut\r
-   this.login = ""\r
-   this.password = ""\r
-   this.email = ""\r
+   this.id = 0
+   this.pseudo = euphorik.conf.pseudoDefaut
+   this.login = ""
+   this.password = ""
+   this.email = ""
    this.css = $("link#cssPrincipale").attr("href")
    this.css = $("link#cssPrincipale").attr("href")
+   this.chatOrder = "reverse"
    this.nickFormat = "nick"
    this.nickFormat = "nick"
+   this.viewTimes = true
+   this.viewTooltips = true
    this.cookie = undefined
    
    this.pagePrincipale = 1
    this.ekMaster = false
    this.cookie = undefined
    
    this.pagePrincipale = 1
    this.ekMaster = false
+   this.ostentatiousMaster = "light"
    
    // les conversations, une conversation est un objet possédant les attributs suivants :
    
    // les conversations, une conversation est un objet possédant les attributs suivants :
-   // - racine (entier)
+   // - root (entier)
    // - page (entier)
    // - page (entier)
-   this.conversations = new Array()\r
+   // - reduit (bool)
+   this.conversations = []
 }
 
 Client.prototype.setCss = function(css)
 }
 
 Client.prototype.setCss = function(css)
@@ -494,7 +145,7 @@ Client.prototype.setCss = function(css)
 
    this.css = css
    $("link#cssPrincipale").attr("href", this.css)
 
    this.css = css
    $("link#cssPrincipale").attr("href", this.css)
-   this.majMenu()
+   if (this.autoflush) this.flush(true)
 }
 
 Client.prototype.pageSuivante = function(numConv)
 }
 
 Client.prototype.pageSuivante = function(numConv)
@@ -546,8 +197,10 @@ Client.prototype.ajouterConversation = function(racine)
    for (var i = 0; i < this.conversations.length; i++)
       if (this.conversations[i].root == racine)
          return false
    for (var i = 0; i < this.conversations.length; i++)
       if (this.conversations[i].root == racine)
          return false
-         
-   this.conversations.push({root : racine, page : 1})
+   
+   this.conversations.push({root : racine, page : 1, reduit : false})
+   if (this.autoflush) this.flush(true)
+   
    return true
 }
 
    return true
 }
 
@@ -559,12 +212,14 @@ Client.prototype.supprimerConversation = function(num)
    for (var i = num; i < this.conversations.length - 1; i++)
       this.conversations[i] = this.conversations[i+1]
    this.conversations.pop()
    for (var i = num; i < this.conversations.length - 1; i++)
       this.conversations[i] = this.conversations[i+1]
    this.conversations.pop()
+   
+   if (this.autoflush) this.flush(true)
 }
 
 Client.prototype.getJSONLogin = function(login, password)
 {
    return {
 }
 
 Client.prototype.getJSONLogin = function(login, password)
 {
    return {
-      "action" : "authentification",
+      "header" : { "action" : "authentification", "version" : euphorik.conf.versionProtocole },
       "login" : login,
       "password" : password
    }
       "login" : login,
       "password" : password
    }
@@ -573,7 +228,7 @@ Client.prototype.getJSONLogin = function(login, password)
 Client.prototype.getJSONLoginCookie = function()
 {
    return {
 Client.prototype.getJSONLoginCookie = function()
 {
    return {
-      "action" : "authentification",
+      "header" : { "action" : "authentification", "version" : euphorik.conf.versionProtocole },
       "cookie" : this.cookie
    }
 }  
       "cookie" : this.cookie
    }
 }  
@@ -584,7 +239,7 @@ Client.prototype.getJSONLoginCookie = function()
   */
 Client.prototype.getJSONEnregistrement = function(login, password)
 {
   */
 Client.prototype.getJSONEnregistrement = function(login, password)
 {
-   var mess = { "action" : "register" }
+   var mess = { "header" : { "action" : "register", "version" : euphorik.conf.versionProtocole }}
    
    if (login != undefined && password != undefined)
    {
    
    if (login != undefined && password != undefined)
    {
@@ -599,23 +254,26 @@ Client.prototype.getJSONConversations = function()
 {
    var conversations = new Array()
    for (var i = 0; i < this.conversations.length; i++)
 {
    var conversations = new Array()
    for (var i = 0; i < this.conversations.length; i++)
-      conversations.push({ "root" : this.conversations[i].root, "page" : this.conversations[i].page})
+      conversations.push({root : this.conversations[i].root, minimized : this.conversations[i].reduit})
    return conversations
 }
 
 Client.prototype.getJSONProfile = function()
 {
    return {
    return conversations
 }
 
 Client.prototype.getJSONProfile = function()
 {
    return {
-      "action" : "set_profile",
+      "header" : { "action" : "set_profile", "version" : euphorik.conf.versionProtocole },
       "cookie" : this.cookie,
       "login" : this.login,
       "password" : this.password,
       "nick" : this.pseudo,
       "email" : this.email,
       "css" : this.css,
       "cookie" : this.cookie,
       "login" : this.login,
       "password" : this.password,
       "nick" : this.pseudo,
       "email" : this.email,
       "css" : this.css,
+      "chat_order" : this.chatOrder,
       "nick_format" : this.nickFormat,
       "nick_format" : this.nickFormat,
-      "main_page" : this.pagePrincipale < 1 ? 1 : this.pagePrincipale,
-      "conversations" : this.getJSONConversations()
+      "view_times" : this.viewTimes,
+      "view_tooltips" : this.viewTooltips,
+      "conversations" : this.getJSONConversations(),
+      "ostentatious_master" : this.ostentatiousMaster
    }
 }
 
    }
 }
 
@@ -627,21 +285,23 @@ Client.prototype.getCookie = function()
    var cookie = this.regexCookie.exec(document.cookie)
    if (cookie == null) this.cookie = null
    else this.cookie = cookie[1]
    var cookie = this.regexCookie.exec(document.cookie)
    if (cookie == null) this.cookie = null
    else this.cookie = cookie[1]
-}\r
-\r
-Client.prototype.delCookie = function()\r
-{\r
-   document.cookie = "cookie=; max-age=0"\r
 }
 
 }
 
-Client.prototype.setCookie = function(cookie)
+Client.prototype.delCookie = function()
+{
+   document.cookie = "cookie=; max-age=0"
+}
+
+Client.prototype.setCookie = function()
 {
 {
-   if (this.cookie == null)
+   if (this.cookie == null || this.cookie == undefined)
       return
       
       return
       
-   document.cookie =
-      "cookie="+this.cookie+
-      "; max-age="  + (60 * 60 * 24 * 365)
+   // ne fonctionne pas sous IE....
+   /*document.cookie = "cookie=" + this.cookie + "; max-age="  + (60 * 60 * 24 * 365) */
+   
+   document.cookie = 
+      "cookie="+this.cookie+"; expires=" + new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 365).toUTCString()
 }
 
 Client.prototype.authentifie = function()
 }
 
 Client.prototype.authentifie = function()
@@ -651,38 +311,39 @@ Client.prototype.authentifie = function()
 
 Client.prototype.setStatut = function(statut)
 {  
 
 Client.prototype.setStatut = function(statut)
 {  
-   // conversation en "enum" si en "string"\r
-   if (typeof(statut) == "string")\r
+   // conversation en "enum" si en "string"
+   if (typeof(statut) == "string")
    {
       statut =
          statut == "auth_registered" ?
             statutType.auth_registered :
    {
       statut =
          statut == "auth_registered" ?
             statutType.auth_registered :
-         (statut == "auth_not_registered" ? statutType.auth_not_registered : statutType.deconnected)\r
-   }   \r
-   \r
-   if (statut == this.statut) return\r
-   \r
-   this.statut = statut   \r
+         (statut == "auth_not_registered" ? statutType.auth_not_registered : statutType.deconnected)
+   }   
+   
+   if (statut == this.statut) return
+   
+   this.statut = statut   
    this.majMenu()
    this.majMenu()
+   this.majLogo()
 }
 }
-\r
-/**\r
-  * Effectue la connexion vers le serveur.\r
-  * Cette fonction est bloquante tant que la connexion n'a pas été établie.\r
-  * S'il existe un cookie en local on s'authentifie directement avec lui.\r
-  * Si il n'est pas possible de s'authentifier alors on affiche un captcha anti-bot.\r
-  */\r
-Client.prototype.connexionCookie = function()\r
-{\r
-   this.getCookie()\r
+
+/**
+  * Effectue la connexion vers le serveur.
+  * Cette fonction est bloquante tant que la connexion n'a pas été établie.
+  * S'il existe un cookie en local on s'authentifie directement avec lui.
+  * Si il n'est pas possible de s'authentifier alors on affiche un captcha anti-bot.
+  */
+Client.prototype.connexionCookie = function()
+{
+   this.getCookie()
    if (this.cookie == null) return false;
    if (this.cookie == null) return false;
-   return this.connexion(this.getJSONLoginCookie())\r
+   return this.connexion(this.getJSONLoginCookie())
 }
 
 Client.prototype.connexionLogin = function(login, password)
 {
    return this.connexion(this.getJSONLogin(login, password))
 }
 
 Client.prototype.connexionLogin = function(login, password)
 {
    return this.connexion(this.getJSONLogin(login, password))
-}\r
+}
 
 Client.prototype.enregistrement = function(login, password)
 { 
 
 Client.prototype.enregistrement = function(login, password)
 { 
@@ -697,16 +358,18 @@ Client.prototype.enregistrement = function(login, password)
       }
       return false
    }
       }
       return false
    }
-   else\r
+   else
    {
    {
-      return this.connexion(this.getJSONEnregistrement(login, password))\r
+      return this.connexion(this.getJSONEnregistrement(login, password))
    }
 }
 
    }
 }
 
+/**
+  * Connexion. Réalisé de manière synchrone.
+  */
 Client.prototype.connexion = function(messageJson)
 {
 Client.prototype.connexion = function(messageJson)
 {
-   ;;; dumpObj(messageJson)
-   thisClient = this
+   var thisClient = this
    jQuery.ajax(
       {
          async: false,
    jQuery.ajax(
       {
          async: false,
@@ -717,23 +380,26 @@ Client.prototype.connexion = function(messageJson)
          success:
             function(data)
             {
          success:
             function(data)
             {
-               ;;; dumpObj(data)
                if (data["reply"] == "error")
                if (data["reply"] == "error")
+               {
                   thisClient.util.messageDialogue(data["error_message"])
                   thisClient.util.messageDialogue(data["error_message"])
+                  // suppression du cookie actuel, cas où le cookie du client ne permet pas une authentification
+                  thisClient.delCookie()
+               }
                else
                   thisClient.chargerDonnees(data)
             }
       }
    )
    return this.authentifie()
                else
                   thisClient.chargerDonnees(data)
             }
       }
    )
    return this.authentifie()
-}\r
-\r
-Client.prototype.deconnexion = function()\r
+}
+
+Client.prototype.deconnexion = function()
 {
    this.flush(true)
    this.delCookie()
 {
    this.flush(true)
    this.delCookie()
-   this.resetDonneesPersonnelles()\r
-   this.setStatut(statutType.deconnected) // deconnexion\r
+   this.resetDonneesPersonnelles()
+   this.setStatut(statutType.deconnected) // deconnexion
 }
 
 Client.prototype.chargerDonnees = function(data)
 }
 
 Client.prototype.chargerDonnees = function(data)
@@ -749,18 +415,28 @@ Client.prototype.chargerDonnees = function(data)
       this.cookie = data["cookie"]
       this.setCookie()
       
       this.cookie = data["cookie"]
       this.setCookie()
       
-      this.id = data["id"]\r
+      this.id = data["id"]
       this.login = data["login"]
       this.login = data["login"]
-      this.pseudo = data["nick"]\r
-      this.email = data["email"]\r
+      this.pseudo = data["nick"]
+      this.email = data["email"]
       this.setCss(data["css"])
       this.setCss(data["css"])
+      this.chatOrder = data["chat_order"]
       this.nickFormat = data["nick_format"]
       this.nickFormat = data["nick_format"]
+      this.viewTimes = data["view_times"]
+      this.viewTooltips = data["view_tooltips"]
+      this.ostentatiousMaster = data["ostentatious_master"]
       
       // la page de la conversation principale
       
       // la page de la conversation principale
-      this.pagePrincipale = data["main_page"] == undefined ? 1 : data["main_page"]
+      this.pagePrincipale = 1
       
       // les conversations
       this.conversations = data["conversations"]
       
       // les conversations
       this.conversations = data["conversations"]
+      for (var i = 0; i < this.conversations.length; i++)
+         this.conversations[i] = {root : this.conversations[i].root, page : 1, reduit : this.conversations[i].minimized}
+      
+      this.majBulle()
+      this.majCssSelectionee()
+      //this.majLogo()
    }
 }
 
    }
 }
 
@@ -779,8 +455,6 @@ Client.prototype.flush = function(async)
 
    var thisClient = this
    var ok = true
 
    var thisClient = this
    var ok = true
-   
-   ;;; dumpObj(this.getJSONProfile())
    jQuery.ajax(
       {
          async: async,
    jQuery.ajax(
       {
          async: async,
@@ -791,12 +465,15 @@ Client.prototype.flush = function(async)
          success:
             function(data)
             {
          success:
             function(data)
             {
-               ;;; dumpObj(data)
                if (data["reply"] == "error")
                {
                   thisClient.util.messageDialogue(data["error_message"])
                   ok = false
                }
                if (data["reply"] == "error")
                {
                   thisClient.util.messageDialogue(data["error_message"])
                   ok = false
                }
+               else
+               {
+                  thisClient.majBulle()
+               }
             }
       }
    )
             }
       }
    )
@@ -806,33 +483,65 @@ Client.prototype.flush = function(async)
 
 Client.prototype.majMenu = function()
 {
 
 Client.prototype.majMenu = function()
 {
-   // TODO : à virer : ne plus changer de style de display ... spa beau .. ou trouver une autre méthode
-   // var displayType = this.css == "css/3/euphorik.css" ? "block" : "inline" //this.client
-   displayType = "block"
+   var displayType = "block"
 
    $("#menu .admin").css("display", this.ekMaster ? displayType : "none")
 
    // met à jour le menu   
    if (this.statut == statutType.auth_registered)
    {
 
    $("#menu .admin").css("display", this.ekMaster ? displayType : "none")
 
    // met à jour le menu   
    if (this.statut == statutType.auth_registered)
    {
-      $("#menu .profile").css("display", displayType).text("profile")\r
+      $("#menu .profile").css("display", displayType).text("profile")
       $("#menu .logout").css("display", displayType)
       $("#menu .register").css("display", "none")
    }
    else if (this.statut == statutType.auth_not_registered)
    {
       $("#menu .logout").css("display", displayType)
       $("#menu .register").css("display", "none")
    }
    else if (this.statut == statutType.auth_not_registered)
    {
-      $("#menu .profile").css("display", "none")\r
+      $("#menu .profile").css("display", "none")
       $("#menu .logout").css("display", displayType)
       $("#menu .register").css("display", displayType)
    }
    else
    {
       $("#menu .logout").css("display", displayType)
       $("#menu .register").css("display", displayType)
    }
    else
    {
-      $("#menu .profile").css("display", displayType).text("login")\r
+      $("#menu .profile").css("display", displayType).text("login")
       $("#menu .logout").css("display", "none")
       $("#menu .register").css("display", displayType)
    }
 }
 
       $("#menu .logout").css("display", "none")
       $("#menu .register").css("display", displayType)
    }
 }
 
+/**
+  * Met à jour l'affichage des infos bulles en fonction du profile.
+  */
+Client.prototype.majBulle = function()
+{
+   this.util.bulleActive = this.viewTooltips
+}
+
+/**
+  * Met à jour la css sélectionnée, lors du chargement des données.
+  */
+Client.prototype.majCssSelectionee = function()
+{
+   // extraction du numéro de la css courante
+   var numCssCourante = this.css.match(/^.*?\/(\d)\/.*$/)
+   if (numCssCourante != null && numCssCourante[1] != undefined)
+   {
+      $("#menuCss option").removeAttr("selected")
+      $("#menuCss option[value=" + numCssCourante[1]+ "]").attr("selected", "selected")
+   }
+}
+
+/**
+  * Change la "class" du logo en fonction du statut de ekMaster.
+  */
+Client.prototype.majLogo = function()
+{
+   if (this.ekMaster)
+      $("#logo").addClass("ekMaster")
+   else
+      $("#logo").removeClass("ekMaster")   
+}
+
+
 Client.prototype.slap = function(userId, raison)
 {
    var thisClient = this
 Client.prototype.slap = function(userId, raison)
 {
    var thisClient = this
@@ -843,7 +552,7 @@ Client.prototype.slap = function(userId, raison)
       dataType: "json",
       data: this.util.jsonVersAction(
          {
       dataType: "json",
       data: this.util.jsonVersAction(
          {
-            "action" : "slap",
+            "header" : { "action" : "slap", "version" : euphorik.conf.versionProtocole },
             "cookie" : thisClient.cookie,
             "user_id" : userId,
             "reason" : raison
             "cookie" : thisClient.cookie,
             "user_id" : userId,
             "reason" : raison
@@ -863,7 +572,7 @@ Client.prototype.ban = function(userId, raison, minutes)
 
    // par défaut un ban correspond à 3 jours
    if (typeof(minutes) == "undefined")
 
    // par défaut un ban correspond à 3 jours
    if (typeof(minutes) == "undefined")
-      minutes = 60 * 24 * 3
+      minutes = euphorik.conf.tempsBan;
       
    jQuery.ajax({
       type: "POST",
       
    jQuery.ajax({
       type: "POST",
@@ -871,7 +580,7 @@ Client.prototype.ban = function(userId, raison, minutes)
       dataType: "json",
       data: this.util.jsonVersAction(
          {
       dataType: "json",
       data: this.util.jsonVersAction(
          {
-            "action" : "ban",
+            "header" : { "action" : "ban", "version" : euphorik.conf.versionProtocole },
             "cookie" : thisClient.cookie,
             "duration" : minutes,
             "user_id" : userId,
             "cookie" : thisClient.cookie,
             "duration" : minutes,
             "user_id" : userId,
@@ -888,13 +597,23 @@ Client.prototype.ban = function(userId, raison, minutes)
 
 Client.prototype.kick = function(userId, raison)
 {
 
 Client.prototype.kick = function(userId, raison)
 {
-   this.ban(userId, raison, 15)
+   this.ban(userId, raison, euphorik.conf.tempsKick)
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /**
    * classe permettant de gérer les événements (push serveur).
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 /**
    * classe permettant de gérer les événements (push serveur).
+   * l'information envoyé est sous la forme :
+   *  {
+   *     "header" : {"action" : "wait_event", "version" : <v> },
+   *     "page" : <page>
+   *     [..]
+   *  }
+   * l'information reçu est sous la forme :
+   *  {
+   *     "reply" : <reply>
+   *  }
    * @page la page
    */
 function PageEvent(page, util)
    * @page la page
    */
 function PageEvent(page, util)
@@ -904,6 +623,9 @@ function PageEvent(page, util)
    
    // l'objet JSONHttpRequest représentant la connexion d'attente
    this.attenteCourante = null
    
    // l'objet JSONHttpRequest représentant la connexion d'attente
    this.attenteCourante = null
+   
+   // le multhreading du pauvre, merci javascript de m'offrire autant de primitives pour la gestion de la concurrence...
+   this.stop = false
 }
 
 /**
 }
 
 /**
@@ -911,85 +633,90 @@ function PageEvent(page, util)
   */
 PageEvent.prototype.stopAttenteCourante = function()
 {
   */
 PageEvent.prototype.stopAttenteCourante = function()
 {
+   this.stop = true
+         
    if (this.attenteCourante != null)
    if (this.attenteCourante != null)
+   {
       this.attenteCourante.abort()   
       this.attenteCourante.abort()   
+   }
 }
 
 /**
   * Attend un événement lié à la page. 
   * @funSend une fonction renvoyant les données json à envoyer
 }
 
 /**
   * 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)
 {
 {
-   var thisPageEvent = this
-      
    this.stopAttenteCourante()
    this.stopAttenteCourante()
+   
+   this.stop = false
+   
+   var thisPageEvent = this
       
       
-   // on doit conserver l'ordre des valeurs de l'objet JSON (le serveur les veux dans l'ordre définit dans le protocole)
+   // on doit conserver l'ordre des valeurs de l'objet JSON (le serveur les veut dans l'ordre définit dans le protocole)
    // TODO : ya pas mieux ?
    var dataToSend = 
    {
    // TODO : ya pas mieux ?
    var dataToSend = 
    {
-      "action" : "wait_event",
+      "header" : { "action" : "wait_event", "version" : euphorik.conf.versionProtocole },
       "page" : this.page
    }
    var poulpe = funSend()
       "page" : this.page
    }
    var poulpe = funSend()
-   for (v in poulpe)
+   for (var v in poulpe)
       dataToSend[v] = poulpe[v]
    
       dataToSend[v] = poulpe[v]
    
-   ;;; dumpObj(dataToSend)
    this.attenteCourante = jQuery.ajax({
       type: "POST",
       url: "request",
       dataType: "json",
    this.attenteCourante = jQuery.ajax({
       type: "POST",
       url: "request",
       dataType: "json",
+      // TODO : doit disparaitre
+      timeout: 180000, // timeout de 3min. 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)
       data: this.util.jsonVersAction(dataToSend),
       success:
          function(data)
-         {            
-            ;;; dumpObj(data)
-            
-            funReceive(data)
+         {                        
+            funsReceive[data["reply"]](data)
             
             // rappel de la fonction dans 100 ms
             
             // rappel de la fonction dans 100 ms
-            setTimeout(function(){ thisPageEvent.waitEvent(funSend, funReceive) }, 100);
+            setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funsReceive) }, 100)
          },
       error:
          function(XMLHttpRequest, textStatus, errorThrown)
          {
          },
       error:
          function(XMLHttpRequest, textStatus, errorThrown)
          {
-            setTimeout(function(){ thisPageEvent.waitEvent(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, funsReceive)
+{
+   if (this.stop)
+      return
+   this.waitEvent(funSend, funsReceive)
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-function initialiserListeStyles(client)
-{
-   $("#menuCss").change(
-      function()
-      {
-         client.setCss("css/" + $("option:selected", this).attr("value") + "/euphorik.css")
-      }
-   )
-}
-            
-// charge dynamiquement le script de debug
-;;; jQuery.ajax({async : false, url : "js/debug.js", dataType : "script"})
-      \r
+
 // le main
 $(document).ready(
    function()
 // le main
 $(document).ready(
    function()
-   {  
-      var formateur = new Formateur()
-      var util = new Util(formateur)
+   {
+      var formateur = new euphorik.Formateur()
+      var util = new euphorik.Util(formateur)
       var client = new Client(util)
       var client = new Client(util)
-      var pages = new Pages()\r
-      \r
-      // connexion vers le serveur (utilise un cookie qui traine)\r
+      var pages = new euphorik.Pages()
+      
+      // connexion vers le serveur (utilise un cookie qui traine)
       client.connexionCookie()
       
       client.connexionCookie()
       
-      initialiserListeStyles(client)
+      $("#menuCss").change(function(){ client.setCss("styles/" + $("option:selected", this).attr("value") + "/euphorik.css")})
 
       // FIXME : ne fonctionne pas sous opera
       // voir : http://dev.jquery.com/ticket/2892#preview
 
       // FIXME : ne fonctionne pas sous opera
       // voir : http://dev.jquery.com/ticket/2892#preview
@@ -997,9 +724,9 @@ $(document).ready(
       
       $("#menu .minichat").click(function(){ pages.afficherPage("minichat") })
       $("#menu .admin").click(function(){ pages.afficherPage("admin") })
       
       $("#menu .minichat").click(function(){ pages.afficherPage("minichat") })
       $("#menu .admin").click(function(){ pages.afficherPage("admin") })
-      $("#menu .profile").click(function(){ pages.afficherPage("profile") })\r
+      $("#menu .profile").click(function(){ pages.afficherPage("profile") })
       $("#menu .logout").click(function(){
       $("#menu .logout").click(function(){
-         util.messageDialogue("Êtes-vous sur de vouloir vous délogger ?", messageType.question,
+         util.messageDialogue("Êtes-vous sur de vouloir vous délogger ?", euphorik.Util.messageType.question,
             {"Oui" : function()
                {
                   client.deconnexion();
             {"Oui" : function()
                {
                   client.deconnexion();
@@ -1011,12 +738,17 @@ $(document).ready(
       })
       $("#menu .register").click(function(){ pages.afficherPage("register") })
       $("#menu .about").click(function(){ pages.afficherPage("about") })
       })
       $("#menu .register").click(function(){ pages.afficherPage("register") })
       $("#menu .about").click(function(){ pages.afficherPage("about") })
+      
+      // TODO : simplifier et pouvoir créer des liens par exemple : <span class="lien" href="conditions">Conditions d'utilisation</span>
+      $("#footer .conditions").click(function(){ pages.afficherPage("conditions_utilisation") })
 
       pages.ajouterPage(new PageMinichat(client, formateur, util))
       pages.ajouterPage(new PageAdmin(client, formateur, util))
       pages.ajouterPage(new PageProfile(client, formateur, util))
       pages.ajouterPage(new PageRegister(client, formateur, util))
       pages.ajouterPage(new PageAbout(client, formateur, util))
 
       pages.ajouterPage(new PageMinichat(client, formateur, util))
       pages.ajouterPage(new PageAdmin(client, formateur, util))
       pages.ajouterPage(new PageProfile(client, formateur, util))
       pages.ajouterPage(new PageRegister(client, formateur, util))
       pages.ajouterPage(new PageAbout(client, formateur, util))
+      pages.ajouterPage("conditions_utilisation")
+      
       pages.afficherPage("minichat")
    }
 )
       pages.afficherPage("minichat")
    }
 )