]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/irc/ircclient.js
Add status display in channel text.
[irc/quakenet/qwebirc.git] / js / irc / ircclient.js
1 qwebirc.irc.IRCClient = new Class({
2 Extends: qwebirc.irc.BaseIRCClient,
3 options: {
4 nickname: "qwebirc",
5 autojoin: "",
6 maxnicks: 10
7 },
8 initialize: function(options, ui) {
9 this.parent(options);
10
11 this.ui = ui;
12
13 this.prefixes = "@+";
14 this.modeprefixes = "ov";
15 this.windows = {};
16
17 this.commandparser = new qwebirc.irc.CommandParser(this);
18 this.exec = this.commandparser.dispatch.bind(this.commandparser);
19
20 this.statusWindow = this.ui.newClient(this);
21 this.lastNicks = [];
22 },
23 newLine: function(window, type, data) {
24 if(!data)
25 data = {};
26
27 var w = this.getWindow(window);
28 if(w) {
29 w.addLine(type, data);
30 } else {
31 this.statusWindow.addLine(type, data);
32 }
33 },
34 newChanLine: function(channel, type, user, extra) {
35 if(!extra)
36 extra = {};
37
38 extra["n"] = user.hostToNick();
39 extra["h"] = user.hostToHost();
40 extra["c"] = channel;
41 extra["-"] = this.nickname;
42
43 this.newLine(channel, type, extra);
44 },
45 newServerLine: function(type, data) {
46 this.statusWindow.addLine(type, data);
47 },
48 newActiveLine: function(type, data) {
49 this.getActiveWindow().addLine(type, data);
50 },
51 newTargetOrActiveLine: function(target, type, data) {
52 if(this.getWindow(target)) {
53 this.newLine(target, type, data);
54 } else {
55 this.newActiveLine(type, data);
56 }
57 },
58 updateNickList: function(channel) {
59 var n1 = this.tracker.getChannel(channel);
60 var names = new Array();
61 var tff = String.fromCharCode(255);
62 var nh = {}
63
64 /* MEGAHACK */
65 for(var n in n1) {
66 var nc = n1[n];
67 var nx;
68
69 if(nc.prefixes.length > 0) {
70 var c = nc.prefixes.charAt(0);
71 nx = String.fromCharCode(this.prefixes.indexOf(c)) + n.toIRCLower();
72 nh[nx] = c + n;
73 } else {
74 nx = tff + n.toIRCLower();
75 nh[nx] = n;
76 }
77 names.push(nx);
78 };
79
80 names.sort();
81
82 var sortednames = new Array();
83 names.each(function(name) {
84 sortednames.push(nh[name]);
85 });
86
87 var w = this.getWindow(channel);
88 if(w)
89 w.updateNickList(sortednames);
90 },
91 getWindow: function(name) {
92 return this.windows[name];
93 },
94 newWindow: function(name, type, select) {
95 var w = this.getWindow(name);
96 if(!w) {
97 w = this.windows[name] = this.ui.newWindow(this, type, name);
98
99 w.addEvent("close", function(w) {
100 delete this.windows[name];
101 }.bind(this));
102 }
103
104 if(select)
105 this.ui.selectWindow(w);
106
107 return w;
108 },
109 newQueryWindow: function(name) {
110 if(this.ui.uiOptions.DEDICATED_MSG_WINDOW)
111 return this.ui.newWindow(this, qwebirc.ui.WINDOW_MESSAGES, "Messages");
112 return this.newWindow(name, qwebirc.ui.WINDOW_QUERY, false);
113 },
114 newQueryLine: function(window, type, data, active) {
115 if(this.getWindow(window))
116 return this.newLine(window, type, data);
117
118 var w = this.ui.getWindow(this, qwebirc.ui.WINDOW_MESSAGES);
119
120 if(this.ui.uiOptions.DEDICATED_MSG_WINDOW && w) {
121 return w.addLine(type, data);
122 } else {
123 if(active) {
124 return this.newActiveLine(window, type, data);
125 } else {
126 return this.newLine(window, type, data);
127 }
128 }
129 },
130 newQueryOrActiveLine: function(window, type, data) {
131 this.newQueryLine(window, type, data, true);
132 },
133 getActiveWindow: function() {
134 return this.ui.getActiveIRCWindow(this);
135 },
136 getNickname: function() {
137 return this.nickname;
138 },
139 addPrefix: function(nickchanentry, prefix) {
140 var ncp = nickchanentry.prefixes + prefix;
141 var prefixes = [];
142
143 /* O(n^2) */
144 for(var i=0;i<this.prefixes.length;i++) {
145 var pc = this.prefixes.charAt(i);
146 var index = ncp.indexOf(pc);
147 if(index != -1)
148 prefixes.push(pc);
149 }
150
151 nickchanentry.prefixes = prefixes.join("");
152 },
153 stripPrefix: function(nick) {
154 var l = nick.charAt(0);
155 if(!l)
156 return nick;
157
158 if(this.prefixes.indexOf(l) != -1)
159 return nick.substring(1);
160
161 return nick;
162 },
163 removePrefix: function(nickchanentry, prefix) {
164 nickchanentry.prefixes = nickchanentry.prefixes.replaceAll(prefix, "");
165 },
166
167 /* from here down are events */
168 rawNumeric: function(numeric, prefix, params) {
169 this.newServerLine("RAW", {"n": "numeric", "m": params.slice(1).join(" ")});
170 },
171 signedOn: function(nickname) {
172 this.tracker = new qwebirc.irc.IRCTracker();
173 this.nickname = nickname;
174 this.newServerLine("SIGNON");
175
176 if(this.options.autojoin)
177 this.send("JOIN " + this.options.autojoin);
178 },
179 userJoined: function(user, channel) {
180 var nick = user.hostToNick();
181 var host = user.hostToHost();
182
183 if((nick == this.nickname) && !this.getWindow(channel))
184 this.newWindow(channel, qwebirc.ui.WINDOW_CHANNEL, true);
185 this.tracker.addNickToChannel(nick, channel);
186
187 if(nick == this.nickname) {
188 this.newChanLine(channel, "OURJOIN", user);
189 } else {
190 this.newChanLine(channel, "JOIN", user);
191 }
192 this.updateNickList(channel);
193 },
194 userPart: function(user, channel, message) {
195 var nick = user.hostToNick();
196 var host = user.hostToHost();
197
198 if(nick == this.nickname) {
199 this.tracker.removeChannel(channel);
200 } else {
201 this.tracker.removeNickFromChannel(nick, channel);
202 this.newChanLine(channel, "PART", user, {"m": message});
203 }
204
205 this.updateNickList(channel);
206
207 if(nick == this.nickname) {
208 var w = this.getWindow(channel)
209 if(w)
210 w.close();
211 }
212 },
213 userKicked: function(kicker, channel, kickee, message) {
214 if(kickee == this.nickname) {
215 this.tracker.removeChannel(channel);
216 this.getWindow(channel).close();
217 } else {
218 this.tracker.removeNickFromChannel(kickee, channel);
219 this.updateNickList(channel);
220 }
221
222 this.newChanLine(channel, "KICK", kicker, {"v": kickee, "m": message});
223 },
224 channelMode: function(user, channel, modes, raw) {
225 modes.each(function(mo) {
226 var direction = mo[0];
227 var mode = mo[1];
228
229 var prefixindex = this.modeprefixes.indexOf(mode);
230 if(prefixindex == -1)
231 return;
232
233 var nick = mo[2];
234 var prefixchar = this.prefixes.charAt(prefixindex);
235
236 var nc = this.tracker.getOrCreateNickOnChannel(nick, channel);
237 if(direction == "-") {
238 this.removePrefix(nc, prefixchar);
239 } else {
240 this.addPrefix(nc, prefixchar);
241 }
242 }, this);
243
244 this.newChanLine(channel, "MODE", user, {"m": raw.join(" ")});
245
246 this.updateNickList(channel);
247 },
248 userQuit: function(user, message) {
249 var nick = user.hostToNick();
250
251 var channels = this.tracker.getNick(nick);
252
253 var clist = [];
254 for(var c in channels) {
255 clist.push(c);
256 this.newChanLine(c, "QUIT", user, {"m": message});
257 }
258
259 this.tracker.removeNick(nick);
260
261 clist.each(function(cli) {
262 this.updateNickList(cli);
263 }, this);
264 },
265 nickChanged: function(user, newnick) {
266 var oldnick = user.hostToNick();
267
268 if(oldnick == this.nickname)
269 this.nickname = newnick;
270
271 this.tracker.renameNick(oldnick, newnick);
272
273 var channels = this.tracker.getNick(newnick);
274 var found = false;
275
276 for(var c in channels) {
277 var found = true;
278
279 this.newChanLine(c, "NICK", user, {"w": newnick});
280 /* TODO: rename queries */
281 this.updateNickList(c);
282 }
283
284 if(!found)
285 this.newChanLine(undefined, "NICK", user, {"w": newnick});
286 },
287 channelTopic: function(user, channel, topic) {
288 this.newChanLine(channel, "TOPIC", user, {"m": topic});
289 this.getWindow(channel).updateTopic(topic);
290 },
291 initialTopic: function(channel, topic) {
292 this.getWindow(channel).updateTopic(topic);
293 },
294 channelCTCP: function(user, channel, type, args) {
295 if(args == undefined)
296 args = "";
297
298 if(type == "ACTION") {
299 this.tracker.updateLastSpoke(user.hostToNick(), channel, new Date().getTime());
300 this.newChanLine(channel, "CHANACTION", user, {"m": args, "c": channel});
301 return;
302 }
303
304 this.newChanLine(channel, "CHANCTCP", user, {"x": type, "m": args, "c": channel});
305 },
306 userCTCP: function(user, type, args) {
307 var nick = user.hostToNick();
308 var host = user.hostToHost();
309 if(args == undefined)
310 args = "";
311
312 if(type == "ACTION") {
313 this.newQueryWindow(nick);
314 this.newQueryLine(nick, "PRIVACTION", {"m": args, "x": type, "h": host, "n": nick});
315 return;
316 }
317
318 this.newTargetOrActiveLine(nick, "PRIVCTCP", {"m": args, "x": type, "h": host, "n": nick, "-": this.nickname});
319 },
320 userCTCPReply: function(user, type, args) {
321 var nick = user.hostToNick();
322 var host = user.hostToHost();
323 if(args == undefined)
324 args = "";
325
326 this.newTargetOrActiveLine(nick, "CTCPREPLY", {"m": args, "x": type, "h": host, "n": nick, "-": this.nickname});
327 },
328 getNickStatus: function(channel, nick) {
329 var n = this.tracker.getNickOnChannel(nick, channel);
330 if(!$defined(n))
331 return "";
332
333 if(n.prefixes.length == 0)
334 return "";
335
336 return n.prefixes.charAt(0);
337 },
338 channelPrivmsg: function(user, channel, message) {
339 var nick = user.hostToNick();
340
341 this.tracker.updateLastSpoke(nick, channel, new Date().getTime());
342 this.newChanLine(channel, "CHANMSG", user, {"m": message, "@": this.getNickStatus(channel, nick)});
343 },
344 channelNotice: function(user, channel, message) {
345 this.newChanLine(channel, "CHANNOTICE", user, {"m": message});
346 },
347 userPrivmsg: function(user, message) {
348 var nick = user.hostToNick();
349 var host = user.hostToHost();
350
351 this.newQueryWindow(nick);
352 this.pushLastNick(nick);
353 this.newQueryLine(nick, "PRIVMSG", {"m": message, "h": host, "n": nick});
354 },
355 serverNotice: function(message) {
356 this.newServerLine("SERVERNOTICE", {"m": message});
357 },
358 userNotice: function(user, message) {
359 var nick = user.hostToNick();
360 var host = user.hostToHost();
361
362 this.newTargetOrActiveLine(nick, "PRIVNOTICE", {"m": message, "h": host, "n": nick});
363 },
364 userInvite: function(user, channel) {
365 var nick = user.hostToNick();
366 var host = user.hostToHost();
367
368 this.newServerLine("INVITE", {"c": channel, "h": host, "n": nick});
369 },
370 userMode: function(modes) {
371 this.newServerLine("UMODE", {"m": modes, "n": this.nickname});
372 },
373 channelNames: function(channel, names) {
374 if(names.length == 0) {
375 this.updateNickList(channel);
376 return;
377 }
378
379 names.each(function(nick) {
380 var prefixes = [];
381 var splitnick = nick.split("");
382
383 splitnick.every(function(c, i) {
384 if(this.prefixes.indexOf(c) == -1) {
385 nick = nick.substr(i);
386 return false;
387 }
388
389 prefixes.push(c);
390 return true;
391 }, this);
392
393 var nc = this.tracker.addNickToChannel(nick, channel);
394 prefixes.each(function(p) {
395 this.addPrefix(nc, p);
396 }, this);
397 }, this);
398 },
399 disconnected: function(message) {
400 for(var x in this.windows) {
401 var w = this.windows[x];
402 if(w.type == qwebirc.ui.WINDOW_CHANNEL)
403 w.close();
404 }
405 this.tracker = undefined;
406
407 this.newServerLine("DISCONNECT", {"m": message});
408 },
409 supported: function(key, value) {
410 if(key == "PREFIX") {
411 var l = (value.length - 2) / 2;
412
413 this.modeprefixes = value.substr(1, l);
414 this.prefixes = value.substr(l + 2, l);
415 }
416 },
417 connected: function() {
418 this.newServerLine("CONNECT");
419 },
420 serverError: function(message) {
421 this.newServerLine("ERROR", {"m": message});
422 },
423 quit: function(message) {
424 this.send("QUIT :" + message);
425 this.disconnect();
426 },
427 awayMessage: function(nick, message) {
428 this.newQueryLine(nick, "AWAY", {"n": nick, "m": message});
429 },
430 whois: function(nick, type, data) {
431 var ndata = {"n": nick};
432 var mtype;
433
434 var xsend = function() {
435 this.newTargetOrActiveLine(nick, "WHOIS" + mtype, ndata);
436 }.bind(this);
437
438 if(type == "user") {
439 mtype = "USER";
440 ndata.h = data.ident + "@" + data.hostname;
441 xsend();
442 mtype = "REALNAME";
443 ndata.m = data.realname;
444 } else if(type == "server") {
445 mtype = "SERVER";
446 ndata.x = data.server;
447 ndata.m = data.serverdesc;
448 } else if(type == "oper") {
449 mtype = "OPER";
450 } else if(type == "idle") {
451 mtype = "IDLE";
452 ndata.x = qwebirc.util.longtoduration(data.idle);
453 ndata.m = qwebirc.irc.IRCDate(new Date(data.connected * 1000));
454 } else if(type == "channels") {
455 mtype = "CHANNELS";
456 ndata.m = data.channels;
457 } else if(type == "account") {
458 mtype = "ACCOUNT";
459 ndata.m = data.account;
460 } else if(type == "away") {
461 mtype = "AWAY";
462 ndata.m = data.away;
463 } else if(type == "opername") {
464 mtype = "OPERNAME";
465 ndata.m = data.opername;
466 } else if(type == "actually") {
467 mtype = "ACTUALLY";
468 ndata.m = data.hostname;
469 ndata.x = data.ip;
470 } else if(type == "end") {
471 mtype = "END";
472 } else {
473 return false;
474 }
475
476 xsend();
477 return true;
478 },
479 genericError: function(target, message) {
480 this.newTargetOrActiveLine(target, "GENERICERROR", {m: message, t: target});
481 },
482 genericQueryError: function(target, message) {
483 this.newQueryOrActiveLine(target, "GENERICERROR", {m: message, t: target});
484 },
485 awayStatus: function(state, message) {
486 this.newActiveLine("GENERICMESSAGE", {m: message});
487 },
488 pushLastNick: function(nick) {
489 var i = this.lastNicks.indexOf(nick);
490 if(i != -1) {
491 this.lastNicks.splice(i, 1);
492 } else {
493 if(this.lastNicks.length == this.options.maxnicks)
494 this.lastNicks.pop();
495 }
496 this.lastNicks.unshift(nick);
497 }
498 });