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