(no commit message)
[euphorik.git] / js / euphorik.js
index f78e103..afe0251 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
   */
 
+// 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); 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("comet");
+
+;; euphorik.include("conf");
+;; euphorik.include("util");
+;; euphorik.include("formateur");
+;; euphorik.include("pages");
+;; euphorik.include("client");
+
+;; euphorik.include("pageMinichat/pageMinichat");\r
+;; euphorik.include("pageMinichat/conversation");\r
+;; euphorik.include("pageMinichat/conversations");\r
+;; euphorik.include("pageMinichat/message");\r
+;; euphorik.include("pageMinichat/commandes");
+;; euphorik.include("pageAdmin");
+;; euphorik.include("pageProfile");
+;; euphorik.include("pageRegister");
+;; euphorik.include("pageAbout");
+
+// tout un tas d'améliorations de JavaScript ;)
 /**
-  * 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],\r
-      "oh" : [/:o/g, /:O/g],\r
-      "pascontent" : [/>\(/g, /&gt;\(/g],\r
-      "sniff" : [/:\(/g, /:-\(/g],\r
-      "argn" : [/\[:argn\]/g],\r
-      "bunny" : [/\[:lapin\]/g],\r
-      "chat" : [/\[:chat\]/g],\r
-      "renne" : [/\[:renne\]/g],\r
-      "lol" : [/\[:lol\]/g],\r
-      "spliff" : [/\[:spliff\]/g],\r
-      "star" : [/\[:star\]/g],\r
-      "triste" : [/\[:triste\]/g],\r
-      "kirby" : [/\[:kirby\]/g]\r
-   }\r
-}\r
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-\r
-String.prototype.trim = function()\r
-{\r
-       return 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
-String.prototype.dump = function(titre)\r
-{
-   titre = titre == undefined ? "" : titre\r
-   if (typeof dump != "undefined")\r
-   {\r
-      dump("\n--- EUPHORIK.CH : " + titre + " ---\n")\r
-      dump(this)\r
-      dump("\n------\n")\r
-   }\r
-}\r
-\r
-///////////////////////////////////////////////////////////////////////////////////////////////////\r
-\r
-/**\r
-  * Cette classe regroupe des fonctions utilitaires (helpers).\r
-  */
-function Util()
-{\r
-   if(typeof XMLSerializer != "undefined")
-      this.serializer = new XMLSerializer()
-      
-   jQuery("#info .fermer").click(function(){
-      jQuery("#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é.
+  * Pour chaque propriété de l'objet execute f(p, v) ou p est le nom de la propriété et v sa valeur.
+  * Ne parcours pas les propriétés des prototypes.
+  * FIXME : Normalement : Object.prototype.each = function(f) mais non supporté par jquery
   */
-Util.prototype.messageDialogue = function(message, type, boutons)
-{
-   if (type == undefined)
-      type = messageType.informatif
-
-   if (this.timeoutMessageDialogue != undefined)
-      clearTimeout(this.timeoutMessageDialogue)
-      
-   var fermer = function(){jQuery("#info").slideUp(100)}
-   fermer()   
-   
-   jQuery("#info .message").html(message)
-   switch(type)
-   {
-      case messageType.informatif : jQuery("#info #icone").attr("class", "information"); break
-      case messageType.question : jQuery("#info #icone").attr("class", "interrogation"); break
-      case messageType.erreur : jQuery("#info #icone").attr("class", "exclamation"); break
-   }   
-   jQuery("#info .boutons").html("")
-   for (var b in boutons)
-      jQuery("#info .boutons").append("<div>" + b + "</div>").find("div:last").click(boutons[b]).click(fermer)
-   
-   jQuery("#info").slideDown(200)
-   this.timeoutMessageDialogue = setTimeout(fermer, conf.tempsAffichageMessageDialogue)   
-}
-var messageType = {informatif: 0, question: 1, erreur: 2}
-\r
-/**\r
-  * Transforme un document XML en string.\r
-  */\r
-Util.prototype.serializeXML = function(documentXML)\r
-{\r
-   if (this.serializer)\r
-      return this.serializer.serializeToString(documentXML)\r
-   else\r
-      return documentXML.xml\r
-}\r
-
-var documentXMLBase = undefined // singleton 
-Util.prototype.creerDocumentXMLAction = function()
-{
-   // FIXME : essayer de garder le doc de base en cache (singleton) et d'en retourner une copie
-   if (true)//documentXMLBase == undefined)
-   {\r
-      if (document.implementation && document.implementation.createDocument)\r
-      {\r
-         // var doc = document.implementation.createDocument("", "action", null)
-         var parser = new DOMParser();
-         documentXMLBase =  parser.parseFromString("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<action/>", "text/xml")
-         //alert(this.serializeXML(doc))\r
-      }\r
-      else if (window.ActiveXObject)\r
-      {\r
-         documentXMLBase = new ActiveXObject("MSXML2.DOMDocument") //("Microsoft.XMLDOM")\r
-         documentXMLBase.appendChild(doc.createElement("action"));\r
-         //doc.loadXML("<action></action>")\r
-         //alert(doc.documentElement)\r
-         //doc.createElement("action")\r
+//Object.prototype.each = function(f) {
+var objectEach = function(o, f) {
+   for (var k in o) {
+      if (o.hasOwnProperty(k)) {
+         f(k, o[k]);
       }
    }
-   return documentXMLBase
-}
-
-Util.prototype.xmlVersAction = function(xml)
-{
-   //return {action: this.to_utf8(this.serializeXML(xml /*, "UTF-8"*/))}
-   return {action: this.serializeXML(xml)}
-}
-
-/**
-  * Utilisé pour l'envoie de donnée avec la méthode ajax de jQuery.
-  */
-Util.prototype.jsonVersAction = function(json)
-{
-   // FIXME : ne plus encapsuler json dans de l'xml (problème avec yaws)
-   return {action: "<json>" + JSON.stringify(json) + "</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
+var objectMemberCount = function(o) {\r
+   var nb = 0;\r
+   for (var k in o) {\r
+      if (o.hasOwnProperty(k)) {\r
+         nb += 1;\r
       }\r
    }\r
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-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()
-  
-   jQuery("#menu div").removeClass("courante")
-   jQuery("#menu div." + nomPage).addClass("courante")
-      
-   this.pageCourante = page
-   jQuery("#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
-
-Formateur.prototype.traitementComplet = function(M, pseudo)
-{
-   return this.traiterLiensConv(this.traiterSmiles(this.traiterURL(this.remplacerBalisesHTML(M), pseudo)))
-}
+   return nb;\r
+};\r
 
-/**
-  * 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.\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\" />")
+Array.prototype.each = function(f) {
+   for (var i = 0; i < this.length; i++) {
+      f(i, this[i]);
    }
-   return M
-}
+};
 
-Formateur.prototype.remplacerBalisesHTML = function(M)
-{
-   return M.replace(/</g, "&lt;").replace(/>/g, "&gt;")
-}
-
-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[groupe]\"" : "") + " 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" : "") + "]"
+Array.prototype.map = function(f) {
+   for (var i = 0; i < this.length; i++) {
+      this[i] = f(this[i])
    }
-   \r
-   return this.remplacerBalisesHTML(M).replace(this.regexUrl, traitementUrl)
-}
-
+};
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-var statutType = {enregistre: 0, identifie: 1, non_identifie: 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.non_identifie)
-   
-   // le dernier message d'erreur recut du serveur (par exemple une connexion foireuse : "login impossible")
-   this.dernierMessageErreur = ""\r
+String.prototype.trim = function() {
+       return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");
 }
-\r
-Client.prototype.resetDonneesPersonnelles = function()\r
-{\r
-   this.pseudo = conf.pseudoDefaut\r
-   this.login = ""\r
-   this.password = ""\r
-   this.email = ""\r
-   this.css = jQuery("link#cssPrincipale").attr("href")
-   
-   this.pagePrincipale = 1
-   
-   // 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)
-      return
 
-   this.css = css
-   jQuery("link#cssPrincipale").attr("href", this.css)
-   this.majMenu()
-
-   if (this.identifie())
-      this.flush()   
+String.prototype.ltrim = function() {
+       return this.replace(/^\s+/, "");
 }
 
-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
-   this.flush(false)
+String.prototype.rtrim = function() {
+       return this.replace(/\s+$/, "");
 }
 
-Client.prototype.pagePrecedente = function(numConv)
-{
-   if (numConv < 0)
-      this.pagePrincipale += 1
-   else 
-      this.conversations[numConv].page += 1
-   this.flush(false)
-}
 
 /**
-  * Définit la première page pour la conversation donnée.
-  * @return true si la page a changé sinon false
+  * Voir : http://www.coolpage.com/developer/javascript/Correct%20OOP%20for%20Javascript.html
+  *
+  * Exemple :  
+  *
+  * function Mammal(name) {
+  *    this.name = name;
+  * }
+  *
+  * Cat.Inherits(Mammal);
+  * function Cat(name) {
+  *    this.Super(Mammal, name);
+  * }
   */
-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
+/*Object.prototype.Super = function(parent) {
+   if(arguments.length > 1) {
+      parent.apply( this, Array.prototype.slice.call( arguments, 1 ) );
+   } else {
+      parent.call( this );
    }
-   this.flush(false)
-   return true
 }
+Function.prototype.Inherits = function(parent) {
+   this.prototype = new parent();
+   this.prototype.constructor = this;
+}*/
 
-/**
-  * 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
-  * @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].racine == racine)
-         return false
-         
-   this.conversations.push({racine : racine, page : 1})
-   this.flush(false)
-   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()
-   
-   this.flush(false)
-}\r
-
-Client.prototype.getXMLlogin = function(login, password)
-{
-   var XMLDocument = this.util.creerDocumentXMLAction()
-   XMLDocument.documentElement.setAttribute("name", "login")
-   
-   var nodeLogin = XMLDocument.createElement("login")
-   nodeLogin.appendChild(XMLDocument.createTextNode(login))
-   XMLDocument.documentElement.appendChild(nodeLogin)
-   
-   var nodePassword = XMLDocument.createElement("password")
-   nodePassword.appendChild(XMLDocument.createTextNode(password))
-   XMLDocument.documentElement.appendChild(nodePassword)
-   
-   return XMLDocument   
-}
-
-Client.prototype.getJSONLogin = function(login, password)
-{
-   return {
-      "action" : "authentification",
-      "login" : login,
-      "password" : password
-   }
-}
 
-Client.prototype.getXMLloginCookie = function()
-{
-   var XMLDocument = this.util.creerDocumentXMLAction()
-   XMLDocument.documentElement.setAttribute("name", "login")
-   
-   var nodeCookie = XMLDocument.createElement("cookie")
-   nodeCookie.appendChild(XMLDocument.createTextNode(this.cookie))
-   XMLDocument.documentElement.appendChild(nodeCookie)
-   
-   return XMLDocument
-}
-
-Client.prototype.getXMLEnregistrement = function(login, password)
-{
-   var XMLDocument = this.util.creerDocumentXMLAction()
-   XMLDocument.documentElement.setAttribute("name", "register")
-   
-   var nodeLogin = XMLDocument.createElement("login")
-   nodeLogin.appendChild(XMLDocument.createTextNode(login))
-   XMLDocument.documentElement.appendChild(nodeLogin)
-   
-   var nodePassword = XMLDocument.createElement("password")
-   nodePassword.appendChild(XMLDocument.createTextNode(password))
-   XMLDocument.documentElement.appendChild(nodePassword)
-   
-   return XMLDocument   
-}
-
-/**
-  * Sérialize le profile en XML.
-  * TODO : méthode assez lourde, 3.25ms de moyenne
-  */
-Client.prototype.getXMLProfile = function()
-{
-   var XMLDocument = this.util.creerDocumentXMLAction()
-   XMLDocument.documentElement.setAttribute("name", "profile")
-   
-   var nodeCookie = XMLDocument.createElement("cookie")
-   nodeCookie.appendChild(XMLDocument.createTextNode(this.cookie))
-   XMLDocument.documentElement.appendChild(nodeCookie)
-   
-   var nodeLogin = XMLDocument.createElement("login")
-   nodeLogin.appendChild(XMLDocument.createTextNode(this.login))
-   XMLDocument.documentElement.appendChild(nodeLogin)
-   
-   var nodePassword = XMLDocument.createElement("password")
-   nodePassword.appendChild(XMLDocument.createTextNode(this.password))
-   XMLDocument.documentElement.appendChild(nodePassword)
-   
-   var nodePseudo = XMLDocument.createElement("pseudo")
-   nodePseudo.appendChild(XMLDocument.createTextNode(this.pseudo))
-   XMLDocument.documentElement.appendChild(nodePseudo)
-   
-   var nodeEmail = XMLDocument.createElement("email")
-   nodeEmail.appendChild(XMLDocument.createTextNode(this.email))
-   XMLDocument.documentElement.appendChild(nodeEmail)
-   
-   var nodeCSS = XMLDocument.createElement("css")
-   nodeCSS.appendChild(XMLDocument.createTextNode(this.css))
-   XMLDocument.documentElement.appendChild(nodeCSS)
-   
-   var nodePagePrincipale = XMLDocument.createElement("pagePrincipale")
-   nodePagePrincipale.appendChild(XMLDocument.createTextNode(this.pagePrincipale < 1 ? 1 : this.pagePrincipale))
-   XMLDocument.documentElement.appendChild(nodePagePrincipale)
-   
-   // mémorise les conversations affichées
-   for (var i = 0; i < this.conversations.length; i++)
-   {
-      var nodeConv = XMLDocument.createElement("conversation")
-      XMLDocument.documentElement.appendChild(nodeConv)
-      
-      var nodeRacine = XMLDocument.createElement("racine")
-      nodeRacine.appendChild(XMLDocument.createTextNode(this.conversations[i].racine))
-      nodeConv.appendChild(nodeRacine)
-      
-      var nodePage = XMLDocument.createElement("page")
-      nodePage.appendChild(XMLDocument.createTextNode(this.conversations[i].page))
-      nodeConv.appendChild(nodePage)
-   }
-   
-   return XMLDocument    
-}
-
-/**
-  * 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
-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.identifie = function()
-{
-   return this.statut == statutType.enregistre || this.statut == statutType.identifie
-}
-
-Client.prototype.setStatut = function(statut)
-{  \r
-   if(typeof(statut) == "string")\r
-   {
-      statut =
-         statut == "enregistre" ?
-            statutType.enregistre : (statut == "identifie" ? statutType.identifie : statutType.non_identifie)   \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.util.xmlVersAction(this.getXMLloginCookie()))\r
-}
-
-Client.prototype.connexionLogin = function(login, password)
-{
-   // return this.connexion(this.util.xmlVersAction(this.getXMLlogin(login, password)))
-   return this.connexion(this.util.jsonVersAction(this.getJSONLogin(login, password)))
-}\r
-
-Client.prototype.enregistrement = function(login, password)
-{ 
-   if (this.identifie())
-   {
-      this.login = login
-      this.password = password
-      if(this.flush())
-         this.setStatut(statutType.enregistre)
-      return true
-   }
-   else\r
-   {\r
-      if (login == undefined) login = ""\r
-      if (password == undefined) password = ""
-      return this.connexion(this.util.xmlVersAction(this.getXMLEnregistrement(login, password)))\r
-   }
-}
-
-Client.prototype.connexion = function(action)
-{
-   action.action.dump("Connexion client")
-   thisClient = this
-   jQuery.ajax(
-      {
-         async: false,
-         type: "POST",
-         url: "request",
-         dataType: "json",
-         data: action,
-         success:
-            function(data)
-            {
-               //alert(data["error_message"])
-               //thisClient.util.serializer.serializeToString(data).dump("Charger client")
-               //thisClient.chargerDonnees(data)
-            }
-      }
-   )
-   return this.identifie()
-}\r
-\r
-Client.prototype.deconnexion = function()\r
-{\r
-   this.setStatut(statutType.non_identifie) // deconnexion\r
-   this.resetDonneesPersonnelles()\r
-   this.delCookie ()\r
-}
-
-Client.prototype.chargerDonnees = function(data)
-{
-   var thisClient = this
-
-   this.setStatut(jQuery("statut", data.documentElement).text())       
-   
-   if (this.identifie())
-   {
-      this.cookie = jQuery("cookie", data.documentElement).text()
-      this.setCookie()
-      \r
-      this.login = jQuery("login", data.documentElement).text()
-      this.pseudo = jQuery("pseudo", data.documentElement).text()\r
-      this.email = jQuery("email", data.documentElement).text()\r
-      this.css = jQuery("css", data.documentElement).text()
-      
-      // la page de la conversation principale
-      var tmp = jQuery("pagePrincipale", data.documentElement)
-      this.pagePrincipale = tmp.length < 1 ? 1 : parseInt(tmp.text())
-      
-      // met à jour la css
-      if (this.css != "")
-      {
-         jQuery("link#cssPrincipale").attr("href", this.css)
-         this.majMenu()
-      }
-      // les conversations
-      this.conversations = new Array()
-      jQuery("conversation", data.documentElement).each(
-         function(i)
-         {
-            thisClient.conversations.push( { racine : jQuery("racine", this).text(), page : jQuery("page", this).text() } )
-         }
-      )
-   }
-   this.dernierMessageErreur = jQuery("information", data.documentElement).text()
-}
-
-/**
-  * Met à jour les données personne sur serveur.
-  * @param async de manière asynchrone ? défaut = true
-  */
-Client.prototype.flush = function(async)
-{
-   if (async == undefined)
-      async = true
-
-   thisClient = this
-   this.util.xmlVersAction(this.getXMLProfile()).action.dump("Flush client")      
-   jQuery.ajax(
-      {
-         async: async,
-         type: "POST",
-         url: "request",
-         dataType: "xml",
-         data: this.util.xmlVersAction(this.getXMLProfile()),
-         success:
-            function(data)
-            {
-               //thisClient.util.log(thisClient.util.serializer.serializeToString(data))   
-            }
-      }
-   )
-   // TODO : retourner false si un problème est survenu lors de l'update du profile
-   return true
-}
-
-Client.prototype.majMenu = function()
-{
-   var displayType = this.css == "css/3/euphorik.css" ? "block" : "inline" //this.client
-
-   // met à jour le menu   
-   if (this.statut == statutType.enregistre)
-   {
-      jQuery("#menu .profile").css("display", displayType).text("profile")\r
-      jQuery("#menu .logout").css("display", displayType)
-      jQuery("#menu .register").css("display", "none")
-   }
-   else if (this.statut == statutType.identifie)
-   {
-      jQuery("#menu .profile").css("display", "none")\r
-      jQuery("#menu .logout").css("display", displayType)
-      jQuery("#menu .register").css("display", displayType)
-   }
-   else
-   {
-      jQuery("#menu .profile").css("display", displayType).text("login")\r
-      jQuery("#menu .logout").css("display", "none")
-      jQuery("#menu .register").css("display", displayType)
-   }
-}
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-jQuery.noConflict()
-            
-      \r
+
 // le main
-jQuery(document).ready(
+$(document).ready(
    function()
-   {  
-      /* FIXME : ce code pose problème sur konqueror, voir : http://www.kde-forum.org/thread.php?threadid=17993
-      var p = new DOMParser();
-      var doc =  p.parseFromString("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<action/>", "text/xml")
-      var s = new XMLSerializer()
-      alert(s.serializeToString(doc)) */
-   
-      var util = new 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 formateur = new euphorik.Formateur()
+      var util = new euphorik.Util(formateur)   
+      var client = new euphorik.Client(util)
+      var pages = new euphorik.Pages()
+      
+      
+      // connexion vers le serveur (utilise un cookie qui traine)
       client.connexionCookie()
       
-      // les styles css
-      for (var i = 1; i <= 3; i++)
-      {
-         jQuery("#css"+i).click(function(){
-            client.setCss("css/" + jQuery(this).attr("id").charAt(3) + "/euphorik.css")
-         })
-      }
+      $("#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
+      $(window).unload(function(){client.flush()})
       
-      jQuery("#menu .minichat").click(function(){ pages.afficherPage("minichat") })
-      jQuery("#menu .profile").click(function(){ pages.afficherPage("profile") })\r
-      jQuery("#menu .logout").click(function(){
-         util.messageDialogue("Êtes-vous sur de vouloir vous délogger ?", messageType.question,
+      $("#menu .minichat").click(function(){ pages.afficherPage("minichat") })
+      $("#menu .admin").click(function(){ pages.afficherPage("admin") })
+      $("#menu .profile").click(function(){ pages.afficherPage("profile") })
+      $("#menu .logout").click(function(){
+         util.messageDialogue("Êtes-vous sur de vouloir vous délogger ?", euphorik.Util.messageType.question,
             {"Oui" : function()
                {
                   client.deconnexion();
@@ -887,12 +178,19 @@ jQuery(document).ready(
             }
          )
       })
-      jQuery("#menu .register").click(function(){ pages.afficherPage("register") })
-
-      pages.ajouterPage(new PageMinichat(client, formateur, util))
-      pages.ajouterPage(new PageProfile(client, formateur, util))
-      pages.ajouterPage(new PageRegister(client, formateur, util))
+      $("#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 euphorik.PageMinichat(client, formateur, util))
+      pages.ajouterPage(new euphorik.PageAdmin(client, formateur, util))
+      pages.ajouterPage(new euphorik.PageProfile(client, formateur, util))
+      pages.ajouterPage(new euphorik.PageRegister(client, formateur, util))
+      pages.ajouterPage(new euphorik.PageAbout(client, formateur, util))
+      pages.ajouterPage("conditions_utilisation")
+      
       pages.afficherPage("minichat")
    }
 )
-