]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/irc/ircclient.js
b6224be608c8e0f0c3f6b04f8bcfa251c3c575ec
[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.send("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 if(type == "ACTION") {
302 this.tracker.updateLastSpoke(user.hostToNick(), channel, new Date().getTime());
303 this.newChanLine(channel, "CHANACTION", user, {"m": args, "c": channel, "@": this.getNickStatus(channel, nick)});
304 return;
305 }
306
307 this.newChanLine(channel, "CHANCTCP", user, {"x": type, "m": args, "c": channel, "@": this.getNickStatus(channel, nick)});
308 },
309 userCTCP: function(user, type, args) {
310 var nick = user.hostToNick();
311 var host = user.hostToHost();
312 if(args == undefined)
313 args = "";
314
315 if(type == "ACTION") {
316 this.newQueryWindow(nick);
317 this.newQueryLine(nick, "PRIVACTION", {"m": args, "x": type, "h": host, "n": nick});
318 return;
319 }
320
321 this.newTargetOrActiveLine(nick, "PRIVCTCP", {"m": args, "x": type, "h": host, "n": nick, "-": this.nickname});
322 },
323 userCTCPReply: function(user, type, args) {
324 var nick = user.hostToNick();
325 var host = user.hostToHost();
326 if(args == undefined)
327 args = "";
328
329 this.newTargetOrActiveLine(nick, "CTCPREPLY", {"m": args, "x": type, "h": host, "n": nick, "-": this.nickname});
330 },
331 getNickStatus: function(channel, nick) {
332 var n = this.tracker.getNickOnChannel(nick, channel);
333 if(!$defined(n))
334 return "";
335
336 if(n.prefixes.length == 0)
337 return "";
338
339 return n.prefixes.charAt(0);
340 },
341 channelPrivmsg: function(user, channel, message) {
342 var nick = user.hostToNick();
343
344 this.tracker.updateLastSpoke(nick, channel, new Date().getTime());
345 this.newChanLine(channel, "CHANMSG", user, {"m": message, "@": this.getNickStatus(channel, nick)});
346 },
347 channelNotice: function(user, channel, message) {
348 this.newChanLine(channel, "CHANNOTICE", user, {"m": message, "@": this.getNickStatus(channel, nick)});
349 },
350 userPrivmsg: function(user, message) {
351 var nick = user.hostToNick();
352 var host = user.hostToHost();
353
354 this.newQueryWindow(nick);
355 this.pushLastNick(nick);
356 this.newQueryLine(nick, "PRIVMSG", {"m": message, "h": host, "n": nick});
357 },
358 serverNotice: function(message) {
359 this.newServerLine("SERVERNOTICE", {"m": message});
360 },
361 userNotice: function(user, message) {
362 var nick = user.hostToNick();
363 var host = user.hostToHost();
364
365 this.newTargetOrActiveLine(nick, "PRIVNOTICE", {"m": message, "h": host, "n": nick});
366 },
367 userInvite: function(user, channel) {
368 var nick = user.hostToNick();
369 var host = user.hostToHost();
370
371 this.newServerLine("INVITE", {"c": channel, "h": host, "n": nick});
372 },
373 userMode: function(modes) {
374 this.newServerLine("UMODE", {"m": modes, "n": this.nickname});
375 },
376 channelNames: function(channel, names) {
377 if(names.length == 0) {
378 this.updateNickList(channel);
379 return;
380 }
381
382 names.each(function(nick) {
383 var prefixes = [];
384 var splitnick = nick.split("");
385
386 splitnick.every(function(c, i) {
387 if(this.prefixes.indexOf(c) == -1) {
388 nick = nick.substr(i);
389 return false;
390 }
391
392 prefixes.push(c);
393 return true;
394 }, this);
395
396 var nc = this.tracker.addNickToChannel(nick, channel);
397 prefixes.each(function(p) {
398 this.addPrefix(nc, p);
399 }, this);
400 }, this);
401 },
402 disconnected: function(message) {
403 for(var x in this.windows) {
404 var w = this.windows[x];
405 if(w.type == qwebirc.ui.WINDOW_CHANNEL)
406 w.close();
407 }
408 this.tracker = undefined;
409
410 this.newServerLine("DISCONNECT", {"m": message});
411 },
412 supported: function(key, value) {
413 if(key == "PREFIX") {
414 var l = (value.length - 2) / 2;
415
416 this.modeprefixes = value.substr(1, l);
417 this.prefixes = value.substr(l + 2, l);
418 }
419 },
420 connected: function() {
421 this.newServerLine("CONNECT");
422 },
423 serverError: function(message) {
424 this.newServerLine("ERROR", {"m": message});
425 },
426 quit: function(message) {
427 this.send("QUIT :" + message);
428 this.disconnect();
429 },
430 awayMessage: function(nick, message) {
431 this.newQueryLine(nick, "AWAY", {"n": nick, "m": message});
432 },
433 whois: function(nick, type, data) {
434 var ndata = {"n": nick};
435 var mtype;
436
437 var xsend = function() {
438 this.newTargetOrActiveLine(nick, "WHOIS" + mtype, ndata);
439 }.bind(this);
440
441 if(type == "user") {
442 mtype = "USER";
443 ndata.h = data.ident + "@" + data.hostname;
444 xsend();
445 mtype = "REALNAME";
446 ndata.m = data.realname;
447 } else if(type == "server") {
448 mtype = "SERVER";
449 ndata.x = data.server;
450 ndata.m = data.serverdesc;
451 } else if(type == "oper") {
452 mtype = "OPER";
453 } else if(type == "idle") {
454 mtype = "IDLE";
455 ndata.x = qwebirc.util.longtoduration(data.idle);
456 ndata.m = qwebirc.irc.IRCDate(new Date(data.connected * 1000));
457 } else if(type == "channels") {
458 mtype = "CHANNELS";
459 ndata.m = data.channels;
460 } else if(type == "account") {
461 mtype = "ACCOUNT";
462 ndata.m = data.account;
463 } else if(type == "away") {
464 mtype = "AWAY";
465 ndata.m = data.away;
466 } else if(type == "opername") {
467 mtype = "OPERNAME";
468 ndata.m = data.opername;
469 } else if(type == "actually") {
470 mtype = "ACTUALLY";
471 ndata.m = data.hostname;
472 ndata.x = data.ip;
473 } else if(type == "end") {
474 mtype = "END";
475 } else {
476 return false;
477 }
478
479 xsend();
480 return true;
481 },
482 genericError: function(target, message) {
483 this.newTargetOrActiveLine(target, "GENERICERROR", {m: message, t: target});
484 },
485 genericQueryError: function(target, message) {
486 this.newQueryOrActiveLine(target, "GENERICERROR", {m: message, t: target});
487 },
488 awayStatus: function(state, message) {
489 this.newActiveLine("GENERICMESSAGE", {m: message});
490 },
491 pushLastNick: function(nick) {
492 var i = this.lastNicks.indexOf(nick);
493 if(i != -1) {
494 this.lastNicks.splice(i, 1);
495 } else {
496 if(this.lastNicks.length == this.options.maxnicks)
497 this.lastNicks.pop();
498 }
499 this.lastNicks.unshift(nick);
500 }
501 });