e57cc4ee62e1f589f0e782af411965988e31b066
[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 // tout euphorik est contenu dans cet objet
29 var euphorik = {}
30 // ;; euphorik.include =
31 // ;; euphorik.include = function(f) { var s = document.createElement('script'); s.type = 'text/javascript'; s.src = "js/" + f + ".js"; document.getElementsByTagName('head')[0].appendChild(s); }
32
33
34 // version jQuery : function(f) { jQuery.ajax({async : false, url : "js/" + f + ".js", dataType : "script"}) }
35 // mais comme il n'est pas encore chargé...
36 ;; euphorik.include = function(f) {
37 ;; var req, url = 'js/' + f + '.js'
38 ;; if (window.XMLHttpRequest) {
39 ;; req = new XMLHttpRequest(); req.open("GET", url, false /* async */); req.send(null);
40 ;; } else if (window.ActiveXObject) {
41 ;; req = new ActiveXObject((navigator.userAgent.toLowerCase().indexOf('msie 5') != -1) ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP");
42 ;; if (req) { req.open("GET", url, false); req.send(); }
43 ;; }
44 ;; if (req!==false) { if (req.status==200) { window.eval(req.responseText); } else if (req.status==404) { alert("erreur de chargement (404) de : " + url) } }
45 ;; }
46
47 ;; euphorik.include("jquery")
48 ;; euphorik.include("jquery.lightbox")
49 ;; euphorik.include("md5")
50 ;; euphorik.include("json2")
51
52 ;; euphorik.include("conf")
53 ;; euphorik.include("util")
54
55 ;; euphorik.include("pageMinichat")
56 ;; euphorik.include("pageAdmin")
57 ;; euphorik.include("pageProfile")
58 ;; euphorik.include("pageRegister")
59 ;; euphorik.include("pageAbout")
60
61
62 ///////////////////////////////////////////////////////////////////////////////////////////////////
63
64 String.prototype.trim = function()
65 {
66 return jQuery.trim(this) // anciennement : this.replace(/^\s+|\s+$/g, "");
67 }
68
69 String.prototype.ltrim = function()
70 {
71 return this.replace(/^\s+/, "");
72 }
73
74 String.prototype.rtrim = function()
75 {
76 return this.replace(/\s+$/, "");
77 }
78
79
80 ///////////////////////////////////////////////////////////////////////////////////////////////////
81
82 function Pages()
83 {
84 this.pageCourante = null
85 this.pages = {}
86 }
87
88 /**
89 * Accepte soit un objet soit un string.
90 * un string correspond au nom de la page, par exemple : "page" -> "page.html"
91 */
92 Pages.prototype.ajouterPage = function(page)
93 {
94 if (typeof page == "string")
95 {
96 this.pages[page] = page
97 }
98 else
99 {
100 page.pages = this // la magie des langages dynamiques : le foutoire
101 this.pages[page.nom] = page
102 }
103 }
104
105 Pages.prototype.afficherPage = function(nomPage, forcerChargement)
106 {
107 if (forcerChargement == undefined) forcerChargement = false
108
109 var page = this.pages[nomPage]
110 if (page == undefined || (!forcerChargement && page == this.pageCourante)) return
111
112 if (this.pageCourante != null && this.pageCourante.decharger)
113 this.pageCourante.decharger()
114
115 $("#menu li").removeClass("courante")
116 $("#menu li." + nomPage).addClass("courante")
117
118 this.pageCourante = page
119 var contenu = ""
120 if (typeof page == "string")
121 $.ajax({async: false, url: "pages/" + page + ".html", success : function(page) { contenu += page }})
122 else
123 {
124 contenu += this.pageCourante.contenu()
125 }
126 $("#page").html(contenu).removeClass().addClass(this.pageCourante.nom +
127 (this.pageCourante.classes != undefined ? " " + this.pageCourante.classes() : "") // l'objet peut fournire des classes css supplémentaires sous la forme d'un string
128 )
129
130 if (this.pageCourante.charger)
131 this.pageCourante.charger()
132 }
133
134 ///////////////////////////////////////////////////////////////////////////////////////////////////
135
136 /**
137 * Classe permettant de formater du texte par exemple pour la substitution des liens dans les
138 * message par "[url]".
139 * TODO : améliorer l'efficacité des méthods notamment lié au smiles.
140 */
141 function Formateur()
142 {
143 this.smiles = euphorik.conf.smiles
144 this.protocoles = "http|https|ed2k"
145
146 this.regexUrl = new RegExp("(?:(?:" + this.protocoles + ")://|www\\.)[^ ]*", "gi")
147 this.regexImg = new RegExp("^.*?\\.(gif|jpg|png|jpeg|bmp|tiff)$", "i")
148 this.regexDomaine = new RegExp("^(?:(?:" + this.protocoles + ")://|www\\.).*?([^/.]+\\.[^/.]+)(?:$|/).*$", "i")
149 this.regexTestProtocoleExiste = new RegExp("^(?:" + this.protocoles + ")://.*$", "i")
150 this.regexNomProtocole = new RegExp("^(.*?)://")
151 }
152
153 /**
154 * Formate un pseudo saise par l'utilisateur.
155 * @param pseudo le pseudo brut
156 * @return le pseudo filtré
157 */
158 Formateur.prototype.filtrerInputPseudo = function(pseudo)
159 {
160 return pseudo.replace(/{|}/g, "").trim()
161 }
162
163 Formateur.prototype.getSmilesHTML = function()
164 {
165 var XHTML = ""
166 for (var sNom in this.smiles)
167 {
168 XHTML += "<img class=\"" + sNom + "\" src=\"img/smileys/" + sNom + ".gif\" alt =\"" + sNom + "\" />"
169 }
170 return XHTML
171 }
172
173 /**
174 * Formatage complet d'un texte.
175 * @M le message
176 * @pseudo facultatif, permet de contruire le label des images sous la forme : "<Pseudo> : <Message>"
177 */
178 Formateur.prototype.traitementComplet = function(M, pseudo)
179 {
180 return this.traiterLiensConv(this.traiterSmiles(this.traiterURL(this.traiterWikiSyntaxe(this.remplacerBalisesHTML(M)), pseudo)))
181 }
182
183 /**
184 * Transforme les liens en entités clickables.
185 * Un lien vers une conversation permet d'ouvrire celle ci, elle se marque comme ceci dans un message :
186 * "{5F}" ou 5F est la racine de la conversation.
187 * Ce lien sera transformer en <span class="lienConv">{5F}</span> pouvant être clické pour créer la conv 5F.
188 */
189 Formateur.prototype.traiterLiensConv = function(M)
190 {
191 return M.replace(
192 /\{\w+\}/g,
193 function(lien)
194 {
195 return "<span class=\"lienConv\">" + lien + "</span>"
196 }
197 )
198 }
199
200 /**
201 * FIXME : Cette méthode est attrocement lourde ! A optimiser.
202 * moyenne sur échantillon : 234ms
203 */
204 Formateur.prototype.traiterSmiles = function(M)
205 {
206 for (var sNom in this.smiles)
207 {
208 var ss = this.smiles[sNom]
209 for (var i = 0; i < ss.length; i++)
210 M = M.replace(ss[i], "<img src=\"img/smileys/" + sNom + ".gif\" alt =\"" + sNom + "\" />")
211 }
212 return M
213 }
214
215 Formateur.prototype.remplacerBalisesHTML = function(M)
216 {
217 return M.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;")
218 }
219
220 Formateur.prototype.traiterURL = function(M, pseudo)
221 {
222 var thisFormateur = this
223
224 var traitementUrl = function(url)
225 {
226 // si ya pas de protocole on rajoute "http://"
227 if (!thisFormateur.regexTestProtocoleExiste.test(url))
228 url = "http://" + url
229 var extension = thisFormateur.getShort(url)
230 return "<a " + (extension[1] ? "title=\"" + (pseudo == undefined ? "" : thisFormateur.traiterPourFenetreLightBox(pseudo, url) + ": ") + thisFormateur.traiterPourFenetreLightBox(M, url) + "\"" + " rel=\"lightbox\"" : "") + " href=\"" + url + "\" >[" + extension[0] + "]</a>"
231 }
232 return M.replace(this.regexUrl, traitementUrl)
233 }
234
235 /**
236 * Formatage en utilisant un sous-ensemble des règles de mediwiki.
237 * par exemple ''italic'' devient <i>italic</i>
238 */
239 Formateur.prototype.traiterWikiSyntaxe = function(M)
240 {
241 return M.replace(
242 /'''(.*?)'''/g,
243 function(texte, capture)
244 {
245 return "<b>" + capture + "</b>"
246 }
247 ).replace(
248 /''(.*?)''/g,
249 function(texte, capture)
250 {
251 return "<i>" + capture + "</i>"
252 }
253 )
254 }
255
256 /**
257 * Renvoie une version courte de l'url.
258 * par exemple : http://en.wikipedia.org/wiki/Yakov_Smirnoff devient wikipedia.org
259 */
260 Formateur.prototype.getShort = function(url)
261 {
262 var estUneImage = false
263 var versionShort = null
264 var rechercheImg = this.regexImg.exec(url)
265
266 if (rechercheImg != null)
267 {
268 versionShort = rechercheImg[1].toLowerCase()
269 if (versionShort == "jpeg") versionShort = "jpg" // jpeg -> jpg
270 estUneImage = true
271 }
272 else
273 {
274 var rechercheDomaine = this.regexDomaine.exec(url)
275 if (rechercheDomaine != null && rechercheDomaine.length >= 2)
276 versionShort = rechercheDomaine[1]
277 else
278 {
279 var nomProtocole = this.regexNomProtocole.exec(url)
280 if (nomProtocole != null && nomProtocole.length >= 2)
281 versionShort = nomProtocole[1]
282 }
283 }
284
285 return [versionShort == null ? "url" : versionShort, estUneImage]
286 }
287
288 /**
289 * Traite les pseudo et messages à être affiché dans le titre d'une image visualisé avec lightbox.
290 */
291 Formateur.prototype.traiterPourFenetreLightBox = function(M, urlCourante)
292 {
293 var thisFormateur = this
294 var traitementUrl = function(url)
295 {
296 return "[" + thisFormateur.getShort(url)[0] + (urlCourante == url ? "*" : "") + "]"
297 }
298
299 return this.remplacerBalisesHTML(M).replace(this.regexUrl, traitementUrl)
300 }
301
302
303 ///////////////////////////////////////////////////////////////////////////////////////////////////
304
305 // les statuts possibes du client
306 var statutType = {
307 // mode enregistré, peut poster des messages et modifier son profile
308 auth_registered : 0,
309 // mode identifié, peut poster des messages mais n'a pas accès au profile
310 auth_not_registered : 1,
311 // mode déconnecté, ne peut pas poster de message
312 deconnected : 2
313 }
314
315 function Client(util)
316 {
317 this.util = util
318
319 this.cookie = null
320 this.regexCookie = new RegExp("^cookie=([^;]*)")
321
322 // données personnels
323 this.resetDonneesPersonnelles()
324
325 this.setStatut(statutType.deconnected)
326
327 // si true alors chaque modification du client est mémorisé sur le serveur
328 this.autoflush = $.browser["opera"]
329 }
330
331 Client.prototype.resetDonneesPersonnelles = function()
332 {
333 this.id = 0
334 this.pseudo = euphorik.conf.pseudoDefaut
335 this.login = ""
336 this.password = ""
337 this.email = ""
338 this.css = $("link#cssPrincipale").attr("href")
339 this.chatOrder = "reverse"
340 this.nickFormat = "nick"
341 this.viewTimes = true
342 this.viewTooltips = true
343 this.cookie = undefined
344
345 this.pagePrincipale = 1
346 this.ekMaster = false
347 this.ostentatiousMaster = "light"
348
349 // les conversations, une conversation est un objet possédant les attributs suivants :
350 // - root (entier)
351 // - page (entier)
352 // - reduit (bool)
353 this.conversations = []
354 }
355
356 Client.prototype.setCss = function(css)
357 {
358 if (this.css == css || css == "")
359 return
360
361 this.css = css
362 $("link#cssPrincipale").attr("href", this.css)
363 if (this.autoflush) this.flush(true)
364 }
365
366 Client.prototype.pageSuivante = function(numConv)
367 {
368 if (numConv < 0 && this.pagePrincipale > 1)
369 this.pagePrincipale -= 1
370 else if (this.conversations[numConv].page > 1)
371 this.conversations[numConv].page -= 1
372 }
373
374 Client.prototype.pagePrecedente = function(numConv)
375 {
376 if (numConv < 0)
377 this.pagePrincipale += 1
378 else
379 this.conversations[numConv].page += 1
380 }
381
382 /**
383 * Définit la première page pour la conversation donnée.
384 * @return true si la page a changé sinon false
385 */
386 Client.prototype.goPremierePage = function(numConv)
387 {
388 if (numConv < 0)
389 {
390 if (this.pagePrincipale == 1)
391 return false
392 this.pagePrincipale = 1
393 }
394 else
395 {
396 if (this.conversations[numConv].page == 1)
397 return false
398 this.conversations[numConv].page = 1
399 }
400 return true
401 }
402
403 /**
404 * Ajoute une conversation à la vue de l'utilisateur.
405 * Le profile de l'utilisateur est directement sauvegardé sur le serveur.
406 * @param racines la racine de la conversation (integer)
407 * @return true si la conversation a été créée sinon false (par exemple si la conv existe déjà)
408 */
409 Client.prototype.ajouterConversation = function(racine)
410 {
411 // vérification s'il elle n'existe pas déjà
412 for (var i = 0; i < this.conversations.length; i++)
413 if (this.conversations[i].root == racine)
414 return false
415
416 this.conversations.push({root : racine, page : 1, reduit : false})
417 if (this.autoflush) this.flush(true)
418
419 return true
420 }
421
422 Client.prototype.supprimerConversation = function(num)
423 {
424 if (num < 0 || num >= this.conversations.length) return
425
426 // décalage TODO : supprimer le dernier élément
427 for (var i = num; i < this.conversations.length - 1; i++)
428 this.conversations[i] = this.conversations[i+1]
429 this.conversations.pop()
430
431 if (this.autoflush) this.flush(true)
432 }
433
434 Client.prototype.getJSONLogin = function(login, password)
435 {
436 return {
437 "header" : { "action" : "authentification", "version" : euphorik.conf.versionProtocole },
438 "login" : login,
439 "password" : password
440 }
441 }
442
443 Client.prototype.getJSONLoginCookie = function()
444 {
445 return {
446 "header" : { "action" : "authentification", "version" : euphorik.conf.versionProtocole },
447 "cookie" : this.cookie
448 }
449 }
450
451 /**
452 * le couple (login, password) est facultatif. S'il n'est pas fournit alors il ne sera pas possible
453 * de s'autentifier avec (login, password).
454 */
455 Client.prototype.getJSONEnregistrement = function(login, password)
456 {
457 var mess = { "header" : { "action" : "register", "version" : euphorik.conf.versionProtocole }}
458
459 if (login != undefined && password != undefined)
460 {
461 mess["login"] = login
462 mess["password"] = password
463 }
464
465 return mess;
466 }
467
468 Client.prototype.getJSONConversations = function()
469 {
470 var conversations = new Array()
471 for (var i = 0; i < this.conversations.length; i++)
472 conversations.push({root : this.conversations[i].root, minimized : this.conversations[i].reduit})
473 return conversations
474 }
475
476 Client.prototype.getJSONProfile = function()
477 {
478 return {
479 "header" : { "action" : "set_profile", "version" : euphorik.conf.versionProtocole },
480 "cookie" : this.cookie,
481 "login" : this.login,
482 "password" : this.password,
483 "nick" : this.pseudo,
484 "email" : this.email,
485 "css" : this.css,
486 "chat_order" : this.chatOrder,
487 "nick_format" : this.nickFormat,
488 "view_times" : this.viewTimes,
489 "view_tooltips" : this.viewTooltips,
490 "conversations" : this.getJSONConversations(),
491 "ostentatious_master" : this.ostentatiousMaster
492 }
493 }
494
495 /**
496 * Renvoie null si pas définit.
497 */
498 Client.prototype.getCookie = function()
499 {
500 var cookie = this.regexCookie.exec(document.cookie)
501 if (cookie == null) this.cookie = null
502 else this.cookie = cookie[1]
503 }
504
505 Client.prototype.delCookie = function()
506 {
507 document.cookie = "cookie=; max-age=0"
508 }
509
510 Client.prototype.setCookie = function()
511 {
512 if (this.cookie == null || this.cookie == undefined)
513 return
514
515 // ne fonctionne pas sous IE....
516 /*document.cookie = "cookie=" + this.cookie + "; max-age=" + (60 * 60 * 24 * 365) */
517
518 document.cookie =
519 "cookie="+this.cookie+"; expires=" + new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 365).toUTCString()
520 }
521
522 Client.prototype.authentifie = function()
523 {
524 return this.statut == statutType.auth_registered || this.statut == statutType.auth_not_registered
525 }
526
527 Client.prototype.setStatut = function(statut)
528 {
529 // conversation en "enum" si en "string"
530 if (typeof(statut) == "string")
531 {
532 statut =
533 statut == "auth_registered" ?
534 statutType.auth_registered :
535 (statut == "auth_not_registered" ? statutType.auth_not_registered : statutType.deconnected)
536 }
537
538 if (statut == this.statut) return
539
540 this.statut = statut
541 this.majMenu()
542 this.majLogo()
543 }
544
545 /**
546 * Effectue la connexion vers le serveur.
547 * Cette fonction est bloquante tant que la connexion n'a pas été établie.
548 * S'il existe un cookie en local on s'authentifie directement avec lui.
549 * Si il n'est pas possible de s'authentifier alors on affiche un captcha anti-bot.
550 */
551 Client.prototype.connexionCookie = function()
552 {
553 this.getCookie()
554 if (this.cookie == null) return false;
555 return this.connexion(this.getJSONLoginCookie())
556 }
557
558 Client.prototype.connexionLogin = function(login, password)
559 {
560 return this.connexion(this.getJSONLogin(login, password))
561 }
562
563 Client.prototype.enregistrement = function(login, password)
564 {
565 if (this.authentifie())
566 {
567 this.login = login
568 this.password = password
569 if(this.flush())
570 {
571 this.setStatut(statutType.auth_registered)
572 return true
573 }
574 return false
575 }
576 else
577 {
578 return this.connexion(this.getJSONEnregistrement(login, password))
579 }
580 }
581
582 /**
583 * Connexion. Réalisé de manière synchrone.
584 */
585 Client.prototype.connexion = function(messageJson)
586 {
587 var thisClient = this
588 jQuery.ajax(
589 {
590 async: false,
591 type: "POST",
592 url: "request",
593 dataType: "json",
594 data: this.util.jsonVersAction(messageJson),
595 success:
596 function(data)
597 {
598 if (data["reply"] == "error")
599 {
600 thisClient.util.messageDialogue(data["error_message"])
601 // suppression du cookie actuel, cas où le cookie du client ne permet pas une authentification
602 thisClient.delCookie()
603 }
604 else
605 thisClient.chargerDonnees(data)
606 }
607 }
608 )
609 return this.authentifie()
610 }
611
612 Client.prototype.deconnexion = function()
613 {
614 this.flush(true)
615 this.delCookie()
616 this.resetDonneesPersonnelles()
617 this.setStatut(statutType.deconnected) // deconnexion
618 }
619
620 Client.prototype.chargerDonnees = function(data)
621 {
622 // la modification du statut qui suit met à jour le menu, le menu dépend (page admin)
623 // de l'état ekMaster
624 this.ekMaster = data["ek_master"] != undefined ? data["ek_master"] : false
625
626 this.setStatut(data["status"])
627
628 if (this.authentifie())
629 {
630 this.cookie = data["cookie"]
631 this.setCookie()
632
633 this.id = data["id"]
634 this.login = data["login"]
635 this.pseudo = data["nick"]
636 this.email = data["email"]
637 this.setCss(data["css"])
638 this.chatOrder = data["chat_order"]
639 this.nickFormat = data["nick_format"]
640 this.viewTimes = data["view_times"]
641 this.viewTooltips = data["view_tooltips"]
642 this.ostentatiousMaster = data["ostentatious_master"]
643
644 // la page de la conversation principale
645 this.pagePrincipale = 1
646
647 // les conversations
648 this.conversations = data["conversations"]
649 for (var i = 0; i < this.conversations.length; i++)
650 this.conversations[i] = {root : this.conversations[i].root, page : 1, reduit : this.conversations[i].minimized}
651
652 this.majBulle()
653 this.majCssSelectionee()
654 //this.majLogo()
655 }
656 }
657
658 /**
659 * Met à jour les données personne sur serveur.
660 * @param async de manière asynchrone ? défaut = true
661 * @return false si le flush n'a pas pû se faire sinon true
662 */
663 Client.prototype.flush = function(async)
664 {
665 if (async == undefined)
666 async = false
667
668 if (!this.authentifie())
669 return false
670
671 var thisClient = this
672 var ok = true
673 jQuery.ajax(
674 {
675 async: async,
676 type: "POST",
677 url: "request",
678 dataType: "json",
679 data: this.util.jsonVersAction(this.getJSONProfile()),
680 success:
681 function(data)
682 {
683 if (data["reply"] == "error")
684 {
685 thisClient.util.messageDialogue(data["error_message"])
686 ok = false
687 }
688 else
689 {
690 thisClient.majBulle()
691 }
692 }
693 }
694 )
695
696 return ok
697 }
698
699 Client.prototype.majMenu = function()
700 {
701 var displayType = "block"
702
703 $("#menu .admin").css("display", this.ekMaster ? displayType : "none")
704
705 // met à jour le menu
706 if (this.statut == statutType.auth_registered)
707 {
708 $("#menu .profile").css("display", displayType).text("profile")
709 $("#menu .logout").css("display", displayType)
710 $("#menu .register").css("display", "none")
711 }
712 else if (this.statut == statutType.auth_not_registered)
713 {
714 $("#menu .profile").css("display", "none")
715 $("#menu .logout").css("display", displayType)
716 $("#menu .register").css("display", displayType)
717 }
718 else
719 {
720 $("#menu .profile").css("display", displayType).text("login")
721 $("#menu .logout").css("display", "none")
722 $("#menu .register").css("display", displayType)
723 }
724 }
725
726 /**
727 * Met à jour l'affichage des infos bulles en fonction du profile.
728 */
729 Client.prototype.majBulle = function()
730 {
731 this.util.bulleActive = this.viewTooltips
732 }
733
734 /**
735 * Met à jour la css sélectionnée, lors du chargement des données.
736 */
737 Client.prototype.majCssSelectionee = function()
738 {
739 // extraction du numéro de la css courante
740 var numCssCourante = this.css.match(/^.*?\/(\d)\/.*$/)
741 if (numCssCourante != null && numCssCourante[1] != undefined)
742 {
743 $("#menuCss option").removeAttr("selected")
744 $("#menuCss option[value=" + numCssCourante[1]+ "]").attr("selected", "selected")
745 }
746 }
747
748 /**
749 * Change la "class" du logo en fonction du statut de ekMaster.
750 */
751 Client.prototype.majLogo = function()
752 {
753 if (this.ekMaster)
754 $("#logo").addClass("ekMaster")
755 else
756 $("#logo").removeClass("ekMaster")
757 }
758
759
760 Client.prototype.slap = function(userId, raison)
761 {
762 var thisClient = this
763
764 jQuery.ajax({
765 type: "POST",
766 url: "request",
767 dataType: "json",
768 data: this.util.jsonVersAction(
769 {
770 "header" : { "action" : "slap", "version" : euphorik.conf.versionProtocole },
771 "cookie" : thisClient.cookie,
772 "user_id" : userId,
773 "reason" : raison
774 }),
775 success:
776 function(data)
777 {
778 if (data["reply"] == "error")
779 thisClient.util.messageDialogue(data["error_message"])
780 }
781 })
782 }
783
784 Client.prototype.ban = function(userId, raison, minutes)
785 {
786 var thisClient = this
787
788 // par défaut un ban correspond à 3 jours
789 if (typeof(minutes) == "undefined")
790 minutes = euphorik.conf.tempsBan;
791
792 jQuery.ajax({
793 type: "POST",
794 url: "request",
795 dataType: "json",
796 data: this.util.jsonVersAction(
797 {
798 "header" : { "action" : "ban", "version" : euphorik.conf.versionProtocole },
799 "cookie" : thisClient.cookie,
800 "duration" : minutes,
801 "user_id" : userId,
802 "reason" : raison
803 }),
804 success:
805 function(data)
806 {
807 if (data["reply"] == "error")
808 thisClient.util.messageDialogue(data["error_message"])
809 }
810 })
811 }
812
813 Client.prototype.kick = function(userId, raison)
814 {
815 this.ban(userId, raison, euphorik.conf.tempsKick)
816 }
817
818 ///////////////////////////////////////////////////////////////////////////////////////////////////
819
820 /**
821 * classe permettant de gérer les événements (push serveur).
822 * l'information envoyé est sous la forme :
823 * {
824 * "header" : {"action" : "wait_event", "version" : <v> },
825 * "page" : <page>
826 * [..]
827 * }
828 * l'information reçu est sous la forme :
829 * {
830 * "reply" : <reply>
831 * }
832 * @page la page
833 */
834 function PageEvent(page, util)
835 {
836 this.page = page
837 this.util = util
838
839 // l'objet JSONHttpRequest représentant la connexion d'attente
840 this.attenteCourante = null
841
842 // le multhreading du pauvre, merci javascript de m'offrire autant de primitives pour la gestion de la concurrence...
843 this.stop = false
844 }
845
846 /**
847 * Arrête l'attente courante s'il y en a une.
848 */
849 PageEvent.prototype.stopAttenteCourante = function()
850 {
851 this.stop = true
852
853 if (this.attenteCourante != null)
854 {
855 this.attenteCourante.abort()
856 }
857 }
858
859 /**
860 * Attend un événement lié à la page.
861 * @funSend une fonction renvoyant les données json à envoyer
862 * @funsReceive est un objet comprenant les fonctions à appeler en fonction du "reply"
863 * les fonctions acceptent un paramètre correspondant au données reçues.
864 * exemple : {"new_message" : function(data){ ... }}
865 */
866 PageEvent.prototype.waitEvent = function(funSend, funsReceive)
867 {
868 this.stopAttenteCourante()
869
870 this.stop = false
871
872 var thisPageEvent = this
873
874 // on doit conserver l'ordre des valeurs de l'objet JSON (le serveur les veut dans l'ordre définit dans le protocole)
875 // TODO : ya pas mieux ?
876 var dataToSend =
877 {
878 "header" : { "action" : "wait_event", "version" : euphorik.conf.versionProtocole },
879 "page" : this.page
880 }
881 var poulpe = funSend()
882 for (var v in poulpe)
883 dataToSend[v] = poulpe[v]
884
885 this.attenteCourante = jQuery.ajax({
886 type: "POST",
887 url: "request",
888 dataType: "json",
889 // TODO : doit disparaitre
890 timeout: 180000, // timeout de 3min. Gros HACK pas beau. FIXME problème décrit ici : http://groups.google.com/group/jquery-en/browse_thread/thread/8724e64af3333a76
891 data: this.util.jsonVersAction(dataToSend),
892 success:
893 function(data)
894 {
895 funsReceive[data["reply"]](data)
896
897 // rappel de la fonction dans 100 ms
898 setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funsReceive) }, 100)
899 },
900 error:
901 function(XMLHttpRequest, textStatus, errorThrown)
902 {
903 ;; console.log("Connexion perdue dans waitEvent")
904 setTimeout(function(){ thisPageEvent.waitEvent2(funSend, funsReceive) }, 1000)
905 }
906 })
907 }
908
909 /**
910 * Si un stopAttenteCourante survient un peu n'importe quand il faut imédiatement arreter de boucler.
911 */
912 PageEvent.prototype.waitEvent2 = function(funSend, funsReceive)
913 {
914 if (this.stop)
915 return
916 this.waitEvent(funSend, funsReceive)
917 }
918
919 ///////////////////////////////////////////////////////////////////////////////////////////////////
920
921
922 // le main
923 $(document).ready(
924 function()
925 {
926 var formateur = new Formateur()
927 var util = new euphorik.Util(formateur)
928 var client = new Client(util)
929 var pages = new Pages()
930
931 // connexion vers le serveur (utilise un cookie qui traine)
932 client.connexionCookie()
933
934 $("#menuCss").change(function(){ client.setCss("styles/" + $("option:selected", this).attr("value") + "/euphorik.css")})
935
936 // FIXME : ne fonctionne pas sous opera
937 // voir : http://dev.jquery.com/ticket/2892#preview
938 $(window).unload(function(){client.flush()})
939
940 $("#menu .minichat").click(function(){ pages.afficherPage("minichat") })
941 $("#menu .admin").click(function(){ pages.afficherPage("admin") })
942 $("#menu .profile").click(function(){ pages.afficherPage("profile") })
943 $("#menu .logout").click(function(){
944 util.messageDialogue("Êtes-vous sur de vouloir vous délogger ?", euphorik.Util.messageType.question,
945 {"Oui" : function()
946 {
947 client.deconnexion();
948 pages.afficherPage("minichat", true)
949 },
950 "Non" : function(){}
951 }
952 )
953 })
954 $("#menu .register").click(function(){ pages.afficherPage("register") })
955 $("#menu .about").click(function(){ pages.afficherPage("about") })
956
957 // TODO : simplifier et pouvoir créer des liens par exemple : <span class="lien" href="conditions">Conditions d'utilisation</span>
958 $("#footer .conditions").click(function(){ pages.afficherPage("conditions_utilisation") })
959
960 pages.ajouterPage(new PageMinichat(client, formateur, util))
961 pages.ajouterPage(new PageAdmin(client, formateur, util))
962 pages.ajouterPage(new PageProfile(client, formateur, util))
963 pages.ajouterPage(new PageRegister(client, formateur, util))
964 pages.ajouterPage(new PageAbout(client, formateur, util))
965 pages.ajouterPage("conditions_utilisation")
966
967 pages.afficherPage("minichat")
968 }
969 )