3 _kiwi
.model
.Network
= Backbone
.Model
.extend({
7 * The name of the network
13 * The address (URL) of the network
19 * The port for the network
25 * If this network uses SSL
31 * The password to connect to this network
37 * The current nickname
43 * The channel prefix for this network
49 * The user prefixes for channel owner/admin/op/voice etc. on this network
52 user_prefixes
: ['~', '&', '@', '+']
56 initialize: function () {
57 // If we already have a connection, bind our events
58 if (typeof this.get('connection_id') !== 'undefined') {
59 this.gateway
= _kiwi
.global
.components
.Network(this.get('connection_id'));
60 this.bindGatewayEvents();
63 // Create our panel list (tabs)
64 this.panels
= new _kiwi
.model
.PanelList([], this);
65 //this.panels.network = this;
67 // Automatically create a server tab
68 var server_panel
= new _kiwi
.model
.Server({name
: 'Server'});
69 this.panels
.add(server_panel
);
70 this.panels
.server
= this.panels
.active
= server_panel
;
74 reconnect: function(callback_fn
) {
77 nick
: this.get('nick'),
78 host
: this.get('address'),
79 port
: this.get('port'),
81 password
: this.get('password')
84 _kiwi
.gateway
.makeIrcConnection(server_info
, function(err
, connection_id
) {
86 that
.gateway
.dispose();
88 that
.set('connection_id', connection_id
);
89 that
.gateway
= _kiwi
.global
.components
.Network(that
.get('connection_id'));
90 that
.bindGatewayEvents();
92 callback_fn
&& callback_fn(err
);
95 console
.log("_kiwi.gateway.socket.on('error')", {reason
: err
});
96 callback_fn
&& callback_fn(err
);
102 bindGatewayEvents: function () {
103 //this.gateway.on('all', function() {console.log('ALL', this.get('connection_id'), arguments);});
105 this.gateway
.on('connect', onConnect
, this);
106 this.gateway
.on('disconnect', onDisconnect
, this);
108 this.gateway
.on('nick', function(event
) {
109 if (event
.nick
=== this.get('nick')) {
110 this.set('nick', event
.newnick
);
114 this.gateway
.on('options', onOptions
, this);
115 this.gateway
.on('motd', onMotd
, this);
116 this.gateway
.on('join', onJoin
, this);
117 this.gateway
.on('part', onPart
, this);
118 this.gateway
.on('quit', onQuit
, this);
119 this.gateway
.on('kick', onKick
, this);
120 this.gateway
.on('msg', onMsg
, this);
121 this.gateway
.on('nick', onNick
, this);
122 this.gateway
.on('ctcp_request', onCtcpRequest
, this);
123 this.gateway
.on('ctcp_response', onCtcpResponse
, this);
124 this.gateway
.on('notice', onNotice
, this);
125 this.gateway
.on('action', onAction
, this);
126 this.gateway
.on('topic', onTopic
, this);
127 this.gateway
.on('topicsetby', onTopicSetBy
, this);
128 this.gateway
.on('userlist', onUserlist
, this);
129 this.gateway
.on('userlist_end', onUserlistEnd
, this);
130 this.gateway
.on('mode', onMode
, this);
131 this.gateway
.on('whois', onWhois
, this);
132 this.gateway
.on('whowas', onWhowas
, this);
133 this.gateway
.on('away', onAway
, this);
134 this.gateway
.on('list_start', onListStart
, this);
135 this.gateway
.on('irc_error', onIrcError
, this);
136 this.gateway
.on('unknown_command', onUnknownCommand
, this);
141 * Create panels and join the channel
142 * This will not wait for the join event to create a panel. This
143 * increases responsiveness in case of network lag
145 createAndJoinChannels: function (channels
) {
149 // Multiple channels may come as comma-delimited
150 if (typeof channels
=== 'string') {
151 channels
= channels
.split(',');
154 $.each(channels
, function (index
, channel_name_key
) {
155 // We may have a channel key so split it off
156 var spli
= channel_name_key
.trim().split(' '),
157 channel_name
= spli
[0],
158 channel_key
= spli
[1] || '';
160 // Trim any whitespace off the name
161 channel_name
= channel_name
.trim();
163 // If not a valid channel name, display a warning
164 if (!_kiwi
.app
.isChannelName(channel_name
)) {
165 that
.panels
.server
.addMsg('', _kiwi
.global
.i18n
.translate('client_models_network_channel_invalid_name').fetch(channel_name
));
166 _kiwi
.app
.message
.text(_kiwi
.global
.i18n
.translate('client_models_network_channel_invalid_name').fetch(channel_name
), {timeout
: 5000});
170 // Check if we have the panel already. If not, create it
171 channel
= that
.panels
.getByName(channel_name
);
173 channel
= new _kiwi
.model
.Channel({name
: channel_name
});
174 that
.panels
.add(channel
);
177 panels
.push(channel
);
179 that
.gateway
.join(channel_name
, channel_key
);
187 * Join all the open channels we have open
188 * Reconnecting to a network would typically call this.
190 rejoinAllChannels: function() {
193 this.panels
.forEach(function(panel
) {
194 if (!panel
.isChannel())
197 that
.gateway
.join(panel
.get('name'));
204 function onDisconnect(event
) {
205 $.each(this.panels
.models
, function (index
, panel
) {
206 panel
.addMsg('', _kiwi
.global
.i18n
.translate('client_models_network_disconnected').fetch(), 'action quit');
212 function onConnect(event
) {
213 var panels
, channel_names
;
215 // Update our nick with what the network gave us
216 this.set('nick', event
.nick
);
218 // If this is a re-connection then we may have some channels to re-join
219 this.rejoinAllChannels();
221 // Auto joining channels
222 if (this.auto_join
&& this.auto_join
.channel
) {
223 panels
= this.createAndJoinChannels(this.auto_join
.channel
+ ' ' + (this.auto_join
.key
|| ''));
225 // Show the last channel if we have one
227 panels
[panels
.length
- 1].view
.show();
229 delete this.auto_join
;
235 function onOptions(event
) {
238 $.each(event
.options
, function (name
, value
) {
241 that
.set('channel_prefix', value
.join(''));
244 that
.set('name', value
);
247 that
.set('user_prefixes', value
);
252 this.set('cap', event
.cap
);
257 function onMotd(event
) {
258 this.panels
.server
.addMsg(this.get('name'), event
.msg
, 'motd');
263 function onJoin(event
) {
264 var c
, members
, user
;
265 c
= this.panels
.getByName(event
.channel
);
267 c
= new _kiwi
.model
.Channel({name
: event
.channel
});
271 members
= c
.get('members');
272 if (!members
) return;
274 user
= new _kiwi
.model
.Member({nick
: event
.nick
, ident
: event
.ident
, hostname
: event
.hostname
});
275 members
.add(user
, {kiwi
: event
});
280 function onPart(event
) {
281 var channel
, members
, user
,
284 part_options
.type
= 'part';
285 part_options
.message
= event
.message
|| '';
286 part_options
.time
= event
.time
;
288 channel
= this.panels
.getByName(event
.channel
);
289 if (!channel
) return;
291 // If this is us, close the panel
292 if (event
.nick
=== this.get('nick')) {
297 members
= channel
.get('members');
298 if (!members
) return;
300 user
= members
.getByNick(event
.nick
);
303 members
.remove(user
, {kiwi
: part_options
});
308 function onQuit(event
) {
312 quit_options
.type
= 'quit';
313 quit_options
.message
= event
.message
|| '';
314 quit_options
.time
= event
.time
;
316 $.each(this.panels
.models
, function (index
, panel
) {
317 if (!panel
.isChannel()) return;
319 member
= panel
.get('members').getByNick(event
.nick
);
321 panel
.get('members').remove(member
, {kiwi
: quit_options
});
328 function onKick(event
) {
329 var channel
, members
, user
,
332 part_options
.type
= 'kick';
333 part_options
.by
= event
.nick
;
334 part_options
.message
= event
.message
|| '';
335 part_options
.current_user_kicked
= (event
.kicked
== this.get('nick'));
336 part_options
.current_user_initiated
= (event
.nick
== this.get('nick'));
337 part_options
.time
= event
.time
;
339 channel
= this.panels
.getByName(event
.channel
);
340 if (!channel
) return;
342 members
= channel
.get('members');
343 if (!members
) return;
345 user
= members
.getByNick(event
.kicked
);
349 members
.remove(user
, {kiwi
: part_options
});
351 if (part_options
.current_user_kicked
) {
358 function onMsg(event
) {
360 is_pm
= (event
.channel
.toLowerCase() == this.get('nick').toLowerCase());
362 // An ignored user? don't do anything with it
363 if (_kiwi
.gateway
.isNickIgnored(event
.nick
)) {
368 // If a panel isn't found for this PM, create one
369 panel
= this.panels
.getByName(event
.nick
);
371 panel
= new _kiwi
.model
.Query({name
: event
.nick
});
372 this.panels
.add(panel
);
376 // If a panel isn't found for this channel, reroute to the
378 panel
= this.panels
.getByName(event
.channel
);
380 panel
= this.panels
.server
;
384 panel
.addMsg(event
.nick
, event
.msg
, 'privmsg', {time
: event
.time
});
389 function onNick(event
) {
392 $.each(this.panels
.models
, function (index
, panel
) {
393 if (panel
.get('name') == event
.nick
)
394 panel
.set('name', event
.newnick
);
396 if (!panel
.isChannel()) return;
398 member
= panel
.get('members').getByNick(event
.nick
);
400 member
.set('nick', event
.newnick
);
401 panel
.addMsg('', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_nickname_changed').fetch(event
.nick
, event
.newnick
) , 'action nick', {time
: event
.time
});
408 function onCtcpRequest(event
) {
409 // An ignored user? don't do anything with it
410 if (_kiwi
.gateway
.isNickIgnored(event
.nick
)) {
414 // Reply to a TIME ctcp
415 if (event
.msg
.toUpperCase() === 'TIME') {
416 this.gateway
.ctcp(false, event
.type
, event
.nick
, (new Date()).toString());
422 function onCtcpResponse(event
) {
423 // An ignored user? don't do anything with it
424 if (_kiwi
.gateway
.isNickIgnored(event
.nick
)) {
428 this.panels
.server
.addMsg('[' + event
.nick
+ ']', 'CTCP ' + event
.msg
, 'ctcp', {time
: event
.time
});
433 function onNotice(event
) {
434 var panel
, channel_name
;
436 // An ignored user? don't do anything with it
437 if (!event
.from_server
&& event
.nick
&& _kiwi
.gateway
.isNickIgnored(event
.nick
)) {
441 // Find a panel for the destination(channel) or who its from
442 if (!event
.from_server
) {
443 panel
= this.panels
.getByName(event
.target
) || this.panels
.getByName(event
.nick
);
445 // Forward ChanServ messages to its associated channel
446 if (event
.nick
&& event
.nick
.toLowerCase() == 'chanserv' && event
.msg
.charAt(0) == '[') {
447 channel_name
= /\[([^ \]]+)\]/gi.exec(event
.msg
);
448 if (channel_name
&& channel_name
[1]) {
449 channel_name
= channel_name
[1];
451 panel
= this.panels
.getByName(channel_name
);
456 panel
= this.panels
.server
;
459 panel
= this.panels
.server
;
462 panel
.addMsg('[' + (event
.nick
||'') + ']', event
.msg
, 'notice', {time
: event
.time
});
464 // Show this notice to the active panel if it didn't have a set target
465 if (!event
.from_server
&& panel
=== this.panels
.server
&& _kiwi
.app
.panels().active
!== this.panels
.server
)
466 _kiwi
.app
.panels().active
.addMsg('[' + (event
.nick
||'') + ']', event
.msg
, 'notice', {time
: event
.time
});
471 function onAction(event
) {
473 is_pm
= (event
.channel
.toLowerCase() == this.get('nick').toLowerCase());
475 // An ignored user? don't do anything with it
476 if (_kiwi
.gateway
.isNickIgnored(event
.nick
)) {
481 // If a panel isn't found for this PM, create one
482 panel
= this.panels
.getByName(event
.nick
);
484 panel
= new _kiwi
.model
.Channel({name
: event
.nick
});
485 this.panels
.add(panel
);
489 // If a panel isn't found for this channel, reroute to the
491 panel
= this.panels
.getByName(event
.channel
);
493 panel
= this.panels
.server
;
497 panel
.addMsg('', '* ' + event
.nick
+ ' ' + event
.msg
, 'action', {time
: event
.time
});
502 function onTopic(event
) {
504 c
= this.panels
.getByName(event
.channel
);
507 // Set the channels topic
508 c
.set('topic', event
.topic
);
510 // If this is the active channel, update the topic bar too
511 if (c
.get('name') === this.panels
.active
.get('name')) {
512 _kiwi
.app
.topicbar
.setCurrentTopic(event
.topic
);
518 function onTopicSetBy(event
) {
520 c
= this.panels
.getByName(event
.channel
);
523 when
= formatDate(new Date(event
.when
* 1000));
524 c
.addMsg('', _kiwi
.global
.i18n
.translate('client_models_network_topic').fetch(event
.nick
, when
), 'topic');
529 function onUserlist(event
) {
531 channel
= this.panels
.getByName(event
.channel
);
533 // If we didn't find a channel for this, may aswell leave
534 if (!channel
) return;
536 channel
.temp_userlist
= channel
.temp_userlist
|| [];
537 _
.each(event
.users
, function (item
) {
538 var user
= new _kiwi
.model
.Member({nick
: item
.nick
, modes
: item
.modes
});
539 channel
.temp_userlist
.push(user
);
545 function onUserlistEnd(event
) {
547 channel
= this.panels
.getByName(event
.channel
);
549 // If we didn't find a channel for this, may aswell leave
550 if (!channel
) return;
552 // Update the members list with the new list
553 channel
.get('members').reset(channel
.temp_userlist
|| []);
555 // Clear the temporary userlist
556 delete channel
.temp_userlist
;
561 function onMode(event
) {
562 var channel
, i
, prefixes
, members
, member
, find_prefix
;
564 // Build a nicely formatted string to be displayed to a regular human
565 function friendlyModeString (event_modes
, alt_target
) {
566 var modes
= {}, return_string
;
568 // If no default given, use the main event info
570 event_modes
= event
.modes
;
571 alt_target
= event
.target
;
574 // Reformat the mode object to make it easier to work with
575 _
.each(event_modes
, function (mode
){
576 var param
= mode
.param
|| alt_target
|| '';
578 // Make sure we have some modes for this param
580 modes
[param
] = {'+':'', '-':''};
583 modes
[param
][mode
.mode
[0]] += mode
.mode
.substr(1);
586 // Put the string together from each mode
588 _
.each(modes
, function (modeset
, param
) {
590 if (modeset
['+']) str
+= '+' + modeset
['+'];
591 if (modeset
['-']) str
+= '-' + modeset
['-'];
592 return_string
.push(str
+ ' ' + param
);
594 return_string
= return_string
.join(', ');
596 return return_string
;
600 channel
= this.panels
.getByName(event
.target
);
602 prefixes
= this.get('user_prefixes');
603 find_prefix = function (p
) {
604 return event
.modes
[i
].mode
[1] === p
.mode
;
606 for (i
= 0; i
< event
.modes
.length
; i
++) {
607 if (_
.any(prefixes
, find_prefix
)) {
609 members
= channel
.get('members');
611 member
= members
.getByNick(event
.modes
[i
].param
);
613 console
.log('MODE command recieved for unknown member %s on channel %s', event
.modes
[i
].param
, event
.target
);
616 if (event
.modes
[i
].mode
[0] === '+') {
617 member
.addMode(event
.modes
[i
].mode
[1]);
618 } else if (event
.modes
[i
].mode
[0] === '-') {
619 member
.removeMode(event
.modes
[i
].mode
[1]);
622 //channel.addMsg('', '== ' + event.nick + ' set mode ' + event.modes[i].mode + ' ' + event.modes[i].param, 'action mode');
625 // Channel mode being set
626 // TODO: Store this somewhere?
627 //channel.addMsg('', 'CHANNEL === ' + event.nick + ' set mode ' + event.modes[i].mode + ' on ' + event.target, 'action mode');
631 channel
.addMsg('', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_mode').fetch(event
.nick
, friendlyModeString()), 'action mode', {time
: event
.time
});
633 // This is probably a mode being set on us.
634 if (event
.target
.toLowerCase() === this.get("nick").toLowerCase()) {
635 this.panels
.server
.addMsg('', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_selfmode').fetch(event
.nick
, friendlyModeString()), 'action mode');
637 console
.log('MODE command recieved for unknown target %s: ', event
.target
, event
);
644 function onWhois(event
) {
645 var logon_date
, idle_time
= '', panel
;
650 if (typeof event
.idle
!== 'undefined') {
651 idle_time
= secondsToTime(parseInt(event
.idle
, 10));
652 idle_time
= idle_time
.h
.toString().lpad(2, "0") + ':' + idle_time
.m
.toString().lpad(2, "0") + ':' + idle_time
.s
.toString().lpad(2, "0");
655 panel
= _kiwi
.app
.panels().active
;
657 panel
.addMsg(event
.nick
, event
.nick
+ ' [' + event
.nick
+ '!' + event
.ident
+ '@' + event
.host
+ '] * ' + event
.msg
, 'whois');
658 } else if (event
.chans
) {
659 panel
.addMsg(event
.nick
, _kiwi
.global
.i18n
.translate('client_models_network_channels').fetch(event
.chans
), 'whois');
660 } else if (event
.irc_server
) {
661 panel
.addMsg(event
.nick
, _kiwi
.global
.i18n
.translate('client_models_network_server').fetch(event
.irc_server
, event
.server_info
), 'whois');
662 } else if (event
.msg
) {
663 panel
.addMsg(event
.nick
, event
.msg
, 'whois');
664 } else if (event
.logon
) {
665 logon_date
= new Date();
666 logon_date
.setTime(event
.logon
* 1000);
667 logon_date
= formatDate(logon_date
);
669 panel
.addMsg(event
.nick
, _kiwi
.global
.i18n
.translate('client_models_network_idle_and_signon').fetch(idle_time
, logon_date
), 'whois');
670 } else if (event
.away_reason
) {
671 panel
.addMsg(event
.nick
, _kiwi
.global
.i18n
.translate('client_models_network_away').fetch(event
.away_reason
), 'whois');
673 panel
.addMsg(event
.nick
, _kiwi
.global
.i18n
.translate('client_models_network_idle').fetch(idle_time
), 'whois');
677 function onWhowas(event
) {
683 panel
= _kiwi
.app
.panels().active
;
685 panel
.addMsg(event
.nick
, event
.nick
+ ' [' + event
.nick
+ ((event
.ident
)? '!' + event
.ident
: '') + '@' + event
.host
+ '] * ' + event
.real_name
, 'whois');
687 panel
.addMsg(event
.nick
, _kiwi
.global
.i18n
.translate('client_models_network_nickname_notfound').fetch(), 'whois');
692 function onAway(event
) {
693 $.each(this.panels
.models
, function (index
, panel
) {
694 if (!panel
.isChannel()) return;
696 member
= panel
.get('members').getByNick(event
.nick
);
698 member
.set('away', !(!event
.trailing
));
705 function onListStart(event
) {
706 var chanlist
= _kiwi
.model
.Applet
.loadOnce('kiwi_chanlist');
707 chanlist
.view
.show();
712 function onIrcError(event
) {
715 if (event
.channel
!== undefined && !(panel
= this.panels
.getByName(event
.channel
))) {
716 panel
= this.panels
.server
;
719 switch (event
.error
) {
720 case 'banned_from_channel':
721 panel
.addMsg(' ', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_banned').fetch(event
.channel
, event
.reason
), 'status');
722 _kiwi
.app
.message
.text(_kiwi
.global
.i18n
.translate('client_models_network_banned').fetch(event
.channel
, event
.reason
));
724 case 'bad_channel_key':
725 panel
.addMsg(' ', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_channel_badkey').fetch(event
.channel
), 'status');
726 _kiwi
.app
.message
.text(_kiwi
.global
.i18n
.translate('client_models_network_channel_badkey').fetch(event
.channel
));
728 case 'invite_only_channel':
729 panel
.addMsg(' ', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_channel_inviteonly').fetch(event
.channel
), 'status');
730 _kiwi
.app
.message
.text(_kiwi
.global
.i18n
.translate('client_models_network_channel_inviteonly').fetch(event
.channel
));
732 case 'user_on_channel':
733 panel
.addMsg(' ', '== ' + event
.nick
+ ' is already on this channel');
735 case 'channel_is_full':
736 panel
.addMsg(' ', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_channel_limitreached').fetch(event
.channel
), 'status');
737 _kiwi
.app
.message
.text(_kiwi
.global
.i18n
.translate('client_models_network_channel_limitreached').fetch(event
.channel
));
739 case 'chanop_privs_needed':
740 panel
.addMsg(' ', '== ' + event
.reason
, 'status');
741 _kiwi
.app
.message
.text(event
.reason
+ ' (' + event
.channel
+ ')');
744 tmp
= this.panels
.getByName(event
.nick
);
746 tmp
.addMsg(' ', '== ' + event
.nick
+ ': ' + event
.reason
, 'status');
748 this.panels
.server
.addMsg(' ', '== ' + event
.nick
+ ': ' + event
.reason
, 'status');
751 case 'nickname_in_use':
752 this.panels
.server
.addMsg(' ', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_nickname_alreadyinuse').fetch( event
.nick
), 'status');
753 if (this.panels
.server
!== this.panels
.active
) {
754 _kiwi
.app
.message
.text(_kiwi
.global
.i18n
.translate('client_models_network_nickname_alreadyinuse').fetch(event
.nick
));
757 // Only show the nickchange component if the controlbox is open
758 if (_kiwi
.app
.controlbox
.$el
.css('display') !== 'none') {
759 (new _kiwi
.view
.NickChangeBox()).render();
764 case 'password_mismatch':
765 this.panels
.server
.addMsg(' ', '== ' + _kiwi
.global
.i18n
.translate('client_models_network_badpassword').fetch(), 'status');
768 // We don't know what data contains, so don't do anything with it.
769 //_kiwi.front.tabviews.server.addMsg(null, ' ', '== ' + data, 'status');
774 function onUnknownCommand(event
) {
775 var display_params
= _
.clone(event
.params
);
777 // A lot of commands have our nick as the first parameter. This is redundant for us
778 if (display_params
[0] && display_params
[0] == this.get('nick')) {
779 display_params
.shift();
783 display_params
.push(event
.trailing
);
785 this.panels
.server
.addMsg('', '[' + event
.command
+ '] ' + display_params
.join(', ', ''));