1 _kiwi
.model
.Application = function () {
2 // Set to a reference to this object within initialize()
5 // The auto connect details entered into the server select box
6 var auto_connect_details
= {};
9 var model = function () {
10 /** _kiwi.view.Application */
13 /** _kiwi.view.StatusMessage */
16 /* Address for the kiwi server */
17 this.kiwi_server
= null;
19 this.initialize = function (options
) {
22 if (options
[0].container
) {
23 this.set('container', options
[0].container
);
26 // The base url to the kiwi server
27 this.set('base_path', options
[0].base_path
? options
[0].base_path
: '/kiwi');
29 // Any options sent down from the server
30 this.server_settings
= options
[0].server_settings
|| {};
32 // Best guess at where the kiwi server is
33 this.detectKiwiServer();
35 // Takes instances of model_network
36 this.connections
= new _kiwi
.model
.NetworkPanelList();
40 this.start = function () {
41 // Only debug if set in the querystring
42 if (!getQueryVariable('debug')) {
49 _kiwi
.gateway
= new _kiwi
.model
.Gateway();
50 this.bindGatewayCommands(_kiwi
.gateway
);
52 this.initializeClient();
53 this.initializeGlobals();
55 this.view
.barsHide(true);
57 this.showIntialConenctionDialog();
61 this.detectKiwiServer = function () {
62 // If running from file, default to localhost:7777 by default
63 if (window
.location
.protocol
=== 'file:') {
64 this.kiwi_server
= 'http://localhost:7778';
66 // Assume the kiwi server is on the same server
67 this.kiwi_server
= window
.location
.protocol
+ '//' + window
.location
.host
;
72 this.showIntialConenctionDialog = function() {
73 var connection_dialog
= new _kiwi
.model
.NewConnection();
74 this.populateDefaultServerSettings(connection_dialog
);
76 connection_dialog
.view
.$el
.addClass('initial');
77 this.view
.$el
.find('.panel_container:first').append(connection_dialog
.view
.$el
);
79 var $info
= $($('#tmpl_new_connection_info').html());
82 connection_dialog
.view
.infoBoxSet($info
);
83 connection_dialog
.view
.infoBoxShow();
86 // TODO: Shouldn't really be here but it's not working in the view.. :/
87 // Hack for firefox browers: Focus is not given on this event loop iteration
88 setTimeout(function(){
89 connection_dialog
.view
.$el
.find('.nick').select();
92 // Once connected, close this dialog and remove its own event
94 connection_dialog
.view
.$el
.slideUp(function() {
95 connection_dialog
.view
.dispose();
96 connection_dialog
= null;
98 _kiwi
.gateway
.off('onconnect', fn
);
102 _kiwi
.gateway
.on('onconnect', fn
);
106 this.initializeClient = function () {
107 this.view
= new _kiwi
.view
.Application({model
: this, el
: this.get('container')});
109 // Applets panel list
110 this.applet_panels
= new _kiwi
.model
.PanelList();
111 this.applet_panels
.view
.$el
.addClass('panellist applets');
112 this.view
.$el
.find('#tabs').append(this.applet_panels
.view
.$el
);
115 * Set the UI components up
117 this.controlbox
= new _kiwi
.view
.ControlBox({el
: $('#controlbox')[0]});
118 this.bindControllboxCommands(this.controlbox
);
120 this.topicbar
= new _kiwi
.view
.TopicBar({el
: $('#topic')[0]});
122 new _kiwi
.view
.AppToolbar({el
: $('#toolbar .app_tools')[0]});
124 this.message
= new _kiwi
.view
.StatusMessage({el
: $('#status_message')[0]});
126 this.resize_handle
= new _kiwi
.view
.ResizeHandler({el
: $('#memberlists_resize_handle')[0]});
128 // Rejigg the UI sizes
129 this.view
.doLayout();
133 this.initializeGlobals = function () {
134 _kiwi
.global
.connections
= this.connections
;
136 _kiwi
.global
.panels
= this.panels
;
137 _kiwi
.global
.panels
.applets
= this.applet_panels
;
139 _kiwi
.global
.components
.Applet
= _kiwi
.model
.Applet
;
140 _kiwi
.global
.components
.Panel
=_kiwi
.model
.Panel
;
144 this.populateDefaultServerSettings = function (new_connection_dialog
) {
158 * Get any settings set by the server
159 * These settings may be changed in the server selection dialog or via URL parameters
161 if (this.server_settings
.client
) {
162 if (this.server_settings
.client
.nick
)
163 defaults
.nick
= this.server_settings
.client
.nick
;
165 if (this.server_settings
.client
.server
)
166 defaults
.server
= this.server_settings
.client
.server
;
168 if (this.server_settings
.client
.port
)
169 defaults
.port
= this.server_settings
.client
.port
;
171 if (this.server_settings
.client
.ssl
)
172 defaults
.ssl
= this.server_settings
.client
.ssl
;
174 if (this.server_settings
.client
.channel
)
175 defaults
.channel
= this.server_settings
.client
.channel
;
181 * Get any settings passed in the URL
182 * These settings may be changed in the server selection dialog
185 // Any query parameters first
186 if (getQueryVariable('nick'))
187 defaults
.nick
= getQueryVariable('nick');
189 if (window
.location
.hash
)
190 defaults
.channel
= window
.location
.hash
;
193 // Process the URL part by part, extracting as we go
194 parts
= window
.location
.pathname
.toString().replace(this.get('base_path'), '').split('/');
196 if (parts
.length
> 0) {
199 if (parts
.length
> 0 && parts
[0]) {
200 // Check to see if we're dealing with an irc: uri, or whether we need to extract the server/channel info from the HTTP URL path.
201 uricheck
= parts
[0].substr(0, 7).toLowerCase();
202 if ((uricheck
=== 'ircs%3a') || (uricheck
.substr(0,6) === 'irc%3a')) {
203 parts
[0] = decodeURIComponent(parts
[0]);
204 // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
205 uricheck
= /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts
[0]);
207 uricheck[1] = ssl (optional)
209 uricheck[3] = port (optional)
210 uricheck[4] = channel (optional)
211 uricheck[5] = channel key (optional, channel must also be set)
214 if (typeof uricheck
[1] !== 'undefined') {
216 if (defaults
.port
=== 6667) {
217 defaults
.port
= 6697;
220 defaults
.server
= uricheck
[2];
221 if (typeof uricheck
[3] !== 'undefined') {
222 defaults
.port
= uricheck
[3];
224 if (typeof uricheck
[4] !== 'undefined') {
225 defaults
.channel
= '#' + uricheck
[4];
226 if (typeof uricheck
[5] !== 'undefined') {
227 defaults
.channel_key
= uricheck
[5];
233 // Extract the port+ssl if we find one
234 if (parts
[0].search(/:/) > 0) {
235 defaults
.port
= parts
[0].substring(parts
[0].search(/:/) + 1);
236 defaults
.server
= parts
[0].substring(0, parts
[0].search(/:/));
237 if (defaults
.port
[0] === '+') {
238 defaults
.port
= parseInt(defaults
.port
.substring(1), 10);
241 defaults
.ssl
= false;
245 defaults
.server
= parts
[0];
252 if (parts
.length
> 0 && parts
[0]) {
253 defaults
.channel
= '#' + parts
[0];
258 // If any settings have been given by the server.. override any auto detected settings
260 * Get any server restrictions as set in the server config
261 * These settings can not be changed in the server selection dialog
263 if (this.server_settings
&& this.server_settings
.connection
) {
264 if (this.server_settings
.connection
.server
) {
265 defaults
.server
= this.server_settings
.connection
.server
;
268 if (this.server_settings
.connection
.port
) {
269 defaults
.port
= this.server_settings
.connection
.port
;
272 if (this.server_settings
.connection
.ssl
) {
273 defaults
.ssl
= this.server_settings
.connection
.ssl
;
276 if (this.server_settings
.connection
.channel
) {
277 defaults
.channel
= this.server_settings
.connection
.channel
;
280 if (this.server_settings
.connection
.nick
) {
281 defaults
.nick
= this.server_settings
.connection
.nick
;
285 // Set any random numbers if needed
286 defaults
.nick
= defaults
.nick
.replace('?', Math
.floor(Math
.random() * 100000).toString());
288 // Populate the server select box with defaults
289 new_connection_dialog
.view
.populateFields(defaults
);
293 this.panels
= (function() {
294 var fn = function(panel_type
) {
297 // Default panel type
298 panel_type
= panel_type
|| 'connections';
300 switch (panel_type
) {
302 panels
= this.connections
.panels();
305 panels
= this.applet_panels
.models
;
309 // Active panels / server
310 panels
.active
= this.connections
.active_panel
;
311 panels
.server
= this.connections
.active_connection
?
312 this.connections
.active_connection
.panels
.server
:
318 _
.extend(fn
, Backbone
.Events
);
324 this.bindGatewayCommands = function (gw
) {
325 gw
.on('onconnect', function (event
) {
326 that
.view
.barsShow();
328 if (auto_connect_details
.channel
) {
329 that
.controlbox
.processInput('/JOIN ' + auto_connect_details
.channel
+ ' ' + auto_connect_details
.channel_key
);
337 gw
.on('disconnect', function (event
) {
338 var msg
= 'You have been disconnected. Attempting to reconnect for you..';
339 that
.message
.text(msg
, {timeout
: 10000});
341 that
.view
.$el
.removeClass('connected');
343 // Mention the disconnection on every channel
344 $.each(_kiwi
.app
.connections
.getByConnectionId(0).panels
.models
, function (idx
, panel
) {
345 if (!panel
|| !panel
.isChannel()) return;
346 panel
.addMsg('', msg
, 'action quit');
348 _kiwi
.app
.connections
.getByConnectionId(0).panels
.server
.addMsg('', msg
, 'action quit');
352 gw
.on('reconnecting', function (event
) {
353 msg
= 'You have been disconnected. Attempting to reconnect again in ' + (event
.delay
/1000) + ' seconds..';
354 _kiwi
.app
.connections
.getByConnectionId(0).panels
.server
.addMsg('', msg
, 'action quit');
356 gw
.on('onconnect', function (event
) {
357 that
.view
.$el
.addClass('connected');
358 if (gw_stat
!== 1) return;
360 var msg
= 'It\'s OK, you\'re connected again :)';
361 that
.message
.text(msg
, {timeout
: 5000});
363 // Mention the disconnection on every channel
364 $.each(_kiwi
.app
.connections
.getByConnectionId(0).panels
.models
, function (idx
, panel
) {
365 if (!panel
|| !panel
.isChannel()) return;
366 panel
.addMsg('', msg
, 'action join');
368 _kiwi
.app
.connections
.getByConnectionId(0).panels
.server
.addMsg('', msg
, 'action join');
379 * Bind to certain commands that may be typed into the control box
381 this.bindControllboxCommands = function (controlbox
) {
383 $.extend(controlbox
.preprocessor
.aliases
, {
386 '/me': '/action $1+',
390 // Op related aliases
391 '/op': '/quote mode $channel +o $1+',
392 '/deop': '/quote mode $channel -o $1+',
393 '/hop': '/quote mode $channel +h $1+',
394 '/dehop': '/quote mode $channel -h $1+',
395 '/voice': '/quote mode $channel +v $1+',
396 '/devoice': '/quote mode $channel -v $1+',
397 '/k': '/kick $channel $1+',
400 '/slap': '/me slaps $1 around a bit with a large trout'
403 controlbox
.on('unknown_command', unknownCommand
);
405 controlbox
.on('command', allCommands
);
406 controlbox
.on('command:msg', msgCommand
);
408 controlbox
.on('command:action', actionCommand
);
410 controlbox
.on('command:join', joinCommand
);
412 controlbox
.on('command:part', partCommand
);
414 controlbox
.on('command:nick', function (ev
) {
415 _kiwi
.gateway
.changeNick(null, ev
.params
[0]);
418 controlbox
.on('command:query', queryCommand
);
420 controlbox
.on('command:topic', topicCommand
);
422 controlbox
.on('command:notice', noticeCommand
);
424 controlbox
.on('command:quote', quoteCommand
);
426 controlbox
.on('command:kick', kickCommand
);
428 controlbox
.on('command:clear', clearCommand
);
430 controlbox
.on('command:ctcp', ctcpCommand
);
432 controlbox
.on('command:server', serverCommand
);
435 controlbox
.on('command:css', function (ev
) {
436 var queryString
= '?reload=' + new Date().getTime();
437 $('link[rel="stylesheet"]').each(function () {
438 this.href
= this.href
.replace(/\?.*|$/, queryString
);
442 controlbox
.on('command:js', function (ev
) {
443 if (!ev
.params
[0]) return;
444 $script(ev
.params
[0] + '?' + (new Date().getTime()));
448 controlbox
.on('command:set', function (ev
) {
449 if (!ev
.params
[0]) return;
451 var setting
= ev
.params
[0],
454 // Do we have a second param to set a value?
458 value
= ev
.params
.join(' ');
460 // If we're setting a true boolean value..
461 if (value
=== 'true')
464 // If we're setting a false boolean value..
465 if (value
=== 'false')
468 // If we're setting a number..
469 if (parseInt(value
, 10).toString() === value
)
470 value
= parseInt(value
, 10);
472 _kiwi
.global
.settings
.set(setting
, value
);
475 // Read the value to the user
476 _kiwi
.app
.panels().active
.addMsg('', setting
+ ' = ' + _kiwi
.global
.settings
.get(setting
).toString());
480 controlbox
.on('command:save', function (ev
) {
481 _kiwi
.global
.settings
.save();
482 _kiwi
.app
.panels().active
.addMsg('', 'Settings have been saved');
486 controlbox
.on('command:alias', function (ev
) {
489 // No parameters passed so list them
491 $.each(controlbox
.preprocessor
.aliases
, function (name
, rule
) {
492 _kiwi
.app
.panels().server
.addMsg(' ', name
+ ' => ' + rule
);
497 // Deleting an alias?
498 if (ev
.params
[0] === 'del' || ev
.params
[0] === 'delete') {
500 if (name
[0] !== '/') name
= '/' + name
;
501 delete controlbox
.preprocessor
.aliases
[name
];
508 rule
= ev
.params
.join(' ');
510 // Make sure the name starts with a slash
511 if (name
[0] !== '/') name
= '/' + name
;
513 // Now actually add the alias
514 controlbox
.preprocessor
.aliases
[name
] = rule
;
518 controlbox
.on('command:ignore', function (ev
) {
519 var list
= _kiwi
.gateway
.get('ignore_list');
521 // No parameters passed so list them
523 if (list
.length
> 0) {
524 _kiwi
.app
.panels().active
.addMsg(' ', 'Ignored nicks:');
525 $.each(list
, function (idx
, ignored_pattern
) {
526 _kiwi
.app
.panels().active
.addMsg(' ', ignored_pattern
);
529 _kiwi
.app
.panels().active
.addMsg(' ', 'Not ignoring anybody');
534 // We have a parameter, so add it
535 list
.push(ev
.params
[0]);
536 _kiwi
.gateway
.set('ignore_list', list
);
537 _kiwi
.app
.panels().active
.addMsg(' ', 'Ignoring ' + ev
.params
[0]);
541 controlbox
.on('command:unignore', function (ev
) {
542 var list
= _kiwi
.gateway
.get('ignore_list');
545 _kiwi
.app
.panels().active
.addMsg(' ', 'Specifiy which nick you wish to stop ignoring');
549 list
= _
.reject(list
, function(pattern
) {
550 return pattern
=== ev
.params
[0];
553 _kiwi
.gateway
.set('ignore_list', list
);
555 _kiwi
.app
.panels().active
.addMsg(' ', 'Stopped ignoring ' + ev
.params
[0]);
559 controlbox
.on('command:applet', appletCommand
);
560 controlbox
.on('command:settings', settingsCommand
);
561 controlbox
.on('command:script', scriptCommand
);
564 // A fallback action. Send a raw command to the server
565 function unknownCommand (ev
) {
566 var raw_cmd
= ev
.command
+ ' ' + ev
.params
.join(' ');
567 console
.log('RAW: ' + raw_cmd
);
568 _kiwi
.gateway
.raw(null, raw_cmd
);
571 function allCommands (ev
) {}
573 function joinCommand (ev
) {
574 var panels
, channel_names
;
576 channel_names
= ev
.params
.join(' ').split(',');
577 panels
= that
.connections
.active_connection
.createAndJoinChannels(channel_names
);
579 // Show the last channel if we have one
581 panels
[panels
.length
- 1].view
.show();
584 function queryCommand (ev
) {
585 var destination
, panel
;
587 destination
= ev
.params
[0];
589 // Check if we have the panel already. If not, create it
590 panel
= that
.connections
.active_connection
.panels
.getByName(destination
);
592 panel
= new _kiwi
.model
.Query({name
: destination
});
593 that
.connections
.active_connection
.panels
.add(panel
);
596 if (panel
) panel
.view
.show();
600 function msgCommand (ev
) {
601 var destination
= ev
.params
[0],
602 panel
= that
.connections
.active_connection
.panels
.getByName(destination
) || that
.panels().server
;
606 panel
.addMsg(_kiwi
.app
.connections
.active_connection
.get('nick'), ev
.params
.join(' '));
607 _kiwi
.gateway
.privmsg(null, destination
, ev
.params
.join(' '));
610 function actionCommand (ev
) {
611 if (_kiwi
.app
.panels().active
.isServer()) {
615 var panel
= _kiwi
.app
.panels().active
;
616 panel
.addMsg('', '* ' + _kiwi
.app
.connections
.active_connection
.get('nick') + ' ' + ev
.params
.join(' '), 'action');
617 _kiwi
.gateway
.action(null, panel
.get('name'), ev
.params
.join(' '));
620 function partCommand (ev
) {
621 if (ev
.params
.length
=== 0) {
622 _kiwi
.gateway
.part(null, _kiwi
.app
.panels().active
.get('name'));
624 _
.each(ev
.params
, function (channel
) {
625 _kiwi
.gateway
.part(null, channel
);
630 function topicCommand (ev
) {
633 if (ev
.params
.length
=== 0) return;
635 if (that
.isChannelName(ev
.params
[0])) {
636 channel_name
= ev
.params
[0];
639 channel_name
= _kiwi
.app
.panels().active
.get('name');
642 _kiwi
.gateway
.topic(null, channel_name
, ev
.params
.join(' '));
645 function noticeCommand (ev
) {
648 // Make sure we have a destination and some sort of message
649 if (ev
.params
.length
<= 1) return;
651 destination
= ev
.params
[0];
654 _kiwi
.gateway
.notice(null, destination
, ev
.params
.join(' '));
657 function quoteCommand (ev
) {
658 var raw
= ev
.params
.join(' ');
659 _kiwi
.gateway
.raw(null, raw
);
662 function kickCommand (ev
) {
663 var nick
, panel
= _kiwi
.app
.panels().active
;
665 if (!panel
.isChannel()) return;
667 // Make sure we have a nick
668 if (ev
.params
.length
=== 0) return;
673 _kiwi
.gateway
.kick(null, panel
.get('name'), nick
, ev
.params
.join(' '));
676 function clearCommand (ev
) {
677 // Can't clear a server or applet panel
678 if (_kiwi
.app
.panels().active
.isServer() || _kiwi
.app
.panels().active
.isApplet()) {
682 if (_kiwi
.app
.panels().active
.clearMessages
) {
683 _kiwi
.app
.panels().active
.clearMessages();
687 function ctcpCommand(ev
) {
690 // Make sure we have a target and a ctcp type (eg. version, time)
691 if (ev
.params
.length
< 2) return;
693 target
= ev
.params
[0];
699 _kiwi
.gateway
.ctcp(null, true, type
, target
, ev
.params
.join(' '));
702 function settingsCommand (ev
) {
703 var settings
= _kiwi
.model
.Applet
.loadOnce('kiwi_settings');
704 settings
.view
.show();
707 function scriptCommand (ev
) {
708 var editor
= _kiwi
.model
.Applet
.loadOnce('kiwi_script_editor');
712 function appletCommand (ev
) {
713 if (!ev
.params
[0]) return;
715 var panel
= new _kiwi
.model
.Applet();
718 // Url and name given
719 panel
.load(ev
.params
[0], ev
.params
[1]);
721 // Load a pre-loaded applet
722 if (_kiwi
.applets
[ev
.params
[0]]) {
723 panel
.load(new _kiwi
.applets
[ev
.params
[0]]());
725 _kiwi
.app
.panels().server
.addMsg('', 'Applet "' + ev
.params
[0] + '" does not exist');
730 _kiwi
.app
.connections
.active_connection
.panels
.add(panel
);
735 function serverCommand (ev
) {
736 var server
, port
, ssl
, password
, nick
,
739 // If no server address given, show the new connection dialog
741 tmp
= new _kiwi
.view
.MenuBox('New Connection');
742 tmp
.addItem('new_connection', new _kiwi
.model
.NewConnection().view
.$el
);
745 // Center screen the dialog
747 top
: (that
.view
.$el
.height() / 2) - (tmp
.$el
.height() / 2),
748 left
: (that
.view
.$el
.width() / 2) - (tmp
.$el
.width() / 2)
754 // Port given in 'host:port' format and no specific port given after a space
755 if (ev
.params
[0].indexOf(':') > 0) {
756 tmp
= ev
.params
[0].split(':');
760 password
= ev
.params
[1] || undefined;
763 // Server + port given as 'host port'
764 server
= ev
.params
[0];
765 port
= ev
.params
[1] || 6667;
767 password
= ev
.params
[2] || undefined;
770 // + in the port means SSL
771 if (port
.toString()[0] === '+') {
773 port
= parseInt(port
.substring(1), 10);
778 // Default port if one wasn't found
781 // Use the same nick as we currently have
782 nick
= _kiwi
.app
.connections
.active_connection
.get('nick');
784 _kiwi
.app
.panels().active
.addMsg('', 'Connecting to ' + server
+ ':' + port
.toString() + '..');
786 _kiwi
.gateway
.newConnection({
792 }, function(err
, new_connection
) {
794 _kiwi
.app
.panels().active
.addMsg('', 'Error connecting to ' + server
+ ':' + port
.toString() + ' (' + err
.toString() + ')');
802 this.isChannelName = function (channel_name
) {
803 var channel_prefix
= _kiwi
.gateway
.get('channel_prefix');
805 if (!channel_name
|| !channel_name
.length
) return false;
806 return (channel_prefix
.indexOf(channel_name
[0]) > -1);
813 model
= Backbone
.Model
.extend(new model());
815 return new model(arguments
);