]> jfr.im git - irc/quakenet/qwebirc.git/blob - static/js/irc/ircclient.js
16e2b727d9bbfeb20fd969380a2429ad5241561a
[irc/quakenet/qwebirc.git] / static / js / irc / ircclient.js
1 function IRCClient(nickname, ui, autojoin) {
2 var self = this;
3 this.prefixes = "@+";
4 this.modeprefixes = "ov";
5 this.windows = {};
6
7 var newLine = function(window, type, data) {
8 if(!data)
9 data = {};
10
11 var w = self.getWindow(window);
12 if(w) {
13 w.addLine(type, data);
14 } else {
15 self.statusWindow.addLine(type, data);
16 }
17 }
18 this.newLine = newLine;
19
20 var newChanLine = function(channel, type, user, extra) {
21 if(!extra)
22 extra = {};
23
24 extra["n"] = hosttonick(user);
25 extra["h"] = hosttohost(user);
26 extra["c"] = channel;
27 extra["-"] = self.nickname;
28
29 newLine(channel, type, extra);
30 }
31
32 var newServerLine = function(type, data) {
33 self.statusWindow.addLine(type, data);
34 }
35
36 var newActiveLine = function(type, data) {
37 ui.getActiveWindow().addLine(type, data);
38 }
39
40 this.rawNumeric = function(numeric, prefix, params) {
41 newServerLine("RAW", {"n": "numeric", "m": params.slice(1).join(" ")});
42 }
43
44 this.signedOn = function(nickname) {
45 self.tracker = new IRCTracker();
46 self.nickname = nickname;
47 newServerLine("SIGNON");
48
49 if(autojoin)
50 self.__send("JOIN " + autojoin);
51 }
52
53 this.updateNickList = function(channel) {
54 var n1 = self.tracker.getChannel(channel);
55 var names = new Array();
56 var tff = String.fromCharCode(255);
57 var nh = {}
58
59 /* MEGAHACK */
60 for(var n in n1) {
61 var nc = n1[n];
62 var nx;
63
64 if(nc.prefixes.length > 0) {
65 var c = nc.prefixes.charAt(0);
66 nx = String.fromCharCode(self.prefixes.indexOf(c)) + toIRCLower(n);
67 nh[nx] = c + n;
68 } else {
69 nx = tff + toIRCLower(n);
70 nh[nx] = n;
71 }
72 names.push(nx);
73 };
74
75 names.sort();
76
77 var sortednames = new Array();
78 forEach(names, function(name) {
79 sortednames.push(nh[name]);
80 });
81
82 var w = self.getWindow(channel);
83 if(w)
84 w.updateNickList(sortednames);
85 }
86
87 this.getWindow = function(name) {
88 return self.windows[name];
89 }
90
91 this.newWindow = function(name, type, select) {
92 var w = self.getWindow(name);
93 if(!w) {
94 w = self.windows[name] = ui.newWindow(self, type, name);
95
96 w.addEvent("close", function(w) {
97 delete self.windows[name];
98 });
99 }
100
101 if(select)
102 ui.selectWindow(w);
103
104 return w;
105 }
106
107 this.userJoined = function(user, channel) {
108 var nick = hosttonick(user);
109 var host = hosttohost(user);
110
111 if((nick == self.nickname) && !self.getWindow(channel))
112 self.newWindow(channel, WINDOW_CHANNEL, true);
113 self.tracker.addNickToChannel(nick, channel);
114
115 newChanLine(channel, "JOIN", user);
116
117 self.updateNickList(channel);
118 }
119
120 this.userPart = function(user, channel, message) {
121 var nick = hosttonick(user);
122 var host = hosttohost(user);
123
124 if(nick == self.nickname) {
125 self.tracker.removeChannel(channel);
126 } else {
127 self.tracker.removeNickFromChannel(nick, channel);
128 newChanLine(channel, "PART", user, {"m": message});
129 }
130
131 self.updateNickList(channel);
132
133 var w = self.getWindow(channel)
134 if(w)
135 w.close();
136 }
137
138 this.userKicked = function(kicker, channel, kickee, message) {
139 if(kickee == self.nickname) {
140 self.tracker.removeChannel(channel);
141 self.getWindow(channel).close();
142 } else {
143 self.tracker.removeNickFromChannel(kickee, channel);
144 self.updateNickList(channel);
145 }
146
147 newChanLine(channel, "KICK", kicker, {"v": kickee, "m": message});
148 }
149
150 this.channelMode = function(user, channel, modes, raw) {
151 forEach(modes, function(mo) {
152 var direction = mo[0];
153 var mode = mo[1];
154
155 var prefixindex = self.modeprefixes.indexOf(mode);
156 if(prefixindex == -1)
157 return;
158
159 var nick = mo[2];
160 var prefixchar = self.prefixes.charAt(prefixindex);
161
162 var nc = self.tracker.getOrCreateNickOnChannel(nick, channel);
163 if(direction == "-") {
164 self.removePrefix(nc, prefixchar);
165 } else {
166 self.addPrefix(nc, prefixchar);
167 }
168 });
169
170 newChanLine(channel, "MODE", user, {"m": raw.join(" ")});
171
172 self.updateNickList(channel);
173 }
174
175 this.userQuit = function(user, message) {
176 var nick = hosttonick(user);
177
178 var channels = self.tracker.getNick(nick);
179
180 var clist = [];
181 for(var c in channels) {
182 clist.push(c);
183 newChanLine(c, "QUIT", user, {"m": message});
184 }
185
186 self.tracker.removeNick(nick);
187
188 forEach(clist, function(cli) {
189 self.updateNickList(cli);
190 });
191 }
192
193 this.nickChanged = function(user, newnick) {
194 var oldnick = hosttonick(user);
195
196 if(oldnick == self.nickname)
197 self.nickname = newnick;
198
199 self.tracker.renameNick(oldnick, newnick);
200
201 var channels = self.tracker.getNick(newnick);
202
203 for(var c in channels) {
204 newChanLine(c, "NICK", user, {"w": newnick});
205 /* TODO: rename queries */
206 self.updateNickList(c);
207 }
208 }
209
210 this.channelTopic = function(user, channel, topic) {
211 newChanLine(channel, "TOPIC", user, {"m": topic});
212 self.getWindow(channel).updateTopic(topic);
213 }
214
215 this.initialTopic = function(channel, topic) {
216 self.getWindow(channel).updateTopic(topic);
217 }
218
219 this.channelCTCP = function(user, channel, type, args) {
220 if(args == undefined)
221 args = "";
222
223 if(type == "ACTION") {
224 newChanLine(channel, "CHANACTION", user, {"m": args, "c": channel});
225 return;
226 }
227
228 newChanLine(channel, "CHANCTCP", user, {"x": type, "m": args, "c": channel});
229 }
230
231 this.userCTCP = function(user, type, args) {
232 var nick = hosttonick(user);
233 var host = hosttohost(user);
234 if(args == undefined)
235 args = "";
236
237 if(type == "ACTION") {
238 self.newWindow(nick, WINDOW_QUERY);
239 newLine(nick, "PRIVACTION", {"m": args, "x": type, "h": host, "n": nick});
240 return;
241 }
242
243 if(self.getWindow(nick)) {
244 newLine(nick, "PRIVCTCP", {"m": args, "x": type, "h": host, "n": nick, "-": self.nickname});
245 } else {
246 newActiveLine("PRIVCTCP", {"m": args, "x": type, "h": host, "n": nick, "-": self.nickname});
247 }
248 }
249
250 this.userCTCPReply = function(user, type, args) {
251 var nick = hosttonick(user);
252 var host = hosttohost(user);
253 if(args == undefined)
254 args = "";
255
256 if(self.getWindow(nick)) {
257 newLine(nick, "CTCPREPLY", {"m": args, "x": type, "h": host, "n": nick, "-": self.nickname});
258 } else {
259 newActiveLine("CTCPREPLY", {"m": args, "x": type, "h": host, "n": nick, "-": self.nickname});
260 }
261 }
262
263 this.channelPrivmsg = function(user, channel, message) {
264 newChanLine(channel, "CHANMSG", user, {"m": message});
265 }
266
267 this.channelNotice = function(user, channel, message) {
268 newChanLine(channel, "CHANNOTICE", user, {"m": message});
269 }
270
271 this.userPrivmsg = function(user, message) {
272 var nick = hosttonick(user);
273 var host = hosttohost(user);
274
275 self.newWindow(nick, WINDOW_QUERY);
276
277 newLine(nick, "PRIVMSG", {"m": message, "h": host, "n": nick});
278 }
279
280 this.serverNotice = function(message) {
281 newServerLine("SERVERNOTICE", {"m": message});
282 }
283
284 this.userNotice = function(user, message) {
285 var nick = hosttonick(user);
286 var host = hosttohost(user);
287
288 if(self.getWindow(nick)) {
289 newLine(nick, "PRIVNOTICE", {"m": message, "h": host, "n": nick});
290 } else {
291 newActiveLine("PRIVNOTICE", {"m": message, "h": host, "n": nick});
292 }
293 }
294
295 this.userInvite = function(user, channel) {
296 var nick = hosttonick(user);
297 var host = hosttohost(user);
298
299 newServerLine("INVITE", {"c": channel, "h": host, "n": nick});
300 }
301
302 this.userMode = function(modes) {
303 newServerLine("UMODE", {"m": modes, "n": self.nickname});
304 }
305
306 this.addPrefix = function(nickchanentry, prefix) {
307 var ncp = nickchanentry.prefixes + prefix;
308 var prefixes = new Array();
309
310 /* O(n^2) */
311 for(var i=0;i<self.prefixes.length;i++) {
312 var pc = self.prefixes.charAt(i);
313 var index = ncp.indexOf(pc);
314 if(index != -1)
315 prefixes.push(pc);
316 }
317
318 nickchanentry.prefixes = prefixes.join("");
319 }
320
321 this.removePrefix = function(nickchanentry, prefix) {
322 nickchanentry.prefixes = nickchanentry.prefixes.replaceAll(prefix, "");
323 }
324
325 this.channelNames = function(channel, names) {
326 if(names.length == 0) {
327 self.updateNickList(channel);
328 return;
329 }
330
331 forEach(names, function(nick) {
332 var prefixes = [];
333 var splitnick = nick.split("");
334
335 var i = 0;
336 forEach(splitnick, function(c) {
337 if(self.prefixes.indexOf(c) == -1) {
338 nick = nick.substr(i);
339 return true;
340 }
341
342 prefixes.push(c);
343 i++;
344 });
345
346 var nc = self.tracker.addNickToChannel(nick, channel);
347 forEach(prefixes, function(p) {
348 self.addPrefix(nc, p);
349 });
350 });
351 }
352
353 this.disconnected = function() {
354 for(var x in this.parent.channels) {
355 ui.closeWindow(x);
356 }
357
358 self.tracker = undefined;
359
360 newServerLine("DISCONNECT");
361 self.disconnect();
362 }
363
364 this.supported = function(key, value) {
365 if(key == "PREFIX") {
366 var l = (value.length - 2) / 2;
367
368 self.modeprefixes = value.substr(1, l);
369 self.prefixes = value.substr(l + 2, l);
370 }
371 }
372
373 this.connected = function() {
374 newServerLine("CONNECT");
375 }
376
377 this.serverError = function(message) {
378 newServerLine("ERROR", {"m": message});
379 }
380
381 this.getActiveWindow = function() {
382 return ui.getActiveWindow();
383 }
384
385 this.getNickname = function() {
386 return self.nickname;
387 }
388
389 this.parent = new BaseIRCClient(nickname, this);
390 this.__send = this.parent.send;
391
392 this.commandparser = new CommandParser(this);
393 this.dispatch = this.commandparser.dispatch
394
395 this.statusWindow = ui.newClient(self);
396
397 this.connect = this.parent.connect;
398 this.disconnect = this.parent.disconnect;
399 }