0b77e651306cf45a225f0f860f02a1b68b1e5afa
[euphorik.git] / js / client.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 /*jslint laxbreak:true */
20
21 /**
22 * Object that represents the user.
23 * It carries all the user data like
24 * - ID
25 * - Nick
26 * - E-Mail
27 * - etc..
28 * It can access directly to the DOM if needed. Some examples :
29 * - Changes the menu if the user is logged or not
30 * - Changes the logo if the user is an ek master
31 * - etc..
32 * @util See util.js.
33 * @communication See communication.js.
34 */
35 euphorik.Client = function(util, communication) {
36 this.util = util;
37 this.communication = communication;
38
39 this.cookie = null;
40 this.regexCookie = /cookie=([^;]*)/;
41
42 this.resetPersonalData();
43
44 this.setStatus(euphorik.Client.statusType.disconnected);
45
46 // If true then each data change is flushed to the server.
47 // Active only for opera which doesn't support the unload event.
48 this.autoflush = $.browser.opera;
49 };
50
51 // The three status of a client.
52 euphorik.Client.statusType = {
53 // authentified and registered : The user can post messages and can modify his profile
54 auth_registered : 0,
55 // authentified but not registered : The user can only post messages
56 auth_not_registered : 1,
57 // disconnected (the initial state) : The user cannot post message
58 disconnected : 2
59 };
60
61 /**
62 * Reset all the local personal data. Does not erase the remote data.
63 * This function is used during disconnecting.
64 */
65 euphorik.Client.prototype.resetPersonalData = function() {
66 this.id = 0;
67 this.nick = euphorik.conf.defaultNick;
68 this.login = "";
69 this.password = "";
70 this.email = "";
71 this.css = $("link#mainCss").attr("href");
72 this.chatOrder = "reverse";
73 this.nickFormat = "nick";
74 this.viewTimes = true;
75 this.viewTooltips = true;
76 this.cookie = undefined;
77
78 this.mainConversationPage = 1;
79 this.ekMaster = false;
80 this.ostentatiousMaster = "light";
81
82 // The user opened conversations.
83 // Each conversation object owns theses properties :
84 // - root (integer)
85 // - page (integer)
86 // - isCollapsed (bool)
87 this.conversations = [];
88 };
89
90 /**
91 * Set the CSS. Dynamically change the href to the CSS in the DOM.
92 * @css The relative path to the CSS file, for example :
93 * "styles/1/euphorik.css".
94 */
95 euphorik.Client.prototype.setCss = function(css) {
96 if (this.css === css || !css) {
97 return;
98 }
99
100 this.css = css;
101 $("link#mainCss").attr("href", this.css);
102
103 if (this.autoflush) {
104 this.flush(true);
105 }
106 };
107
108 /**
109 * Change the current page to the next one for the given conversation.
110 * @numConv The number of the conversation.
111 */
112 euphorik.Client.prototype.nextPage = function(numConv) {
113 if (numConv < 0 && this.mainConversationPage > 1) {
114 this.mainConversationPage -= 1;
115 } else if (this.conversations[numConv].page > 1) {
116 this.conversations[numConv].page -= 1;
117 }
118 };
119
120 /**
121 * Change the current page to the previous one for the given conversation.
122 * @numConv The number of the conversation.
123 */
124 euphorik.Client.prototype.previousPage = function(numConv) {
125 if (numConv < 0) {
126 this.mainConversationPage += 1;
127 } else {
128 this.conversations[numConv].page += 1;
129 }
130 };
131
132 /**
133 * Définit la première page pour la conversation donnée.
134 * @return true si la page a changé sinon false
135 */
136 euphorik.Client.prototype.goFirstPage = function(numConv)
137 {
138 if (numConv < 0) {
139 if (this.mainConversationPage === 1) {
140 return false;
141 }
142 this.mainConversationPage = 1;
143 } else {
144 if (this.conversations[numConv].page === 1) {
145 return false;
146 }
147 this.conversations[numConv].page = 1;
148 }
149 return true;
150 };
151
152 /**
153 * Ajoute une conversation à la vue de l'utilisateur.
154 * Le profile de l'utilisateur est directement sauvegardé sur le serveur.
155 * @param racines la racine de la conversation (integer)
156 * @return true si la conversation a été créée sinon false (par exemple si la conv existe déjà)
157 */
158 euphorik.Client.prototype.ajouterConversation = function(racine) {
159 // vérification s'il elle n'existe pas déjà
160 var existe = false;
161 this.conversations.each(function(i, conv) {
162 if (conv.root === racine) {
163 existe = true;
164 }
165 });
166 if (existe) {
167 return false;
168 }
169
170 this.conversations.push({root : racine, page : 1, isCollapsed : false});
171 if (this.autoflush) {
172 this.flush(true);
173 }
174
175 return true;
176 };
177
178 euphorik.Client.prototype.supprimerConversation = function(num) {
179 if (num < 0 || num >= this.conversations.length) {
180 return;
181 }
182
183 // décalage TODO : supprimer le dernier élément
184 for (var i = num; i < this.conversations.length - 1; i++) {
185 this.conversations[i] = this.conversations[i+1];
186 }
187 this.conversations.pop();
188
189 if (this.autoflush) {
190 this.flush(true);
191 }
192 };
193
194 euphorik.Client.prototype.getJSONConversations = function() {
195 var conversations = [];
196 this.conversations.each(function(i, conv) {
197 conversations.push({ "root" : conv.root, "minimized" : conv.isCollapsed });
198 });
199 return conversations;
200 };
201
202 euphorik.Client.prototype.getJSONProfile = function() {
203 return {
204 "cookie" : this.cookie,
205 "login" : this.login,
206 "password" : this.password,
207 "profile" : this.getJSONProfileInfos()
208 };
209 };
210
211 euphorik.Client.prototype.getJSONProfileInfos = function() {
212 return {
213 "nick" : this.nick,
214 "email" : this.email,
215 "css" : this.css,
216 "chat_order" : this.chatOrder,
217 "nick_format" : this.nickFormat,
218 "view_times" : this.viewTimes,
219 "view_tooltips" : this.viewTooltips,
220 "conversations" : this.getJSONConversations(),
221 "ostentatious_master" : this.ostentatiousMaster
222 };
223 };
224
225 /**
226 * .
227 */
228 euphorik.Client.prototype.getCookie = function() {
229 var cookie = this.regexCookie.exec(document.cookie);
230 if (cookie) {
231 this.cookie = cookie[1];
232 } else {
233 this.cookie = undefined;
234 }
235 };
236
237 euphorik.Client.prototype.delCookie = function() {
238 document.cookie = "cookie=; max-age=0";
239 this.cookie = undefined;
240 };
241
242 euphorik.Client.prototype.setCookie = function() {
243 if (!this.cookie) {
244 return;
245 }
246
247 // doesn't work under IE....
248 /*document.cookie = "cookie=" + this.cookie + "; max-age=" + (60 * 60 * 24 * 365) */
249
250 document.cookie =
251 "cookie="+this.cookie+"; expires=" + new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 365).toUTCString();
252 };
253
254 euphorik.Client.prototype.authentifie = function() {
255 return this.statut === euphorik.Client.statusType.auth_registered || this.statut === euphorik.Client.statusType.auth_not_registered;
256 };
257
258 euphorik.Client.prototype.setStatus = function(statut)
259 {
260 // conversation en "enum" si en "string"
261 if (typeof(statut) === "string") {
262 statut =
263 statut === "auth_registered" ?
264 euphorik.Client.statusType.auth_registered :
265 (statut === "auth_not_registered" ? euphorik.Client.statusType.auth_not_registered : euphorik.Client.statusType.disconnected);
266 }
267
268 if (statut === this.statut) {
269 return;
270 }
271
272 this.statut = statut;
273
274 this.majMenu();
275 this.majLogo();
276 };
277
278 /**
279 * Try to authentify the client with the cookie information.
280 * Do nothing if there is no cookie.
281 */
282 euphorik.Client.prototype.connectionCookie = function() {
283 this.getCookie();
284 if (!this.cookie) {
285 return false;
286 }
287 return this.connexion("authentification", { "cookie" : this.cookie });
288 };
289
290 euphorik.Client.prototype.connexionLogin = function(login, password) {
291 return this.connexion("authentification", {"login" : login, "password" : password });
292 };
293
294 euphorik.Client.prototype.enregistrement = function(login, password) {
295 if (this.authentifie()) {
296 this.login = login;
297 this.password = password;
298 if(this.flush()) {
299 this.setStatus(euphorik.Client.statusType.auth_registered);
300 return true;
301 }
302 return false;
303 } else {
304 return this.connexion("register", this.getJSONEnregistrement(login, password));
305 }
306 };
307
308 /**
309 * le couple (login, password) est facultatif. S'il n'est pas fournit alors il ne sera pas possible
310 * de s'autentifier avec (login, password).
311 */
312 euphorik.Client.prototype.getJSONEnregistrement = function(login, password) {
313 var mess = {};
314
315 if (login && password) {
316 mess.login = login;
317 mess.password = password;
318 }
319
320 mess.profile = this.getJSONProfileInfos();
321
322 return mess;
323 };
324
325 /**
326 * Connexion. Réalisé de manière synchrone.
327 */
328 euphorik.Client.prototype.connexion = function(action, messageJson) {
329 var thisClient = this;
330
331 this.communication.requete(
332 action,
333 messageJson,
334 function(data) {
335 thisClient.chargerDonnees(data);
336 },
337 function(data) {
338 thisClient.util.messageDialog(data.error_message);
339 thisClient.delCookie(); // suppression du cookie actuel, cas où le cookie du client ne permet pas une authentification
340 },
341 false
342 );
343 return this.authentifie();
344 };
345
346 euphorik.Client.prototype.disconnect = function() {
347 this.flush(true);
348 this.delCookie();
349 this.resetPersonalData();
350 this.setStatus(euphorik.Client.statusType.disconnected);
351 };
352
353 euphorik.Client.prototype.chargerDonnees = function(data) {
354 // la modification du statut qui suit met à jour le menu, le menu dépend (page admin)
355 // de l'état ekMaster
356 this.ekMaster = data.ek_master ? data.ek_master : false;
357
358 this.setStatus(data.status);
359
360 if (this.authentifie()) {
361 this.cookie = data.cookie;
362 this.setCookie();
363
364 this.id = data.id;
365 this.login = data.login;
366 this.nick = data.profile.nick;
367 this.email = data.profile.email;
368 this.setCss(data.profile.css);
369 this.chatOrder = data.profile.chat_order;
370 this.nickFormat = data.profile.nick_format;
371 this.viewTimes = data.profile.view_times;
372 this.viewTooltips = data.profile.view_tooltips;
373 this.ostentatiousMaster = data.profile.ostentatious_master;
374
375 // la page de la conversation principale
376 this.mainConversationPage = 1;
377
378 // les conversations
379 this.conversations = data.profile.conversations;
380 this.conversations.map(function(conv) {
381 return { root : conv.root, page : 1, isCollapsed : conv.minimized };
382 });
383
384 this.majBulle();
385 this.majCssSelectionee();
386 }
387 };
388
389 /**
390 * Met à jour les données personne sur serveur.
391 * @param async de manière asynchrone ? défaut = true
392 * @return false si le flush n'a pas pû se faire sinon true
393 */
394 euphorik.Client.prototype.flush = function(async) {
395 async = async || false;
396
397 if (!this.authentifie()) {
398 return false;
399 }
400
401 var thisClient = this;
402 var ok = true;
403
404 this.communication.requete(
405 "set_profile",
406 this.getJSONProfile(),
407 function(data) {
408 thisClient.majBulle();
409 },
410 function(data) {
411 thisClient.util.messageDialog(data.error_message);
412 ok = false;
413 },
414 async
415 );
416
417 return ok;
418 };
419
420 euphorik.Client.prototype.majMenu = function() {
421 var displayType = "block";
422
423 $("#menu .admin").css("display", this.ekMaster ? displayType : "none");
424
425 // met à jour le menu
426 if (this.statut === euphorik.Client.statusType.auth_registered) {
427 $("#menu .profile").css("display", displayType).text("profile");
428 $("#menu .logout").css("display", displayType);
429 $("#menu .register").css("display", "none");
430 } else if (this.statut === euphorik.Client.statusType.auth_not_registered) {
431 $("#menu .profile").css("display", "none");
432 $("#menu .logout").css("display", displayType);
433 $("#menu .register").css("display", displayType);
434 } else {
435 $("#menu .profile").css("display", displayType).text("login");
436 $("#menu .logout").css("display", "none");
437 $("#menu .register").css("display", displayType);
438 }
439 };
440
441 /**
442 * Met à jour l'affichage ou non des infos bulles en fonction du profile.
443 */
444 euphorik.Client.prototype.majBulle = function() {
445 this.util.bulleActive = this.viewTooltips;
446 };
447
448 /**
449 * Met à jour la css sélectionnée, lors du chargement des données.
450 */
451 euphorik.Client.prototype.majCssSelectionee = function() {
452 // extraction du numéro de la css courante
453 var numCssCourante = this.css.match(/^.*?\/(\d)\/.*$/);
454 if (numCssCourante && numCssCourante[1]) {
455 $("#menuCss option").removeAttr("selected");
456 $("#menuCss option[value=" + numCssCourante[1]+ "]").attr("selected", "selected");
457 }
458 };
459
460 /**
461 * Change la "class" du logo en fonction du statut de ekMaster.
462 */
463 euphorik.Client.prototype.majLogo = function() {
464 if (this.ekMaster) {
465 $("#logo").addClass("ekMaster");
466 } else {
467 $("#logo").removeClass("ekMaster");
468 }
469 };
470
471 euphorik.Client.prototype.slap = function(userId, raison) {
472 var thisClient = this;
473 this.communication.requete("slap", { "cookie" : thisClient.cookie, "user_id" : userId, "reason" : raison });
474 };
475
476 euphorik.Client.prototype.ban = function(userId, raison, minutes) {
477 var thisClient = this;
478
479 // par défaut un ban correspond à 3 jours
480 minutes = minutes || euphorik.conf.tempsBan;
481 this.communication.requete("ban", { "cookie" : thisClient.cookie, "duration" : minutes, "user_id" : userId, "reason" : raison });
482 };
483
484 euphorik.Client.prototype.kick = function(userId, raison) {
485 this.ban(userId, raison, euphorik.conf.tempsKick);
486 };