MOD amélioration du style
[euphorik.git] / js / euphorik.js
1 // coding: utf-8
2 // Copyright 2008 Grégory Burri
3 //
4 // This file is part of Euphorik.
5 //
6 // Euphorik is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // Euphorik is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with Euphorik. If not, see <http://www.gnu.org/licenses/>.
18
19 /**
20 * Contient la base javascript pour le site euphorik.ch.
21 * Chaque page possède son propre fichier js nommé "page<nom de la page>.js".
22 * Auteur : GBurri
23 * Date : 6.11.2007
24 */
25
26
27 /**
28 * La configuration.
29 * Normalement 'const' à la place de 'var' mais non supporté par IE7.
30 */
31 var conf = {
32 nbMessageAffiche : 40, // (par page)
33 pseudoDefaut : "<nick>",
34 tempsAffichageMessageDialogue : 4000, // en ms
35 smiles : {
36 "smile" : [/:\)/g, /:-\)/g],
37 "bigsmile" : [/:D/g, /:-D/g],
38 "clin" : [/;\)/g, /;-\)/g],
39 "cool" : [/8\)/g, /8-\)/g],
40 "eheheh" : [/:P/g, /:-P/g],
41 "lol" : [/\[-lol\]/g],
42 "spliff" : [/\[-spliff\]/g],
43 "oh" : [/:o/g, /:O/g],
44 "heink" : [/\[-heink\]/g],
45 "hum" : [/\[-hum\]/g],
46 "boh" : [/\[-boh\]/g],
47 "sniff" : [/:\(/g, /:-\(/g],
48 "triste" : [/\[-triste\]/g],
49 "pascontent" : [/>\(/g, /&gt;\(/g],
50 "argn" : [/\[-argn\]/g],
51 "redface" : [/\[-redface\]/g],
52 "bunny" : [/\[-lapin\]/g],
53 "chat" : [/\[-chat\]/g],
54 "renne" : [/\[-renne\]/g],
55 "star" : [/\[-star\]/g],
56 "kirby" : [/\[-kirby\]/g],
57 "slurp" : [/\[-slurp\]/g],
58 "agreed" : [/\[-agreed\]/g],
59 "dodo" : [/\[-dodo\]/g],
60 "bn" : [/\[-bn\]/g]
61 }
62 }
63
64 ///////////////////////////////////////////////////////////////////////////////////////////////////
65
66 String.prototype.trim = function()
67 {
68 return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");
69 }
70
71 String.prototype.ltrim = function()
72 {
73 return this.replace(/^\s+/, "");
74 }
75
76 String.prototype.rtrim = function()
77 {
78 return this.replace(/\s+$/, "");
79 }
80
81 ///////////////////////////////////////////////////////////////////////////////////////////////////
82
83 /**
84 * Cette classe regroupe des fonctions utilitaires (helpers).
85 * @formateur est permet de formater les messages affichés à l'aide de messageDialogue (facultatif)
86 */
87 function Util(formateur)
88 {
89 $("#info .fermer").click(function(){
90 $("#info").slideUp(50)
91 })
92
93 this.formateur = formateur
94 }
95
96 var messageType = {informatif: 0, question: 1, erreur: 2}
97
98 /**
99 * Affiche une boite de dialogue avec un message à l'intérieur.
100 * @param message le message (string)
101 * @param type voir 'messageType'. par défaut messageType.informatif
102 * @param les boutons sous la forme d'un objet ou les clefs sont les labels des boutons
103 * et les valeurs les fonctions executées lorsqu'un bouton est activé.
104 * @param formate faut-il formaté le message ? true par défaut
105 */
106 Util.prototype.messageDialogue = function(message, type, boutons, formate)
107 {
108 var thisUtil = this
109
110 if (type == undefined)
111 type = messageType.informatif
112
113 if (formate == undefined)
114 formate = true
115
116 if (this.timeoutMessageDialogue != undefined)
117 clearTimeout(this.timeoutMessageDialogue)
118
119 var fermer = function(){$("#info").slideUp(100)}
120 fermer()
121
122 $("#info .message").html(thisUtil.formateur == undefined || !formate ? message : thisUtil.formateur.traitementComplet(message))
123 switch(type)
124 {
125 case messageType.informatif : $("#info #icone").attr("class", "information"); break
126 case messageType.question : $("#info #icone").attr("class", "interrogation"); break
127 case messageType.erreur : $("#info #icone").attr("class", "exclamation"); break
128 }
129 $("#info .boutons").html("")
130 for (var b in boutons)
131 $("#info .boutons").append("<div>" + b + "</div>").find("div:last").click(boutons[b]).click(fermer)
132
133 $("#info").slideDown(200)
134 this.timeoutMessageDialogue = setTimeout(fermer, conf.tempsAffichageMessageDialogue)
135 }
136
137 /**
138 * Utilisé pour l'envoie de donnée avec la méthode ajax de jQuery.
139 */
140 Util.prototype.jsonVersAction = function(json)
141 {
142 return {action : JSON.stringify(json) }
143 }
144
145 Util.prototype.md5 = function(chaine)
146 {
147 return hex_md5(chaine)
148 }
149
150 // pompé de http://www.faqts.com/knowledge_base/view.phtml/aid/13562/fid/130
151 Util.prototype.setSelectionRange = function(input, selectionStart, selectionEnd)
152 {
153 if (input.setSelectionRange)
154 {
155 input.focus()
156 input.setSelectionRange(selectionStart, selectionEnd)
157 }
158 else if (input.createTextRange)
159 {
160 var range = input.createTextRange()
161 range.collapse(true)
162 range.moveEnd('character', selectionEnd)
163 range.moveStart('character', selectionStart)
164 range.select()
165 }
166 }
167
168 Util.prototype.setCaretToEnd = function(input)
169 {
170 this.setSelectionRange(input, input.value.length, input.value.length)
171 }
172 Util.prototype.setCaretToBegin = function(input)
173 {
174 this.setSelectionRange(input, 0, 0)
175 }
176 Util.prototype.setCaretToPos = function(input, pos)
177 {
178 this.setSelectionRange(input, pos, pos)
179 }
180 Util.prototype.selectString = function(input, string)
181 {
182 var match = new RegExp(string, "i").exec(input.value)
183 if (match)
184 {
185 this.setSelectionRange (input, match.index, match.index + match[0].length)
186 }
187 }
188 Util.prototype.replaceSelection = function(input, replaceString) {
189 if (input.setSelectionRange)
190 {
191 var selectionStart = input.selectionStart
192 var selectionEnd = input.selectionEnd
193 input.value = input.value.substring(0, selectionStart) + replaceString + input.value.substring(selectionEnd)
194
195 if (selectionStart != selectionEnd) // has there been a selection
196 this.setSelectionRange(input, selectionStart, selectionStart + replaceString.length)
197 else // set caret
198 this.setCaretToPos(input, selectionStart + replaceString.length)
199 }
200 else if (document.selection)
201 {
202 var range = document.selection.createRange();
203 if (range.parentElement() == input)
204 {
205 var isCollapsed = range.text == ''
206 range.text = replaceString
207 if (!isCollapsed)
208 {
209 // there has been a selection
210 // it appears range.select() should select the newly
211 // inserted text but that fails with IE
212 range.moveStart('character', -replaceString.length);
213 range.select();
214 }
215 }
216 }
217 }
218
219 Util.prototype.rot13 = function(chaine)
220 {
221 var ACode = 'A'.charCodeAt(0)
222 var aCode = 'a'.charCodeAt(0)
223 var MCode = 'M'.charCodeAt(0)
224 var mCode = 'm'.charCodeAt(0)
225 var ZCode = 'Z'.charCodeAt(0)
226 var zCode = 'z'.charCodeAt(0)
227
228 var f = function(ch, pos) {
229 if (pos == ch.length)
230 return ""
231
232 var c = ch.charCodeAt(pos);
233 return String.fromCharCode(
234 c +
235 (c >= ACode && c <= MCode || c >= aCode && c <= mCode ? 13 :
236 (c > MCode && c <= ZCode || c > mCode && c <= zCode ? -13 : 0))
237 ) + f(ch, pos + 1)
238 }
239 return f(chaine, 0)
240 }
241
242 ///////////////////////////////////////////////////////////////////////////////////////////////////
243
244 function Pages()
245 {
246 this.pageCourante = null
247 this.pages = {}
248 }
249
250 Pages.prototype.ajouterPage = function(page)
251 {
252 page.pages = this // la magie des langages dynamiques : le foutoire
253 this.pages[page.nom] = page
254 }
255
256 Pages.prototype.afficherPage = function(nomPage, forcerChargement)
257 {
258 if (forcerChargement == undefined) forcerChargement = false
259
260 var page = this.pages[nomPage]
261 if (page == undefined || (!forcerChargement && page == this.pageCourante)) return
262
263 if (this.pageCourante != null && this.pageCourante.decharger)
264 this.pageCourante.decharger()
265
266 $("#menu li").removeClass("courante")
267 $("#menu li." + nomPage).addClass("courante")
268
269 this.pageCourante = page
270 $("#page").html(this.pageCourante.contenu()).removeClass().addClass(this.pageCourante.nom)
271
272 if (this.pageCourante.charger)
273 this.pageCourante.charger()
274 }
275
276 ///////////////////////////////////////////////////////////////////////////////////////////////////
277
278 /**
279 * Classe permettant de formater du texte par exemple pour la substitution des liens dans les
280 * message par "[url]".
281 * TODO : améliorer l'efficacité des méthods notamment lié au smiles.
282 */
283 function Formateur()
284 {
285 this.smiles = conf.smiles
286 this.protocoles = "http|https|ed2k"
287
288 this.regexUrl = new RegExp("(?:(?:" + this.protocoles + ")://|www\\.)[^ ]*", "gi")
289 this.regexImg = new RegExp("^.*?\\.(gif|jpg|png|jpeg|bmp|tiff)$", "i")
290 this.regexDomaine = new RegExp("^(?:(?:" + this.protocoles + ")://|www\\.).*?([^/.]+\\.[^/.]+)(?:$|/).*$", "i")
291 this.regexTestProtocoleExiste = new RegExp("^(?:" + this.protocoles + ")://.*$", "i")
292 this.regexNomProtocole = new RegExp("^(.*?)://")
293 }
294
295 /**
296 * Formate un pseudo saise par l'utilisateur.
297 * @param pseudo le pseudo brut
298 * @return le pseudo filtré
299 */
300 Formateur.prototype.filtrerInputPseudo = function(pseudo)
301 {
302 return pseudo.replace(/{|}/g, "").trim()
303 }
304
305 Formateur.prototype.getSmilesHTML = function()
306 {
307 var XHTML = ""
308 for (var sNom in this.smiles)
309 {
310 XHTML += "<img class=\"" + sNom + "\" src=\"img/smileys/" + sNom + ".gif\" alt =\"" + sNom + "\" />"
311 }
312 return XHTML
313 }
314
315 /**
316 * Formatage complet d'un texte.
317 * @M le message
318 * @pseudo facultatif, permet de contruire le label des images sous la forme : "<Pseudo> : <Message>"
319 */
320 Formateur.prototype.traitementComplet = function(M, pseudo)
321 {
322 return this.traiterLiensConv(this.traiterSmiles(this.traiterURL(this.traiterWikiSyntaxe(this.remplacerBalisesHTML(M)), pseudo)))
323 }
324
325 /**
326 * Transforme les liens en entités clickables.
327 * Un lien vers une conversation permet d'ouvrire celle ci, elle se marque comme ceci dans un message :
328 * "{5F}" ou 5F est la racine de la conversation.
329 * Ce lien sera transformer en <span class="lienConv">{5F}</span> pouvant être clické pour créer la conv 5F.
330 */
331 Formateur.prototype.traiterLiensConv = function(M)
332 {
333 return M.replace(
334 /\{\w+\}/g,
335 function(lien)
336 {
337 return "<span class=\"lienConv\">" + lien + "</span>"
338 }
339 )
340 }
341
342 /**
343 * FIXME : Cette méthode est attrocement lourde ! A optimiser.
344 * moyenne sur échantillon : 234ms
345 */
346 Formateur.prototype.traiterSmiles = function(M)
347 {
348 for (var sNom in this.smiles)
349 {
350 ss = this.smiles[sNom]
351 for (var i = 0; i < ss.length; i++)
352 M = M.replace(ss[i], "<img src=\"img/smileys/" + sNom + ".gif\" alt =\"" + sNom + "\" />")
353 }
354 return M
355 }
356
357 Formateur.prototype.remplacerBalisesHTML = function(M)
358 {
359 return M.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
360 }
361
362 Formateur.prototype.traiterURL = function(M, pseudo)
363 {
364 thisFormateur = this
365
366 var traitementUrl = function(url)
367 {
368 // si ya pas de protocole on rajoute "http://"
369 if (!thisFormateur.regexTestProtocoleExiste.test(url))
370 url = "http://" + url
371 var extension = thisFormateur.getShort(url)
372 return "<a " + (extension[1] ? "title=\"" + (pseudo == undefined ? "" : thisFormateur.traiterPourFenetreLightBox(pseudo, url) + ": ") + thisFormateur.traiterPourFenetreLightBox(M, url) + "\"" + " rel=\"lightbox\"" : "") + " href=\"" + url + "\" >[" + extension[0] + "]</a>"
373 }
374 return M.replace(this.regexUrl, traitementUrl)
375 }
376
377 /**
378 * Formatage en utilisant un sous-ensemble des règles de mediwiki.
379 * par exemple ''italic'' devient <i>italic</i>
380 */
381 Formateur.prototype.traiterWikiSyntaxe = function(M)
382 {
383 return M.replace(
384 /'''(.*?)'''/g,
385 function(texte, capture)
386 {
387 return "<b>" + capture + "</b>"
388 }
389 ).replace(
390 /''(.*?)''/g,
391 function(texte, capture)
392 {
393 return "<i>" + capture + "</i>"
394 }
395 )
396 }
397
398 /**
399 * Renvoie une version courte de l'url.
400 * par exemple : http://en.wikipedia.org/wiki/Yakov_Smirnoff devient wikipedia.org
401 */
402 Formateur.prototype.getShort = function(url)
403 {
404 var estUneImage = false
405 var versionShort = null
406 var rechercheImg = this.regexImg.exec(url)
407
408 if (rechercheImg != null)
409 {
410 versionShort = rechercheImg[1].toLowerCase()
411 if (versionShort == "jpeg") versionShort = "jpg" // jpeg -> jpg
412 estUneImage = true
413 }
414 else
415 {
416 var rechercheDomaine = this.regexDomaine.exec(url)
417 if (rechercheDomaine != null && rechercheDomaine.length >= 2)
418 versionShort = rechercheDomaine[1]
419 else
420 {
421 var nomProtocole = this.regexNomProtocole.exec(url)
422 if (nomProtocole != null && nomProtocole.length >= 2)
423 versionShort = nomProtocole[1]
424 }
425 }
426
427 return [versionShort == null ? "url" : versionShort, estUneImage]
428 }
429
430 /**
431 * Traite les pseudo et messages à être affiché dans le titre d'une image visualisé avec lightbox.
432 */
433 Formateur.prototype.traiterPourFenetreLightBox = function(M, urlCourante)
434 {
435 thisFormateur = this
436 var traitementUrl = function(url)
437 {
438 return "[" + thisFormateur.getShort(url)[0] + (urlCourante == url ? "*" : "") + "]"
439 }
440
441 return this.remplacerBalisesHTML(M).replace(this.regexUrl, traitementUrl)
442 }
443
444
445 ///////////////////////////////////////////////////////////////////////////////////////////////////
446
447 // les statuts possibes du client
448 var statutType = {
449 // mode enregistré, peut poster des messages et modifier son profile
450 auth_registered : 0,
451 // mode identifié, peut poster des messages mais n'a pas accès au profile
452 auth_not_registered : 1,
453 // mode déconnecté, ne peut pas poster de message
454 deconnected : 2
455 }
456
457 function Client(util)
458 {
459 this.util = util
460
461 this.cookie = null
462 this.regexCookie = new RegExp("^cookie=([^;]*)")
463
464 // données personnels
465 this.resetDonneesPersonnelles()
466
467 this.setStatut(statutType.deconnected)
468
469 // si true alors chaque modification du client est mémorisé sur le serveur
470 this.autoflush = $.browser["opera"]
471 }
472
473 Client.prototype.resetDonneesPersonnelles = function()
474 {
475 this.id = 0
476 this.pseudo = conf.pseudoDefaut
477 this.login = ""
478 this.password = ""
479 this.email = ""
480 this.css = $("link#cssPrincipale").attr("href")
481 this.nickFormat = "nick"
482 this.cookie = undefined
483
484 this.pagePrincipale = 1
485 this.ekMaster = false
486
487 // les conversations, une conversation est un objet possédant les attributs suivants :
488 // - racine (entier)
489 // - page (entier)
490 this.conversations = new Array()
491 }
492
493 Client.prototype.setCss = function(css)
494 {
495 if (this.css == css || css == "")
496 return
497
498 this.css = css
499 $("link#cssPrincipale").attr("href", this.css)
500 this.majMenu()
501
502 if (this.autoflush) this.flush(true)
503 }
504
505 Client.prototype.pageSuivante = function(numConv)
506 {
507 if (numConv < 0 && this.pagePrincipale > 1)
508 this.pagePrincipale -= 1
509 else if (this.conversations[numConv].page > 1)
510 this.conversations[numConv].page -= 1
511 }
512
513 Client.prototype.pagePrecedente = function(numConv)
514 {
515 if (numConv < 0)
516 this.pagePrincipale += 1
517 else
518 this.conversations[numConv].page += 1
519 }
520
521 /**
522 * Définit la première page pour la conversation donnée.
523 * @return true si la page a changé sinon false
524 */
525 Client.prototype.goPremierePage = function(numConv)
526 {
527 if (numConv < 0)
528 {
529 if (this.pagePrincipale == 1)
530 return false
531 this.pagePrincipale = 1
532 }
533 else
534 {
535 if (this.conversations[numConv].page == 1)
536 return false
537 this.conversations[numConv].page = 1
538 }
539 return true
540 }
541
542 /**
543 * Ajoute une conversation à la vue de l'utilisateur.
544 * Le profile de l'utilisateur est directement sauvegardé sur le serveur.
545 * @param racines la racine de la conversation (integer)
546 * @return true si la conversation a été créée sinon false (par exemple si la conv existe déjà)
547 */
548 Client.prototype.ajouterConversation = function(racine)
549 {
550 // vérification s'il elle n'existe pas déjà
551 for (var i = 0; i < this.conversations.length; i++)
552 if (this.conversations[i].root == racine)
553 return false
554
555 this.conversations.push({root : racine, page : 1})
556
557 if (this.autoflush) this.flush(true)
558
559 return true
560 }
561
562 Client.prototype.supprimerConversation = function(num)
563 {
564 if (num < 0 || num >= this.conversations.length) return
565
566 // décalage TODO : supprimer le dernier élément
567 for (var i = num; i < this.conversations.length - 1; i++)
568 this.conversations[i] = this.conversations[i+1]
569 this.conversations.pop()
570
571 if (this.autoflush) this.flush(true)
572 }
573
574 Client.prototype.getJSONLogin = function(login, password)
575 {
576 return {
577 "action" : "authentification",
578 "login" : login,
579 "password" : password
580 }
581 }
582
583 Client.prototype.getJSONLoginCookie = function()
584 {
585 return {
586 "action" : "authentification",
587 "cookie" : this.cookie
588 }
589 }
590
591 /**
592 * le couple (login, password) est facultatif. S'il n'est pas fournit alors il ne sera pas possible
593 * de s'autentifier avec (login, password).
594 */
595 Client.prototype.getJSONEnregistrement = function(login, password)
596 {
597 var mess = { "action" : "register" }
598
599 if (login != undefined && password != undefined)
600 {
601 mess["login"] = login
602 mess["password"] = password
603 }
604
605 return mess;
606 }
607
608 Client.prototype.getJSONConversations = function()
609 {
610 var conversations = new Array()
611 for (var i = 0; i < this.conversations.length; i++)
612 conversations.push({ "root" : this.conversations[i].root, "page" : this.conversations[i].page})
613 return conversations
614 }
615
616 Client.prototype.getJSONProfile = function()
617 {
618 return {
619 "action" : "set_profile",
620 "cookie" : this.cookie,
621 "login" : this.login,
622 "password" : this.password,
623 "nick" : this.pseudo,
624 "email" : this.email,
625 "css" : this.css,
626 "nick_format" : this.nickFormat,
627 "main_page" : this.pagePrincipale < 1 ? 1 : this.pagePrincipale,
628 "conversations" : this.getJSONConversations()
629 }
630 }
631
632 /**
633 * Renvoie null si pas définit.
634 */
635 Client.prototype.getCookie = function()
636 {
637 var cookie = this.regexCookie.exec(document.cookie)
638 if (cookie == null) this.cookie = null
639 else this.cookie = cookie[1]
640 }
641
642 Client.prototype.delCookie = function()
643 {
644 document.cookie = "cookie=; max-age=0"
645 }
646
647 Client.prototype.setCookie = function(cookie)
648 {
649 if (this.cookie == null)
650 return
651
652 document.cookie =
653 "cookie="+this.cookie+
654 "; max-age=" + (60 * 60 * 24 * 365)
655 }
656
657 Client.prototype.authentifie = function()
658 {
659 return this.statut == statutType.auth_registered || this.statut == statutType.auth_not_registered
660 }
661
662 Client.prototype.setStatut = function(statut)
663 {
664 // conversation en "enum" si en "string"
665 if (typeof(statut) == "string")
666 {
667 statut =
668 statut == "auth_registered" ?
669 statutType.auth_registered :
670 (statut == "auth_not_registered" ? statutType.auth_not_registered : statutType.deconnected)
671 }
672
673 if (statut == this.statut) return
674
675 this.statut = statut
676 this.majMenu()
677 }
678
679 /**
680 * Effectue la connexion vers le serveur.
681 * Cette fonction est bloquante tant que la connexion n'a pas été établie.
682 * S'il existe un cookie en local on s'authentifie directement avec lui.
683 * Si il n'est pas possible de s'authentifier alors on affiche un captcha anti-bot.
684 */
685 Client.prototype.connexionCookie = function()
686 {
687 this.getCookie()
688 if (this.cookie == null) return false;
689 return this.connexion(this.getJSONLoginCookie())
690 }
691
692 Client.prototype.connexionLogin = function(login, password)
693 {
694 return this.connexion(this.getJSONLogin(login, password))
695 }
696
697 Client.prototype.enregistrement = function(login, password)
698 {
699 if (this.authentifie())
700 {
701 this.login = login
702 this.password = password
703 if(this.flush())
704 {
705 this.setStatut(statutType.auth_registered)
706 return true
707 }
708 return false
709 }
710 else
711 {
712 return this.connexion(this.getJSONEnregistrement(login, password))
713 }
714 }
715
716 Client.prototype.connexion = function(messageJson)
717 {
718 ;;; dumpObj(messageJson)
719 thisClient = this
720 jQuery.ajax(
721 {
722 async: false,
723 type: "POST",
724 url: "request",
725 dataType: "json",
726 data: this.util.jsonVersAction(messageJson),
727 success:
728 function(data)
729 {
730 ;;; dumpObj(data)
731 if (data["reply"] == "error")
732 thisClient.util.messageDialogue(data["error_message"])
733 else
734 thisClient.chargerDonnees(data)
735 }
736 }
737 )
738 return this.authentifie()
739 }
740
741 Client.prototype.deconnexion = function()
742 {
743 this.flush(true)
744 this.delCookie()
745 this.resetDonneesPersonnelles()
746 this.setStatut(statutType.deconnected) // deconnexion
747 }
748
749 Client.prototype.chargerDonnees = function(data)
750 {
751 // la modification du statut qui suit met à jour le menu, le menu dépend (page admin)
752 // de l'état ekMaster
753 this.ekMaster = data["ek_master"] != undefined ? data["ek_master"] : false
754
755 this.setStatut(data["status"])
756
757 if (this.authentifie())
758 {
759 this.cookie = data["cookie"]
760 this.setCookie()
761
762 this.id = data["id"]
763 this.login = data["login"]
764 this.pseudo = data["nick"]
765 this.email = data["email"]
766 this.setCss(data["css"])
767 this.nickFormat = data["nick_format"]
768
769 // la page de la conversation principale
770 this.pagePrincipale = data["main_page"] == undefined ? 1 : data["main_page"]
771
772 // les conversations
773 this.conversations = data["conversations"]
774 }
775 }
776
777 /**
778 * Met à jour les données personne sur serveur.
779 * @param async de manière asynchrone ? défaut = true
780 * @return false si le flush n'a pas pû se faire sinon true
781 */
782 Client.prototype.flush = function(async)
783 {
784 if (async == undefined)
785 async = false
786
787 if (!this.authentifie())
788 return false
789
790 var thisClient = this
791 var ok = true
792
793 ;;; dumpObj(this.getJSONProfile())
794 jQuery.ajax(
795 {
796 async: async,
797 type: "POST",
798 url: "request",
799 dataType: "json",
800 data: this.util.jsonVersAction(this.getJSONProfile()),
801 success:
802 function(data)
803 {
804 ;;; dumpObj(data)
805 if (data["reply"] == "error")
806 {
807 thisClient.util.messageDialogue(data["error_message"])
808 ok = false
809 }
810 }
811 }
812 )
813
814 return ok
815 }
816
817 Client.prototype.majMenu = function()
818 {
819 // TODO : à virer : ne plus changer de style de display ... spa beau .. ou trouver une autre méthode
820 // var displayType = this.css == "css/3/euphorik.css" ? "block" : "inline" //this.client
821 displayType = "block"
822
823 $("#menu .admin").css("display", this.ekMaster ? displayType : "none")
824
825 // met à jour le menu
826 if (this.statut == statutType.auth_registered)
827 {
828 $("#menu .profile").css("display", displayType).text("profile")
829 $("#menu .logout").css("display", displayType)
830 $("#menu .register").css("display", "none")
831 }
832 else if (this.statut == statutType.auth_not_registered)
833 {
834 $("#menu .profile").css("display", "none")
835 $("#menu .logout").css("display", displayType)
836 $("#menu .register").css("display", displayType)
837 }
838 else
839 {
840 $("#menu .profile").css("display", displayType).text("login")
841 $("#menu .logout").css("display", "none")
842 $("#menu .register").css("display", displayType)
843 }
844 }
845
846 Client.prototype.slap = function(userId, raison)
847 {
848 var thisClient = this
849
850 jQuery.ajax({
851 type: "POST",
852 url: "request",
853 dataType: "json",
854 data: this.util.jsonVersAction(
855 {
856 "action" : "slap",
857 "cookie" : thisClient.cookie,
858 "user_id" : userId,
859 "reason" : raison
860 }),
861 success:
862 function(data)
863 {
864 if (data["reply"] == "error")
865 thisClient.util.messageDialogue(data["error_message"])
866 }
867 })
868 }
869
870 Client.prototype.ban = function(userId, raison, minutes)
871 {
872 var thisClient = this
873
874 // par défaut un ban correspond à 3 jours
875 if (typeof(minutes) == "undefined")
876 minutes = 60 * 24 * 3
877
878 jQuery.ajax({
879 type: "POST",
880 url: "request",
881 dataType: "json",
882 data: this.util.jsonVersAction(
883 {
884 "action" : "ban",
885 "cookie" : thisClient.cookie,
886 "duration" : minutes,
887 "user_id" : userId,
888 "reason" : raison
889 }),
890 success:
891 function(data)
892 {
893 if (data["reply"] == "error")
894 thisClient.util.messageDialogue(data["error_message"])
895 }
896 })
897 }
898
899 Client.prototype.kick = function(userId, raison)
900 {
901 this.ban(userId, raison, 15)
902 }
903
904 ///////////////////////////////////////////////////////////////////////////////////////////////////
905
906 /**
907 * classe permettant de gérer les événements (push serveur).
908 * @page la page
909 */
910 function PageEvent(page, util)
911 {
912 this.page = page
913 this.util = util
914
915 // l'objet JSONHttpRequest représentant la connexion d'attente
916 this.attenteCourante = null
917
918 // le multhreading du pauvre, merci javascript de m'offrire autant de primitives pour la gestion de la concurrence...
919 this.stop = false
920 }
921
922 /**
923 * Arrête l'attente courante s'il y en a une.
924 */
925 PageEvent.prototype.stopAttenteCourante = function()
926 {
927 this.stop = true
928
929 if (this.attenteCourante != null)
930 {
931 this.attenteCourante.abort()
932 }
933 }
934
935 /**
936 * Attend un événement lié à la page.
937 * @funSend une fonction renvoyant les données json à envoyer
938 * @funReceive une fonction qui accepte un paramètre correspondant au données reçues
939 */
940 PageEvent.prototype.waitEvent = function(funSend, funReceive)
941 {
942 this.stopAttenteCourante()
943
944 this.stop = false
945
946 var thisPageEvent = this
947
948 // on doit conserver l'ordre des valeurs de l'objet JSON (le serveur les veut dans l'ordre définit dans le protocole)
949 // TODO : ya pas mieux ?
950 var dataToSend =
951 {
952 "action" : "wait_event",
953 "page" : this.page
954 }
955 var poulpe = funSend()
956 for (v in poulpe)
957 dataToSend[v] = poulpe[v]
958
959 ;;; dumpObj(dataToSend)
960
961 this.attenteCourante = jQuery.ajax({
962 type: "POST",
963 url: "request",
964 dataType: "json",
965 data: this.util.jsonVersAction(dataToSend),
966 success:
967 function(data)
968 {
969 ;;; dumpObj(data)
970
971 funReceive(data)
972
973 // rappel de la fonction dans 100 ms
974 setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funReceive) }, 100)
975 },
976 error:
977 function(XMLHttpRequest, textStatus, errorThrown)
978 {
979 setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funReceive) }, 1000)
980 }
981 })
982 }
983
984 /**
985 * Si un stopAttenteCourante survient un peu n'importe quand il faut imédiatement arreter de boucler.
986 */
987 PageEvent.prototype.waitEvent2 = function(funSend, funReceive)
988 {
989 if (this.stop)
990 return
991 this.waitEvent(funSend, funReceive)
992 }
993
994 ///////////////////////////////////////////////////////////////////////////////////////////////////
995
996 function initialiserListeStyles(client)
997 {
998 $("#menuCss").change(
999 function()
1000 {
1001 client.setCss("css/" + $("option:selected", this).attr("value") + "/euphorik.css")
1002 }
1003 )
1004 }
1005
1006 // charge dynamiquement le script de debug
1007 ;;; jQuery.ajax({async : false, url : "js/debug.js", dataType : "script"})
1008
1009 // le main
1010 $(document).ready(
1011 function()
1012 {
1013 var formateur = new Formateur()
1014 var util = new Util(formateur)
1015 var client = new Client(util)
1016 var pages = new Pages()
1017
1018 // connexion vers le serveur (utilise un cookie qui traine)
1019 client.connexionCookie()
1020
1021 initialiserListeStyles(client)
1022
1023 // FIXME : ne fonctionne pas sous opera
1024 // voir : http://dev.jquery.com/ticket/2892#preview
1025 $(window).unload(function(){client.flush()})
1026
1027 $("#menu .minichat").click(function(){ pages.afficherPage("minichat") })
1028 $("#menu .admin").click(function(){ pages.afficherPage("admin") })
1029 $("#menu .profile").click(function(){ pages.afficherPage("profile") })
1030 $("#menu .logout").click(function(){
1031 util.messageDialogue("Êtes-vous sur de vouloir vous délogger ?", messageType.question,
1032 {"Oui" : function()
1033 {
1034 client.deconnexion();
1035 pages.afficherPage("minichat", true)
1036 },
1037 "Non" : function(){}
1038 }
1039 )
1040 })
1041 $("#menu .register").click(function(){ pages.afficherPage("register") })
1042 $("#menu .about").click(function(){ pages.afficherPage("about") })
1043
1044 pages.ajouterPage(new PageMinichat(client, formateur, util))
1045 pages.ajouterPage(new PageAdmin(client, formateur, util))
1046 pages.ajouterPage(new PageProfile(client, formateur, util))
1047 pages.ajouterPage(new PageRegister(client, formateur, util))
1048 pages.ajouterPage(new PageAbout(client, formateur, util))
1049 pages.afficherPage("minichat")
1050 }
1051 )