MOD avancement dans la Grande Restructuration
[euphorik.git] / js / euphorik.js
index ca84a02..8c13841 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
+// 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); }
 
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-\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
-\r
-///////////////////////////////////////////////////////////////////////////////////////////////////\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) 
-   })
-   
-   $("body").append('<div id="flecheBulle"></div>').append('<div id="messageBulle"><p></p></div>')
-   
-   this.formateur = formateur
-   this.bulleActive = true
-}
 
 
-var messageType = {informatif: 0, question: 1, erreur: 2}
+// 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) } }
+;; } 
 
 
-/**
-  * 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
-
-/**
-  * Affiche un info bulle lorsque le curseur survole l'élément donné.
-  * FIXME : le width de element ne tient pas compte du padding !?
-  */
-Util.prototype.infoBulle = function(message, element)
-{
-   var thisUtil = this
+;; euphorik.include("jquery")
+;; euphorik.include("jquery.lightbox")
+;; euphorik.include("md5")
+;; euphorik.include("json2")
 
 
-   var cacherBulle = function()
-      {   
-         $("#flecheBulle").hide()
-         $("#messageBulle").hide()
-      }
+;; euphorik.include("conf")
+;; euphorik.include("util")\r
+;; euphorik.include("formateur")\r
+;; euphorik.include("pages")
 
 
-   element.hover(
-      function(e)
-      {
-         if (!thisUtil.bulleActive)
-            return
-         
-         var m = $("#messageBulle")
-         var f = $("#flecheBulle")
-         
-         $("p", m).html(message)
-      
-         var positionFleche = {
-            left : element.offset().left + element.width() / 2 - f.width() / 2,
-            top : element.offset().top - f.height()
-         }
-         var positionMessage = {
-            left : element.offset().left + element.width() / 2 - m.width() / 2,
-            top : element.offset().top - f.height() - m.height()
-         }
-         var depassementDroit = (positionMessage.left + m.width()) - $("body").width()
-         if (depassementDroit > 0)
-            positionMessage.left -= depassementDroit
-         else
-         {
-            if (positionMessage.left < 0)
-               positionMessage.left = 0
-         }
-         
-         m.css("top", positionMessage.top).css("left", positionMessage.left).show()
-         f.css("top", positionFleche.top).css("left", positionFleche.left).show()
-      },
-      cacherBulle
-   ).click(cacherBulle)
-}
-
-/**
-  * 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
-}
+;; euphorik.include("pageMinichat")
+;; euphorik.include("pageAdmin")
+;; euphorik.include("pageProfile")
+;; euphorik.include("pageRegister")
+;; euphorik.include("pageAbout")
 \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
 \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
+// tout un tas d'améliorations de JavaScript ;)\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 k in this) {\r
+      if (this.hasOwnProperty(k)) {\r
+         f(k, this[k]);\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
 \r
 \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
+Array.prototype.each = function(f) {\r
+   for (var i = 0; i < this.length; i++) {\r
+      f(i, this[i]);\r
    }\r
    }\r
-   return XHTML\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)))
-}
-
-/**
-  * 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
-}
-
-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)
-}
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-// les statuts possibes du client
-var statutType = {
-   // mode enregistré, peut poster des messages et modifier son profile
-   auth_registered : 0,
-   // mode identifié, peut poster des messages mais n'a pas accès au profile
-   auth_not_registered : 1,
-   // mode déconnecté, ne peut pas poster de message
-   deconnected : 2
-}
-\r
-function Client(util)\r
-{
-   this.util = util
-   \r
-   this.cookie = null
-   this.regexCookie = new RegExp("^cookie=([^;]*)")
-   
-   // données personnels\r
-   this.resetDonneesPersonnelles()
-   
-   this.setStatut(statutType.deconnected)\r
-   \r
-   // si true alors chaque modification du client est mémorisé sur le serveur\r
-   this.autoflush = $.browser["opera"]\r
-}
-\r
-Client.prototype.resetDonneesPersonnelles = function()\r
-{
-   this.id = 0\r
-   this.pseudo = conf.pseudoDefaut\r
-   this.login = ""\r
-   this.password = ""\r
-   this.email = ""\r
-   this.css = $("link#cssPrincipale").attr("href")
-   this.nickFormat = "nick"
-   this.viewTimes = true
-   this.viewTooltips = true
-   this.cookie = undefined
-   
-   this.pagePrincipale = 1
-   this.ekMaster = false
-   
-   // les conversations, une conversation est un objet possédant les attributs suivants :
-   // - racine (entier)
-   // - page (entier)
-   this.conversations = new Array()\r
-}
-
-Client.prototype.setCss = function(css)
-{
-   if (this.css == css || css == "")
-      return
-
-   this.css = css
-   $("link#cssPrincipale").attr("href", this.css)
-   this.majMenu()\r
-   \r
-   if (this.autoflush) this.flush(true)
-}
-
-Client.prototype.pageSuivante = function(numConv)
-{
-   if (numConv < 0 && this.pagePrincipale > 1)
-      this.pagePrincipale -= 1
-   else if (this.conversations[numConv].page > 1)
-      this.conversations[numConv].page -= 1
-}
-
-Client.prototype.pagePrecedente = function(numConv)
-{
-   if (numConv < 0)
-      this.pagePrincipale += 1
-   else 
-      this.conversations[numConv].page += 1
-}
-
-/**
-  * Définit la première page pour la conversation donnée.
-  * @return true si la page a changé sinon false
-  */
-Client.prototype.goPremierePage = function(numConv)
-{
-   if (numConv < 0)
-   {
-      if (this.pagePrincipale == 1)
-         return false
-      this.pagePrincipale = 1
-   }
-   else
-   {
-      if (this.conversations[numConv].page == 1)
-         return false
-      this.conversations[numConv].page = 1
-   }
-   return true
-}
-
-/**
-  * Ajoute une conversation à la vue de l'utilisateur.
-  * Le profile de l'utilisateur est directement sauvegardé sur le serveur.
-  * @param racines la racine de la conversation (integer)
-  * @return true si la conversation a été créée sinon false (par exemple si la conv existe déjà)
-  */
-Client.prototype.ajouterConversation = function(racine)
-{
-   // vérification s'il elle n'existe pas déjà
-   for (var i = 0; i < this.conversations.length; i++)
-      if (this.conversations[i].root == racine)
-         return false
-         
-   this.conversations.push({root : racine, page : 1})\r
-   \r
-   if (this.autoflush) this.flush(true)\r
-   
-   return true
-}
-
-Client.prototype.supprimerConversation = function(num)
-{
-   if (num < 0 || num >= this.conversations.length) return
-   
-   // décalage TODO : supprimer le dernier élément 
-   for (var i = num; i < this.conversations.length - 1; i++)
-      this.conversations[i] = this.conversations[i+1]
-   this.conversations.pop()\r
-   \r
-   if (this.autoflush) this.flush(true)
-}
-
-Client.prototype.getJSONLogin = function(login, password)
-{
-   return {
-      "action" : "authentification",
-      "login" : login,
-      "password" : password
-   }
-}
-
-Client.prototype.getJSONLoginCookie = function()
-{
-   return {
-      "action" : "authentification",
-      "cookie" : this.cookie
-   }
-}  
-
-/**
-  * le couple (login, password) est facultatif. S'il n'est pas fournit alors il ne sera pas possible
-  * de s'autentifier avec (login, password).
-  */
-Client.prototype.getJSONEnregistrement = function(login, password)
-{
-   var mess = { "action" : "register" }
-   
-   if (login != undefined && password != undefined)
-   {
-      mess["login"] = login
-      mess["password"] = password
-   }
-   
-   return mess;
-}
-
-Client.prototype.getJSONConversations = function()
-{
-   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})
-   return conversations
-}
-
-Client.prototype.getJSONProfile = function()
-{
-   return {
-      "action" : "set_profile",
-      "cookie" : this.cookie,
-      "login" : this.login,
-      "password" : this.password,
-      "nick" : this.pseudo,
-      "email" : this.email,
-      "css" : this.css,
-      "nick_format" : this.nickFormat,
-      "view_times" : this.viewTimes,
-      "view_tooltips" : this.viewTooltips,
-      "main_page" : this.pagePrincipale < 1 ? 1 : this.pagePrincipale,
-      "conversations" : this.getJSONConversations()
-   }
-}
-
-/**
-  * Renvoie null si pas définit.
-  */
-Client.prototype.getCookie = function()
-{
-   var cookie = this.regexCookie.exec(document.cookie)
-   if (cookie == null) this.cookie = null
-   else this.cookie = cookie[1]
-}\r
+};\r
 \r
 \r
-Client.prototype.delCookie = function()\r
-{\r
-   document.cookie = "cookie=; max-age=0"\r
-}
-
-Client.prototype.setCookie = function(cookie)
-{
-   if (this.cookie == null)
-      return
-      
-   document.cookie =
-      "cookie="+this.cookie+
-      "; max-age="  + (60 * 60 * 24 * 365)
-}
-
-Client.prototype.authentifie = function()
-{
-   return this.statut == statutType.auth_registered || this.statut == statutType.auth_not_registered
-}
-
-Client.prototype.setStatut = function(statut)
-{  
-   // conversation en "enum" si en "string"\r
-   if (typeof(statut) == "string")\r
-   {
-      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
-   this.majMenu()
-}
-\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
-   if (this.cookie == null) return false;
-   return this.connexion(this.getJSONLoginCookie())\r
-}
-
-Client.prototype.connexionLogin = function(login, password)
-{
-   return this.connexion(this.getJSONLogin(login, password))
-}\r
-
-Client.prototype.enregistrement = function(login, password)
-{ 
-   if (this.authentifie())
-   {
-      this.login = login
-      this.password = password
-      if(this.flush())
-      {
-         this.setStatut(statutType.auth_registered)
-         return true
-      }
-      return false
-   }
-   else\r
-   {
-      return this.connexion(this.getJSONEnregistrement(login, password))\r
-   }
-}
-
-Client.prototype.connexion = function(messageJson)
-{
-   ;;; dumpObj(messageJson)
-   thisClient = this
-   jQuery.ajax(
-      {
-         async: false,
-         type: "POST",
-         url: "request",
-         dataType: "json",
-         data: this.util.jsonVersAction(messageJson),
-         success:
-            function(data)
-            {
-               ;;; dumpObj(data)
-               if (data["reply"] == "error")
-                  thisClient.util.messageDialogue(data["error_message"])
-               else
-                  thisClient.chargerDonnees(data)
-            }
-      }
-   )
-   return this.authentifie()
-}\r
-\r
-Client.prototype.deconnexion = function()\r
-{
-   this.flush(true)
-   this.delCookie()
-   this.resetDonneesPersonnelles()\r
-   this.setStatut(statutType.deconnected) // deconnexion\r
-}
-
-Client.prototype.chargerDonnees = function(data)
-{
-   // la modification du statut qui suit met à jour le menu, le menu dépend (page admin)
-   // de l'état ekMaster
-   this.ekMaster = data["ek_master"] != undefined ? data["ek_master"] : false
-   
-   this.setStatut(data["status"]) 
-   
-   if (this.authentifie())
-   {
-      this.cookie = data["cookie"]
-      this.setCookie()
-      
-      this.id = data["id"]\r
-      this.login = data["login"]
-      this.pseudo = data["nick"]\r
-      this.email = data["email"]\r
-      this.setCss(data["css"])
-      this.nickFormat = data["nick_format"]
-      this.viewTimes = data["view_times"]
-      this.viewTooltips = data["view_tooltips"]
-      
-      // la page de la conversation principale
-      this.pagePrincipale = data["main_page"] == undefined ? 1 : data["main_page"]
-      
-      // les conversations
-      this.conversations = data["conversations"]
-      
-      this.majBulle()
-   }
-}
-
-/**
-  * Met à jour les données personne sur serveur.
-  * @param async de manière asynchrone ? défaut = true
-  * @return false si le flush n'a pas pû se faire sinon true
-  */
-Client.prototype.flush = function(async)
-{
-   if (async == undefined)
-      async = false
-      
-   if (!this.authentifie())
-      return false
-
-   var thisClient = this
-   var ok = true
-   
-   ;;; dumpObj(this.getJSONProfile())
-   jQuery.ajax(
-      {
-         async: async,
-         type: "POST",
-         url: "request",
-         dataType: "json",
-         data: this.util.jsonVersAction(this.getJSONProfile()),
-         success:
-            function(data)
-            {
-               ;;; dumpObj(data)
-               if (data["reply"] == "error")
-               {
-                  thisClient.util.messageDialogue(data["error_message"])
-                  ok = false
-               }
-               else
-               {
-                  thisClient.majBulle()
-               }
-            }
-      }
-   )
-   
-   return ok
-}
-
-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"
-
-   $("#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 .logout").css("display", displayType)
-      $("#menu .register").css("display", "none")
-   }
-   else if (this.statut == statutType.auth_not_registered)
-   {
-      $("#menu .profile").css("display", "none")\r
-      $("#menu .logout").css("display", displayType)
-      $("#menu .register").css("display", displayType)
-   }
-   else
-   {
-      $("#menu .profile").css("display", displayType).text("login")\r
-      $("#menu .logout").css("display", "none")
-      $("#menu .register").css("display", displayType)
-   }
-}
+Array.prototype.map = function(f) {\r
+   for (var i = 0; i < this.length; i++) {\r
+      this[i] = f(this[i])\r
+   }\r
+};\r
 
 
-/**
-  * Met à jour l'affichage des infos bulles en fonction du profile.
-  */
-Client.prototype.majBulle = function()
-{
-   this.util.bulleActive = this.viewTooltips
+String.prototype.trim = function() {
+       return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");
 }
 
 }
 
-Client.prototype.slap = function(userId, raison)
-{
-   var thisClient = this
-   
-   jQuery.ajax({
-      type: "POST",
-      url: "request",
-      dataType: "json",
-      data: this.util.jsonVersAction(
-         {
-            "action" : "slap",
-            "cookie" : thisClient.cookie,
-            "user_id" : userId,
-            "reason" : raison
-         }),
-      success: 
-         function(data)
-         {
-            if (data["reply"] == "error")
-               thisClient.util.messageDialogue(data["error_message"])
-         }
-   })
+String.prototype.ltrim = function() {
+       return this.replace(/^\s+/, "");
 }
 
 }
 
-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
-      
-   jQuery.ajax({
-      type: "POST",
-      url: "request",
-      dataType: "json",
-      data: this.util.jsonVersAction(
-         {
-            "action" : "ban",
-            "cookie" : thisClient.cookie,
-            "duration" : minutes,
-            "user_id" : userId,
-            "reason" : raison
-         }),
-      success: 
-         function(data)
-         {
-            if (data["reply"] == "error")
-               thisClient.util.messageDialogue(data["error_message"])
-         }
-   })
+String.prototype.rtrim = function() {
+       return this.replace(/\s+$/, "");
 }
 
 }
 
-Client.prototype.kick = function(userId, raison)
-{
-   this.ban(userId, raison, 15)
-}
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-/**
-   * classe permettant de gérer les événements (push serveur).
-   * @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()
-{
-   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
-  * @funReceive une fonction qui accepte un paramètre correspondant au données reçues
-  */
-PageEvent.prototype.waitEvent = function(funSend, funReceive)
-{
-   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 = 
-   {
-      "action" : "wait_event",
-      "page" : this.page
-   }
-   var poulpe = funSend()
-   for (v in poulpe)
-      dataToSend[v] = poulpe[v]
-   
-   ;;; dumpObj(dataToSend)
-   
-   this.attenteCourante = jQuery.ajax({
-      type: "POST",
-      url: "request",
-      dataType: "json",
-      data: this.util.jsonVersAction(dataToSend),
-      success:
-         function(data)
-         {            
-            ;;; dumpObj(data)
-            
-            funReceive(data)
-            
-            // rappel de la fonction dans 100 ms
-            setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funReceive) }, 100)
-         },
-      error:
-         function(XMLHttpRequest, textStatus, errorThrown)
-         {
-            setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funReceive) }, 1000)
-         }
-   })
-}
-
-/**
-  * Si un stopAttenteCourante survient un peu n'importe quand il faut imédiatement arreter de boucler.
-  */
-PageEvent.prototype.waitEvent2 = function(funSend, funReceive)
-{
-   if (this.stop)
-      return
-   this.waitEvent(funSend, funReceive)
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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
@@ -1098,9 +120,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();
@@ -1112,12 +134,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")
    }
 )