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