MOD avancement dans la Grande Restructuration
[euphorik.git] / js / euphorik.js
index 929a35c..b93498f 100755 (executable)
-// coding: utf-8\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 : 10, // (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
-\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
+// 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
-///////////////////////////////////////////////////////////////////////////////////////////////////\r
 \r
 \r
+// tout un tas d'améliorations des objets javascript ;)\r
 /**\r
 /**\r
-  * Cette classe regroupe des fonctions utilitaires (helpers).\r
-  */
-function Util()
-{
-   $("#info .fermer").click(function(){
-      $("#info").slideUp(50) 
-   })
-}
-
-/**
-  * 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é.
-  */
-Util.prototype.messageDialogue = function(message, type, boutons)
-{
-   if (type == undefined)
-      type = messageType.informatif
-
-   if (this.timeoutMessageDialogue != undefined)
-      clearTimeout(this.timeoutMessageDialogue)
-      
-   var fermer = function(){$("#info").slideUp(100)}
-   fermer()   
-   
-   $("#info .message").html(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)   
-}
-
-var messageType = {informatif: 0, question: 1, erreur: 2}\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 div").removeClass("courante")
-   $("#menu div." + nomPage).addClass("courante")
-      
-   this.pageCourante = page
-   $("#page").html(this.pageCourante.contenu()).removeClass().addClass(this.pageCourante.nom)
-   
-   if (this.pageCourante.charger)
-      this.pageCourante.charger()
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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\" />"\r
-   }\r
-   return XHTML\r
 }\r
 
 }\r
 
-Formateur.prototype.traitementComplet = function(M, pseudo)
-{
-   return this.traiterLiensConv(this.traiterSmiles(this.traiterURL(this.remplacerBalisesHTML(M), pseudo)))
-}
-
-/**
-  * 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 su é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\" />")
-   }
-   return M
+String.prototype.trim = function() {
+       return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");
 }
 
 }
 
-Formateur.prototype.remplacerBalisesHTML = function(M)
-{
-   return M.replace(/</g, "&lt;").replace(/>/g, "&gt;")
+String.prototype.ltrim = function() {
+       return this.replace(/^\s+/, "");
 }
 
 }
 
-Formateur.prototype.traiterURL = function(M, pseudo)
-{
-   thisFormateur = this
-   
-   if (pseudo == undefined)
-      pseudo = ""
-         
-   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=\"" + thisFormateur.traiterPourFenetreLightBox(pseudo, url) + ": " +  thisFormateur.traiterPourFenetreLightBox(M, url) + "\"" + " rel=\"lightbox\"" : "") + " href=\"" + url + "\" >[" + extension[0] + "]</a>"
-   }
-   return M.replace(this.regexUrl, traitementUrl)
-}
-\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)\r
-      //alert(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 ? ": image courante" : "") + "]"
-   }
-   \r
-   return this.remplacerBalisesHTML(M).replace(this.regexUrl, traitementUrl)
+String.prototype.rtrim = function() {
+       return this.replace(/\s+$/, "");
 }
 
 
 }
 
 
@@ -400,39 +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.setStatut(statutType.deconnected)
    
    this.resetDonneesPersonnelles()
    
    this.setStatut(statutType.deconnected)
    
-   // le dernier message d'erreur recut du serveur (par exemple une connexion foireuse : "login impossible")
-   this.dernierMessageErreur = ""\r
+   // si true alors chaque modification du client est mémorisé sur le serveur
+   this.autoflush = $.browser["opera"]
 }
 }
-\r
-Client.prototype.resetDonneesPersonnelles = function()\r
-{\r
-   this.pseudo = conf.pseudoDefaut\r
-   this.login = ""\r
-   this.password = ""\r
-   this.email = ""\r
+
+Client.prototype.resetDonneesPersonnelles = function()
+{
+   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.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)
@@ -442,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)
@@ -494,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
 }
 
@@ -507,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
    }
@@ -521,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
    }
 }  
@@ -532,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)
    {
@@ -547,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
    }
 }
 
    }
 }
 
@@ -575,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()
 {
 {
-   if (this.cookie == null)
+   document.cookie = "cookie=; max-age=0"
+}
+
+Client.prototype.setCookie = function()
+{
+   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()
@@ -599,39 +311,39 @@ Client.prototype.authentifie = function()
 
 Client.prototype.setStatut = function(statut)
 {  
 
 Client.prototype.setStatut = function(statut)
 {  
-   //alert(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)
 { 
@@ -640,19 +352,24 @@ Client.prototype.enregistrement = function(login, password)
       this.login = login
       this.password = password
       if(this.flush())
       this.login = login
       this.password = password
       if(this.flush())
+      {
          this.setStatut(statutType.auth_registered)
          this.setStatut(statutType.auth_registered)
-      return true
+         return true
+      }
+      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,
@@ -663,20 +380,26 @@ Client.prototype.connexion = function(messageJson)
          success:
             function(data)
             {
          success:
             function(data)
             {
-               ;;; dumpObj(data)
-               thisClient.chargerDonnees(data)
+               if (data["reply"] == "error")
+               {
+                  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()
             }
       }
    )
    return this.authentifie()
-}\r
-\r
-Client.prototype.deconnexion = function()\r
+}
+
+Client.prototype.deconnexion = function()
 {
 {
-   this.flush()
-   this.delCookie()\r
-   this.setStatut(statutType.deconnected) // deconnexion\r
-   this.resetDonneesPersonnelles()\r
+   this.flush(true)
+   this.delCookie()
+   this.resetDonneesPersonnelles()
+   this.setStatut(statutType.deconnected) // deconnexion
 }
 
 Client.prototype.chargerDonnees = function(data)
 }
 
 Client.prototype.chargerDonnees = function(data)
@@ -691,20 +414,30 @@ Client.prototype.chargerDonnees = function(data)
    {
       this.cookie = data["cookie"]
       this.setCookie()
    {
       this.cookie = data["cookie"]
       this.setCookie()
-      \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()
    }
    }
-   this.dernierMessageErreur = data["error_message"]
 }
 
 /**
 }
 
 /**
@@ -715,13 +448,13 @@ Client.prototype.chargerDonnees = function(data)
 Client.prototype.flush = function(async)
 {
    if (async == undefined)
 Client.prototype.flush = function(async)
 {
    if (async == undefined)
-      async = true
+      async = false
       
    if (!this.authentifie())
       return false
 
       
    if (!this.authentifie())
       return false
 
-   thisClient = this
-   ;;; dumpObj(this.getJSONProfile())
+   var thisClient = this
+   var ok = true
    jQuery.ajax(
       {
          async: async,
    jQuery.ajax(
       {
          async: async,
@@ -732,43 +465,82 @@ Client.prototype.flush = function(async)
          success:
             function(data)
             {
          success:
             function(data)
             {
-               //thisClient.util.log(thisClient.util.serializer.serializeToString(data))   
+               if (data["reply"] == "error")
+               {
+                  thisClient.util.messageDialogue(data["error_message"])
+                  ok = false
+               }
+               else
+               {
+                  thisClient.majBulle()
+               }
             }
       }
    )
             }
       }
    )
-   // TODO : retourner false si un problème est survenu lors de l'update du profile
-   return true
+   
+   return ok
 }
 
 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
+   var displayType = "block"
 
 
-   alert(this.ekMaster)
-   $("#menu .admin").css("display", this.ekMaster ? "inline" : "none")
+   $("#menu .admin").css("display", this.ekMaster ? displayType : "none")
 
    // met à jour le menu   
    if (this.statut == statutType.auth_registered)
    {
 
    // 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)
 {
 
 Client.prototype.slap = function(userId, raison)
 {
@@ -780,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
@@ -794,14 +566,13 @@ Client.prototype.slap = function(userId, raison)
    })
 }
 
    })
 }
 
-
 Client.prototype.ban = function(userId, raison, minutes)
 {
    var thisClient = this
 
    // par défaut un ban correspond à 3 jours
    if (typeof(minutes) == "undefined")
 Client.prototype.ban = function(userId, raison, minutes)
 {
    var thisClient = this
 
    // 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",
@@ -809,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,
@@ -826,46 +597,136 @@ 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)
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-function initialiserListeStyles(client)
+/**
+   * 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)
+{
+   this.page = page
+   this.util = util
+   
+   // 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
+}
+
+/**
+  * Arrête l'attente courante s'il y en a une.
+  */
+PageEvent.prototype.stopAttenteCourante = function()
 {
 {
-   $("#menuCss").change(
-      function()
-      {
-         client.setCss("css/" + $("option:selected", this).attr("value") + "/euphorik.css")
-      }
-   )
+   this.stop = true
+         
+   if (this.attenteCourante != null)
+   {
+      this.attenteCourante.abort()   
+   }
 }
 }
+
+/**
+  * Attend un événement lié à la page. 
+  * @funSend une fonction renvoyant les données json à envoyer
+  * @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, funsReceive)
+{
+   this.stopAttenteCourante()
+   
+   this.stop = false
+   
+   var thisPageEvent = this
+      
+   // 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 = 
+   {
+      "header" : { "action" : "wait_event", "version" : euphorik.conf.versionProtocole },
+      "page" : this.page
+   }
+   var poulpe = funSend()
+   for (var v in poulpe)
+      dataToSend[v] = poulpe[v]
+   
+   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)
+         {                        
+            funsReceive[data["reply"]](data)
             
             
-// charge dynamiquement le script de debug
-;;; jQuery.ajax({async : false, url : "js/debug.js", dataType : "script"})
-      \r
+            // rappel de la fonction dans 100 ms
+            setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funsReceive) }, 100)
+         },
+      error:
+         function(XMLHttpRequest, textStatus, errorThrown)
+         {
+            ;; 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)
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+
 // le main
 $(document).ready(
    function()
 // le main
 $(document).ready(
    function()
-   {  
-      var util = new Util()
+   {
+      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()
-      var formateur = new Formateur()\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")})
 
 
-      // TODO : pourquoi $(document).unload ne fonctionne pas ?
-      $(window).unload(function(){client.flush(false)})
+      // FIXME : ne fonctionne pas sous opera
+      // voir : http://dev.jquery.com/ticket/2892#preview
+      $(window).unload(function(){client.flush()})
       
       $("#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();
@@ -877,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")
    }
-)
\ No newline at end of file
+)