Second rev. es-la
[KiwiIRC.git] / client / assets / src / models / network.js
1 (function () {
2
3 _kiwi.model.Network = Backbone.Model.extend({
4 defaults: {
5 connection_id: 0,
6 /**
7 * The name of the network
8 * @type String
9 */
10 name: 'Network',
11
12 /**
13 * The address (URL) of the network
14 * @type String
15 */
16 address: '',
17
18 /**
19 * The current nickname
20 * @type String
21 */
22 nick: '',
23
24 /**
25 * The channel prefix for this network
26 * @type String
27 */
28 channel_prefix: '#',
29
30 /**
31 * The user prefixes for channel owner/admin/op/voice etc. on this network
32 * @type Array
33 */
34 user_prefixes: ['~', '&', '@', '+']
35 },
36
37
38 initialize: function () {
39 this.gateway = _kiwi.global.components.Network(this.get('connection_id'));
40 this.bindGatewayEvents();
41
42 // Create our panel list (tabs)
43 this.panels = new _kiwi.model.PanelList([], this);
44 //this.panels.network = this;
45
46 // Automatically create a server tab
47 var server_panel = new _kiwi.model.Server({name: 'Server'});
48 this.panels.add(server_panel);
49 this.panels.server = this.panels.active = server_panel;
50 },
51
52
53 bindGatewayEvents: function () {
54 //this.gateway.on('all', function() {console.log('ALL', this.get('connection_id'), arguments);});
55
56 this.gateway.on('connect', onConnect, this);
57 this.gateway.on('disconnect', onDisconnect, this);
58
59 this.gateway.on('nick', function(event) {
60 if (event.nick === this.get('nick')) {
61 this.set('nick', event.newnick);
62 }
63 }, this);
64
65 this.gateway.on('options', onOptions, this);
66 this.gateway.on('motd', onMotd, this);
67 this.gateway.on('join', onJoin, this);
68 this.gateway.on('part', onPart, this);
69 this.gateway.on('quit', onQuit, this);
70 this.gateway.on('kick', onKick, this);
71 this.gateway.on('msg', onMsg, this);
72 this.gateway.on('nick', onNick, this);
73 this.gateway.on('ctcp_request', onCtcpRequest, this);
74 this.gateway.on('ctcp_response', onCtcpResponse, this);
75 this.gateway.on('notice', onNotice, this);
76 this.gateway.on('action', onAction, this);
77 this.gateway.on('topic', onTopic, this);
78 this.gateway.on('topicsetby', onTopicSetBy, this);
79 this.gateway.on('userlist', onUserlist, this);
80 this.gateway.on('userlist_end', onUserlistEnd, this);
81 this.gateway.on('mode', onMode, this);
82 this.gateway.on('whois', onWhois, this);
83 this.gateway.on('whowas', onWhowas, this);
84 this.gateway.on('away', onAway, this);
85 this.gateway.on('list_start', onListStart, this);
86 this.gateway.on('irc_error', onIrcError, this);
87 },
88
89
90 /**
91 * Create panels and join the channel
92 * This will not wait for the join event to create a panel. This
93 * increases responsiveness in case of network lag
94 */
95 createAndJoinChannels: function (channels) {
96 var that = this,
97 panels = [];
98
99 // Multiple channels may come as comma-delimited
100 if (typeof channels === 'string') {
101 channels = channels.split(',');
102 }
103
104 $.each(channels, function (index, channel_name_key) {
105 // We may have a channel key so split it off
106 var spli = channel_name_key.trim().split(' '),
107 channel_name = spli[0],
108 channel_key = spli[1] || '';
109
110 // Trim any whitespace off the name
111 channel_name = channel_name.trim();
112
113 // If not a valid channel name, display a warning
114 if (!_kiwi.app.isChannelName(channel_name)) {
115 that.panels.server.addMsg('', _kiwi.global.i18n.translate('client_models_network_channel_invalid_name').fetch(channel_name));
116 _kiwi.app.message.text(_kiwi.global.i18n.translate('client_models_network_channel_invalid_name').fetch(channel_name), {timeout: 5000});
117 return;
118 }
119
120 // Check if we have the panel already. If not, create it
121 channel = that.panels.getByName(channel_name);
122 if (!channel) {
123 channel = new _kiwi.model.Channel({name: channel_name});
124 that.panels.add(channel);
125 }
126
127 panels.push(channel);
128
129 that.gateway.join(channel_name, channel_key);
130 });
131
132 return panels;
133 },
134
135
136 /**
137 * Join all the open channels we have open
138 * Reconnecting to a network would typically call this.
139 */
140 rejoinAllChannels: function() {
141 var that = this;
142
143 this.panels.forEach(function(panel) {
144 if (!panel.isChannel())
145 return;
146
147 that.gateway.join(panel.get('name'));
148 });
149 }
150 });
151
152
153
154 function onDisconnect(event) {
155 $.each(this.panels.models, function (index, panel) {
156 panel.addMsg('', _kiwi.global.i18n.translate('client_models_network_disconnected').fetch(), 'action quit');
157 });
158 }
159
160
161
162 function onConnect(event) {
163 var panels, channel_names;
164
165 // Update our nick with what the network gave us
166 this.set('nick', event.nick);
167
168 // If this is a re-connection then we may have some channels to re-join
169 this.rejoinAllChannels();
170
171 // Auto joining channels
172 if (this.auto_join && this.auto_join.channel) {
173 panels = this.createAndJoinChannels(this.auto_join.channel + ' ' + (this.auto_join.channel_key || ''));
174
175 // Show the last channel if we have one
176 if (panels)
177 panels[panels.length - 1].view.show();
178
179 delete this.auto_join;
180 }
181 }
182
183
184
185 function onOptions(event) {
186 var that = this;
187
188 $.each(event.options, function (name, value) {
189 switch (name) {
190 case 'CHANTYPES':
191 that.set('channel_prefix', value.join(''));
192 break;
193 case 'NETWORK':
194 that.set('name', value);
195 break;
196 case 'PREFIX':
197 that.set('user_prefixes', value);
198 break;
199 }
200 });
201
202 this.set('cap', event.cap);
203 }
204
205
206
207 function onMotd(event) {
208 this.panels.server.addMsg(this.get('name'), event.msg, 'motd');
209 }
210
211
212
213 function onJoin(event) {
214 var c, members, user;
215 c = this.panels.getByName(event.channel);
216 if (!c) {
217 c = new _kiwi.model.Channel({name: event.channel});
218 this.panels.add(c);
219 }
220
221 members = c.get('members');
222 if (!members) return;
223
224 user = new _kiwi.model.Member({nick: event.nick, ident: event.ident, hostname: event.hostname});
225 members.add(user);
226 }
227
228
229
230 function onPart(event) {
231 var channel, members, user,
232 part_options = {};
233
234 part_options.type = 'part';
235 part_options.message = event.message || '';
236
237 channel = this.panels.getByName(event.channel);
238 if (!channel) return;
239
240 // If this is us, close the panel
241 if (event.nick === this.get('nick')) {
242 channel.close();
243 return;
244 }
245
246 members = channel.get('members');
247 if (!members) return;
248
249 user = members.getByNick(event.nick);
250 if (!user) return;
251
252 members.remove(user, part_options);
253 }
254
255
256
257 function onQuit(event) {
258 var member, members,
259 quit_options = {};
260
261 quit_options.type = 'quit';
262 quit_options.message = event.message || '';
263
264 $.each(this.panels.models, function (index, panel) {
265 if (!panel.isChannel()) return;
266
267 member = panel.get('members').getByNick(event.nick);
268 if (member) {
269 panel.get('members').remove(member, quit_options);
270 }
271 });
272 }
273
274
275
276 function onKick(event) {
277 var channel, members, user,
278 part_options = {};
279
280 part_options.type = 'kick';
281 part_options.by = event.nick;
282 part_options.message = event.message || '';
283 part_options.current_user_kicked = (event.kicked == this.get('nick'))
284 part_options.current_user_initiated = (event.nick == this.get('nick'))
285
286 channel = this.panels.getByName(event.channel);
287 if (!channel) return;
288
289 members = channel.get('members');
290 if (!members) return;
291
292 user = members.getByNick(event.kicked);
293 if (!user) return;
294
295
296 members.remove(user, part_options);
297
298 if (part_options.current_user_kicked) {
299 members.reset([]);
300 }
301 }
302
303
304
305 function onMsg(event) {
306 var panel,
307 is_pm = (event.channel == this.get('nick'));
308
309 // An ignored user? don't do anything with it
310 if (_kiwi.gateway.isNickIgnored(event.nick)) {
311 return;
312 }
313
314 if (is_pm) {
315 // If a panel isn't found for this PM, create one
316 panel = this.panels.getByName(event.nick);
317 if (!panel) {
318 panel = new _kiwi.model.Query({name: event.nick});
319 this.panels.add(panel);
320 }
321
322 } else {
323 // If a panel isn't found for this channel, reroute to the
324 // server panel
325 panel = this.panels.getByName(event.channel);
326 if (!panel) {
327 panel = this.panels.server;
328 }
329 }
330
331 panel.addMsg(event.nick, event.msg);
332 }
333
334
335
336 function onNick(event) {
337 var member;
338
339 $.each(this.panels.models, function (index, panel) {
340 if (panel.get('name') == event.nick)
341 panel.set('name', event.newnick);
342
343 if (!panel.isChannel()) return;
344
345 member = panel.get('members').getByNick(event.nick);
346 if (member) {
347 member.set('nick', event.newnick);
348 panel.addMsg('', '== ' + _kiwi.global.i18n.translate('client_models_network_nickname_changed').fetch(event.nick, event.newnick) , 'action nick');
349 }
350 });
351 }
352
353
354
355 function onCtcpRequest(event) {
356 // An ignored user? don't do anything with it
357 if (_kiwi.gateway.isNickIgnored(event.nick)) {
358 return;
359 }
360
361 // Reply to a TIME ctcp
362 if (event.msg.toUpperCase() === 'TIME') {
363 this.gateway.ctcp(null, false, event.type, event.nick, (new Date()).toString());
364 }
365 }
366
367
368
369 function onCtcpResponse(event) {
370 // An ignored user? don't do anything with it
371 if (_kiwi.gateway.isNickIgnored(event.nick)) {
372 return;
373 }
374
375 this.panels.server.addMsg('[' + event.nick + ']', 'CTCP ' + event.msg);
376 }
377
378
379
380 function onNotice(event) {
381 var panel, channel_name;
382
383 // An ignored user? don't do anything with it
384 if (!event.from_server && event.nick && _kiwi.gateway.isNickIgnored(event.nick)) {
385 return;
386 }
387
388 // Find a panel for the destination(channel) or who its from
389 if (!event.from_server) {
390 panel = this.panels.getByName(event.target) || this.panels.getByName(event.nick);
391
392 // Forward ChanServ messages to its associated channel
393 if (event.nick.toLowerCase() == 'chanserv' && event.msg.charAt(0) == '[') {
394 channel_name = /\[([^ \]]+)\]/gi.exec(event.msg);
395 if (channel_name && channel_name[1]) {
396 channel_name = channel_name[1];
397
398 panel = this.panels.getByName(channel_name);
399 }
400 }
401
402 if (!panel) {
403 panel = this.panels.server;
404 }
405 } else {
406 panel = this.panels.server;
407 }
408
409 panel.addMsg('[' + (event.nick||'') + ']', event.msg);
410
411 // Show this notice to the active panel if it didn't have a set target
412 if (!event.from_server && panel === this.panels.server)
413 _kiwi.app.panels().active.addMsg('[' + (event.nick||'') + ']', event.msg);
414 }
415
416
417
418 function onAction(event) {
419 var panel,
420 is_pm = (event.channel == this.get('nick'));
421
422 // An ignored user? don't do anything with it
423 if (_kiwi.gateway.isNickIgnored(event.nick)) {
424 return;
425 }
426
427 if (is_pm) {
428 // If a panel isn't found for this PM, create one
429 panel = this.panels.getByName(event.nick);
430 if (!panel) {
431 panel = new _kiwi.model.Channel({name: event.nick});
432 this.panels.add(panel);
433 }
434
435 } else {
436 // If a panel isn't found for this channel, reroute to the
437 // server panel
438 panel = this.panels.getByName(event.channel);
439 if (!panel) {
440 panel = this.panels.server;
441 }
442 }
443
444 panel.addMsg('', '* ' + event.nick + ' ' + event.msg, 'action');
445 }
446
447
448
449 function onTopic(event) {
450 var c;
451 c = this.panels.getByName(event.channel);
452 if (!c) return;
453
454 // Set the channels topic
455 c.set('topic', event.topic);
456
457 // If this is the active channel, update the topic bar too
458 if (c.get('name') === this.panels.active.get('name')) {
459 _kiwi.app.topicbar.setCurrentTopic(event.topic);
460 }
461 }
462
463
464
465 function onTopicSetBy(event) {
466 var c, when;
467 c = this.panels.getByName(event.channel);
468 if (!c) return;
469
470 when = formatDate(new Date(event.when * 1000));
471 c.addMsg('', _kiwi.global.i18n.translate('client_models_network_topic').fetch(event.nick, when), 'topic');
472 }
473
474
475
476 function onUserlist(event) {
477 var channel;
478 channel = this.panels.getByName(event.channel);
479
480 // If we didn't find a channel for this, may aswell leave
481 if (!channel) return;
482
483 channel.temp_userlist = channel.temp_userlist || [];
484 _.each(event.users, function (item) {
485 var user = new _kiwi.model.Member({nick: item.nick, modes: item.modes});
486 channel.temp_userlist.push(user);
487 });
488 }
489
490
491
492 function onUserlistEnd(event) {
493 var channel;
494 channel = this.panels.getByName(event.channel);
495
496 // If we didn't find a channel for this, may aswell leave
497 if (!channel) return;
498
499 // Update the members list with the new list
500 channel.get('members').reset(channel.temp_userlist || []);
501
502 // Clear the temporary userlist
503 delete channel.temp_userlist;
504 }
505
506
507
508 function onMode(event) {
509 var channel, i, prefixes, members, member, find_prefix;
510
511 // Build a nicely formatted string to be displayed to a regular human
512 function friendlyModeString (event_modes, alt_target) {
513 var modes = {}, return_string;
514
515 // If no default given, use the main event info
516 if (!event_modes) {
517 event_modes = event.modes;
518 alt_target = event.target;
519 }
520
521 // Reformat the mode object to make it easier to work with
522 _.each(event_modes, function (mode){
523 var param = mode.param || alt_target || '';
524
525 // Make sure we have some modes for this param
526 if (!modes[param]) {
527 modes[param] = {'+':'', '-':''};
528 }
529
530 modes[param][mode.mode[0]] += mode.mode.substr(1);
531 });
532
533 // Put the string together from each mode
534 return_string = [];
535 _.each(modes, function (modeset, param) {
536 var str = '';
537 if (modeset['+']) str += '+' + modeset['+'];
538 if (modeset['-']) str += '-' + modeset['-'];
539 return_string.push(str + ' ' + param);
540 });
541 return_string = return_string.join(', ');
542
543 return return_string;
544 }
545
546
547 channel = this.panels.getByName(event.target);
548 if (channel) {
549 prefixes = this.get('user_prefixes');
550 find_prefix = function (p) {
551 return event.modes[i].mode[1] === p.mode;
552 };
553 for (i = 0; i < event.modes.length; i++) {
554 if (_.any(prefixes, find_prefix)) {
555 if (!members) {
556 members = channel.get('members');
557 }
558 member = members.getByNick(event.modes[i].param);
559 if (!member) {
560 console.log('MODE command recieved for unknown member %s on channel %s', event.modes[i].param, event.target);
561 return;
562 } else {
563 if (event.modes[i].mode[0] === '+') {
564 member.addMode(event.modes[i].mode[1]);
565 } else if (event.modes[i].mode[0] === '-') {
566 member.removeMode(event.modes[i].mode[1]);
567 }
568 members.sort();
569 //channel.addMsg('', '== ' + event.nick + ' set mode ' + event.modes[i].mode + ' ' + event.modes[i].param, 'action mode');
570 }
571 } else {
572 // Channel mode being set
573 // TODO: Store this somewhere?
574 //channel.addMsg('', 'CHANNEL === ' + event.nick + ' set mode ' + event.modes[i].mode + ' on ' + event.target, 'action mode');
575 }
576 }
577
578 channel.addMsg('', '== ' + _kiwi.global.i18n.translate('client_models_network_mode').fetch(event.nick, friendlyModeString()), 'action mode');
579 } else {
580 // This is probably a mode being set on us.
581 if (event.target.toLowerCase() === this.get("nick").toLowerCase()) {
582 this.panels.server.addMsg('', '== ' + _kiwi.global.i18n.translate('client_models_network_selfmode').fetch(event.nick, friendlyModeString()), 'action mode');
583 } else {
584 console.log('MODE command recieved for unknown target %s: ', event.target, event);
585 }
586 }
587 }
588
589
590
591 function onWhois(event) {
592 var logon_date, idle_time = '', panel;
593
594 if (event.end)
595 return;
596
597 if (typeof event.idle !== 'undefined') {
598 idle_time = secondsToTime(parseInt(event.idle, 10));
599 idle_time = idle_time.h.toString().lpad(2, "0") + ':' + idle_time.m.toString().lpad(2, "0") + ':' + idle_time.s.toString().lpad(2, "0");
600 }
601
602 panel = _kiwi.app.panels().active;
603 if (event.ident) {
604 panel.addMsg(event.nick, event.nick + ' [' + event.nick + '!' + event.ident + '@' + event.host + '] * ' + event.msg, 'whois');
605 } else if (event.chans) {
606 panel.addMsg(event.nick, _kiwi.global.i18n.translate('client_models_network_channels').fetch(event.chans), 'whois');
607 } else if (event.irc_server) {
608 panel.addMsg(event.nick, _kiwi.global.i18n.translate('client_models_network_server').fetch(event.irc_server, event.server_info), 'whois');
609 } else if (event.msg) {
610 panel.addMsg(event.nick, event.msg, 'whois');
611 } else if (event.logon) {
612 logon_date = new Date();
613 logon_date.setTime(event.logon * 1000);
614 logon_date = formatDate(logon_date);
615
616 panel.addMsg(event.nick, _kiwi.global.i18n.translate('client_models_network_idle_and_signon').fetch(idle_time, logon_date), 'whois');
617 } else if (event.away_reason) {
618 panel.addMsg(event.nick, _kiwi.global.i18n.translate('client_models_network_away').fetch(event.away_reason), 'whois');
619 } else {
620 panel.addMsg(event.nick, _kiwi.global.i18n.translate('client_models_network_idle').fetch(idle_time), 'whois');
621 }
622 }
623
624 function onWhowas(event) {
625 var panel;
626
627 if (event.end)
628 return;
629
630 panel = _kiwi.app.panels().active;
631 if (event.host) {
632 panel.addMsg(event.nick, event.nick + ' [' + event.nick + ((event.ident)? '!' + event.ident : '') + '@' + event.host + '] * ' + event.real_name, 'whois');
633 } else {
634 panel.addMsg(event.nick, _kiwi.global.i18n.translate('client_models_network_nickname_notfound').fetch(), 'whois');
635 }
636 }
637
638
639 function onAway(event) {
640 $.each(this.panels.models, function (index, panel) {
641 if (!panel.isChannel()) return;
642
643 member = panel.get('members').getByNick(event.nick);
644 if (member) {
645 member.set('away', !(!event.trailing));
646 }
647 });
648 }
649
650
651
652 function onListStart(event) {
653 var chanlist = _kiwi.model.Applet.loadOnce('kiwi_chanlist');
654 chanlist.view.show();
655 }
656
657
658
659 function onIrcError(event) {
660 var panel, tmp;
661
662 if (event.channel !== undefined && !(panel = this.panels.getByName(event.channel))) {
663 panel = this.panels.server;
664 }
665
666 switch (event.error) {
667 case 'banned_from_channel':
668 panel.addMsg(' ', '== ' + _kiwi.global.i18n.translate('client_models_network_banned').fetch(event.channel, event.reason), 'status');
669 _kiwi.app.message.text(_kiwi.global.i18n.translate('client_models_network_banned').fetch(event.channel, event.reason));
670 break;
671 case 'bad_channel_key':
672 panel.addMsg(' ', '== ' + _kiwi.global.i18n.translate('client_models_network_channel_badkey').fetch(event.channel), 'status');
673 _kiwi.app.message.text(_kiwi.global.i18n.translate('client_models_network_channel_badkey').fetch(event.channel));
674 break;
675 case 'invite_only_channel':
676 panel.addMsg(' ', '== ' + _kiwi.global.i18n.translate('client_models_network_channel_inviteonly').fetch(event.channel), 'status');
677 _kiwi.app.message.text(_kiwi.global.i18n.translate('client_models_network_channel_inviteonly').fetch(event.channel));
678 break;
679 case 'channel_is_full':
680 panel.addMsg(' ', '== ' + _kiwi.global.i18n.translate('client_models_network_channel_limitreached').fetch(event.channel), 'status');
681 _kiwi.app.message.text(_kiwi.global.i18n.translate('client_models_network_channel_limitreached').fetch(event.channel));
682 break;
683 case 'chanop_privs_needed':
684 panel.addMsg(' ', '== ' + event.reason, 'status');
685 _kiwi.app.message.text(event.reason + ' (' + event.channel + ')');
686 break;
687 case 'no_such_nick':
688 tmp = this.panels.getByName(event.nick);
689 if (tmp) {
690 tmp.addMsg(' ', '== ' + event.nick + ': ' + event.reason, 'status');
691 } else {
692 this.panels.server.addMsg(' ', '== ' + event.nick + ': ' + event.reason, 'status');
693 }
694 break;
695 case 'nickname_in_use':
696 this.panels.server.addMsg(' ', '== ' + _kiwi.global.i18n.translate('client_models_network_nickname_alreadyinuse').fetch( event.nick), 'status');
697 if (this.panels.server !== this.panels.active) {
698 _kiwi.app.message.text(_kiwi.global.i18n.translate('client_models_network_nickname_alreadyinuse').fetch(event.nick));
699 }
700
701 // Only show the nickchange component if the controlbox is open
702 if (_kiwi.app.controlbox.$el.css('display') !== 'none') {
703 (new _kiwi.view.NickChangeBox()).render();
704 }
705
706 break;
707
708 case 'password_mismatch':
709 this.panels.server.addMsg(' ', '== ' + _kiwi.global.i18n.translate('client_models_network_badpassword').fetch(), 'status');
710 break;
711 default:
712 // We don't know what data contains, so don't do anything with it.
713 //_kiwi.front.tabviews.server.addMsg(null, ' ', '== ' + data, 'status');
714 }
715 }
716 }
717
718 )();