142b30ab3e1f643be5def0a08547db05c8811ac1
[KiwiIRC.git] / client / assets / dev / model_application.js
1 _kiwi.model.Application = function () {
2 // Set to a reference to this object within initialize()
3 var that = null;
4
5 // The auto connect details entered into the server select box
6 var auto_connect_details = {};
7
8
9 var model = function () {
10 /** _kiwi.view.Application */
11 this.view = null;
12
13 /** _kiwi.view.StatusMessage */
14 this.message = null;
15
16 /* Address for the kiwi server */
17 this.kiwi_server = null;
18
19 this.initialize = function (options) {
20 that = this;
21
22 if (options[0].container) {
23 this.set('container', options[0].container);
24 }
25
26 // The base url to the kiwi server
27 this.set('base_path', options[0].base_path ? options[0].base_path : '/kiwi');
28
29 // Any options sent down from the server
30 this.server_settings = options[0].server_settings || {};
31
32 // Best guess at where the kiwi server is
33 this.detectKiwiServer();
34
35 // Takes instances of model_network
36 this.connections = new _kiwi.model.NetworkPanelList();
37 };
38
39
40 this.start = function () {
41 // Only debug if set in the querystring
42 if (!getQueryVariable('debug')) {
43 manageDebug(false);
44 } else {
45 //manageDebug(true);
46 }
47
48 // Set the gateway up
49 _kiwi.gateway = new _kiwi.model.Gateway();
50 this.bindGatewayCommands(_kiwi.gateway);
51
52 this.initializeClient();
53 this.initializeGlobals();
54
55 this.view.barsHide(true);
56
57 this.showIntialConenctionDialog();
58 };
59
60
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';
65 } else {
66 // Assume the kiwi server is on the same server
67 this.kiwi_server = window.location.protocol + '//' + window.location.host;
68 }
69 };
70
71
72 this.showIntialConenctionDialog = function() {
73 var connection_dialog = new _kiwi.model.NewConnection();
74 this.populateDefaultServerSettings(connection_dialog);
75
76 connection_dialog.view.$el.addClass('initial');
77 this.view.$el.find('.panel_container:first').append(connection_dialog.view.$el);
78
79 // TODO: Shouldn't really be here but it's not working in the view.. :/
80 // Hack for firefox browers: Focus is not given on this event loop iteration
81 setTimeout(function(){
82 connection_dialog.view.$el.find('.nick').select();
83 }, 0);
84
85 // Once connected, close this dialog and remove its own event
86 var fn = function() {
87 connection_dialog.view.$el.slideUp(function() {
88 console.log('disposing connectio dialog');
89 connection_dialog.view.dispose();
90 connection_dialog = null;
91
92 _kiwi.gateway.off('onconnect', fn);
93 });
94
95 };
96 _kiwi.gateway.on('onconnect', fn);
97 };
98
99
100 this.initializeClient = function () {
101 this.view = new _kiwi.view.Application({model: this, el: this.get('container')});
102
103 // Applets panel list
104 this.applet_panels = new _kiwi.model.PanelList();
105 this.applet_panels.view.$el.addClass('panellist applets');
106 this.view.$el.find('#tabs').append(this.applet_panels.view.$el);
107
108 /**
109 * Set the UI components up
110 */
111 this.controlbox = new _kiwi.view.ControlBox({el: $('#controlbox')[0]});
112 this.bindControllboxCommands(this.controlbox);
113
114 this.topicbar = new _kiwi.view.TopicBar({el: $('#topic')[0]});
115
116 new _kiwi.view.AppToolbar({el: $('#toolbar .app_tools')[0]});
117
118 this.message = new _kiwi.view.StatusMessage({el: $('#status_message')[0]});
119
120 this.resize_handle = new _kiwi.view.ResizeHandler({el: $('#memberlists_resize_handle')[0]});
121
122 // Rejigg the UI sizes
123 this.view.doLayout();
124 };
125
126
127 this.initializeGlobals = function () {
128 _kiwi.global.connections = this.connections;
129
130 _kiwi.global.components.Applet = _kiwi.model.Applet;
131 _kiwi.global.components.Panel =_kiwi.model.Panel;
132 };
133
134
135 this.populateDefaultServerSettings = function (new_connection_dialog) {
136 var parts;
137 var defaults = {
138 nick: getQueryVariable('nick') || '',
139 server: '',
140 port: 6667,
141 ssl: false,
142 channel: window.location.hash || '#chat',
143 channel_key: ''
144 };
145 var uricheck;
146
147
148 /**
149 * Get any settings set by the server
150 * These settings may be changed in the server selection dialog
151 */
152 if (this.server_settings.client) {
153 if (this.server_settings.client.nick)
154 defaults.nick = this.server_settings.client.nick;
155
156 if (this.server_settings.client.server)
157 defaults.server = this.server_settings.client.server;
158
159 if (this.server_settings.client.port)
160 defaults.port = this.server_settings.client.port;
161
162 if (this.server_settings.client.ssl)
163 defaults.ssl = this.server_settings.client.ssl;
164
165 if (this.server_settings.client.channel)
166 defaults.channel = this.server_settings.client.channel;
167 }
168
169
170
171 /**
172 * Get any settings passed in the URL
173 * These settings may be changed in the server selection dialog
174 */
175
176 // Process the URL part by part, extracting as we go
177 parts = window.location.pathname.toString().replace(this.get('base_path'), '').split('/');
178
179 if (parts.length > 0) {
180 parts.shift();
181
182 if (parts.length > 0 && parts[0]) {
183 // 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.
184 uricheck = parts[0].substr(0, 7).toLowerCase();
185 if ((uricheck === 'ircs%3a') || (uricheck.substr(0,6) === 'irc%3a')) {
186 parts[0] = decodeURIComponent(parts[0]);
187 // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
188 uricheck = /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts[0]);
189 /*
190 uricheck[1] = ssl (optional)
191 uricheck[2] = host
192 uricheck[3] = port (optional)
193 uricheck[4] = channel (optional)
194 uricheck[5] = channel key (optional, channel must also be set)
195 */
196 if (uricheck) {
197 if (typeof uricheck[1] !== 'undefined') {
198 defaults.ssl = true;
199 if (defaults.port === 6667) {
200 defaults.port = 6697;
201 }
202 }
203 defaults.server = uricheck[2];
204 if (typeof uricheck[3] !== 'undefined') {
205 defaults.port = uricheck[3];
206 }
207 if (typeof uricheck[4] !== 'undefined') {
208 defaults.channel = '#' + uricheck[4];
209 if (typeof uricheck[5] !== 'undefined') {
210 defaults.channel_key = uricheck[5];
211 }
212 }
213 }
214 parts = [];
215 } else {
216 // Extract the port+ssl if we find one
217 if (parts[0].search(/:/) > 0) {
218 defaults.port = parts[0].substring(parts[0].search(/:/) + 1);
219 defaults.server = parts[0].substring(0, parts[0].search(/:/));
220 if (defaults.port[0] === '+') {
221 defaults.port = parseInt(defaults.port.substring(1), 10);
222 defaults.ssl = true;
223 } else {
224 defaults.ssl = false;
225 }
226
227 } else {
228 defaults.server = parts[0];
229 }
230
231 parts.shift();
232 }
233 }
234
235 if (parts.length > 0 && parts[0]) {
236 defaults.channel = '#' + parts[0];
237 parts.shift();
238 }
239 }
240
241 // If any settings have been given by the server.. override any auto detected settings
242 /**
243 * Get any server restrictions as set in the server config
244 * These settings can not be changed in the server selection dialog
245 */
246 if (this.server_settings && this.server_settings.connection) {
247 if (this.server_settings.connection.server) {
248 defaults.server = this.server_settings.connection.server;
249 }
250
251 if (this.server_settings.connection.port) {
252 defaults.port = this.server_settings.connection.port;
253 }
254
255 if (this.server_settings.connection.ssl) {
256 defaults.ssl = this.server_settings.connection.ssl;
257 }
258
259 if (this.server_settings.connection.channel) {
260 defaults.channel = this.server_settings.connection.channel;
261 }
262
263 if (this.server_settings.connection.nick) {
264 defaults.nick = this.server_settings.connection.nick;
265 }
266 }
267
268 // Set any random numbers if needed
269 defaults.nick = defaults.nick.replace('?', Math.floor(Math.random() * 100000).toString());
270
271 // Populate the server select box with defaults
272 new_connection_dialog.view.populateFields(defaults);
273 };
274
275
276 this.panels = (function() {
277 var fn = function(panel_type) {
278 var panels;
279
280 // Default panel type
281 panel_type = panel_type || 'connections';
282
283 switch (panel_type) {
284 case 'connections':
285 panels = this.connections.panels();
286 break;
287 case 'applets':
288 panels = this.applet_panels.models;
289 break;
290 }
291
292 // Active panels / server
293 panels.active = this.connections.active_panel;
294 panels.server = this.connections.active_connection.panels.server;
295
296 return panels;
297 };
298
299 _.extend(fn, Backbone.Events);
300
301 return fn;
302 })();
303
304
305 this.bindGatewayCommands = function (gw) {
306 gw.on('onconnect', function (event) {
307 that.view.barsShow();
308
309 if (auto_connect_details.channel) {
310 that.controlbox.processInput('/JOIN ' + auto_connect_details.channel + ' ' + auto_connect_details.channel_key);
311 }
312 });
313
314
315 (function () {
316 var gw_stat = 0;
317
318 gw.on('disconnect', function (event) {
319 var msg = 'You have been disconnected. Attempting to reconnect for you..';
320 that.message.text(msg, {timeout: 10000});
321
322 that.view.$el.removeClass('connected');
323
324 // Mention the disconnection on every channel
325 $.each(_kiwi.app.connections.getByConnectionId(0).panels.models, function (idx, panel) {
326 if (!panel || !panel.isChannel()) return;
327 panel.addMsg('', msg, 'action quit');
328 });
329 _kiwi.app.connections.getByConnectionId(0).panels.server.addMsg('', msg, 'action quit');
330
331 gw_stat = 1;
332 });
333 gw.on('reconnecting', function (event) {
334 msg = 'You have been disconnected. Attempting to reconnect again in ' + (event.delay/1000) + ' seconds..';
335 _kiwi.app.connections.getByConnectionId(0).panels.server.addMsg('', msg, 'action quit');
336 });
337 gw.on('onconnect', function (event) {
338 that.view.$el.addClass('connected');
339 if (gw_stat !== 1) return;
340
341 var msg = 'It\'s OK, you\'re connected again :)';
342 that.message.text(msg, {timeout: 5000});
343
344 // Mention the disconnection on every channel
345 $.each(_kiwi.app.connections.getByConnectionId(0).panels.models, function (idx, panel) {
346 if (!panel || !panel.isChannel()) return;
347 panel.addMsg('', msg, 'action join');
348 });
349 _kiwi.app.connections.getByConnectionId(0).panels.server.addMsg('', msg, 'action join');
350
351 gw_stat = 0;
352 });
353 })();
354
355 };
356
357
358
359 /**
360 * Bind to certain commands that may be typed into the control box
361 */
362 this.bindControllboxCommands = function (controlbox) {
363 // Default aliases
364 $.extend(controlbox.preprocessor.aliases, {
365 // General aliases
366 '/p': '/part $1+',
367 '/me': '/action $1+',
368 '/j': '/join $1+',
369 '/q': '/query $1+',
370
371 // Op related aliases
372 '/op': '/quote mode $channel +o $1+',
373 '/deop': '/quote mode $channel -o $1+',
374 '/hop': '/quote mode $channel +h $1+',
375 '/dehop': '/quote mode $channel -h $1+',
376 '/voice': '/quote mode $channel +v $1+',
377 '/devoice': '/quote mode $channel -v $1+',
378 '/k': '/kick $channel $1+',
379
380 // Misc aliases
381 '/slap': '/me slaps $1 around a bit with a large trout'
382 });
383
384 controlbox.on('unknown_command', unknownCommand);
385
386 controlbox.on('command', allCommands);
387 controlbox.on('command:msg', msgCommand);
388
389 controlbox.on('command:action', actionCommand);
390
391 controlbox.on('command:join', joinCommand);
392
393 controlbox.on('command:part', partCommand);
394
395 controlbox.on('command:nick', function (ev) {
396 _kiwi.gateway.changeNick(ev.params[0]);
397 });
398
399 controlbox.on('command:query', queryCommand);
400
401 controlbox.on('command:topic', topicCommand);
402
403 controlbox.on('command:notice', noticeCommand);
404
405 controlbox.on('command:quote', quoteCommand);
406
407 controlbox.on('command:kick', kickCommand);
408
409 controlbox.on('command:clear', clearCommand);
410
411 controlbox.on('command:ctcp', ctcpCommand);
412
413 controlbox.on('command:server', serverCommand);
414
415
416 controlbox.on('command:css', function (ev) {
417 var queryString = '?reload=' + new Date().getTime();
418 $('link[rel="stylesheet"]').each(function () {
419 this.href = this.href.replace(/\?.*|$/, queryString);
420 });
421 });
422
423 controlbox.on('command:js', function (ev) {
424 if (!ev.params[0]) return;
425 $script(ev.params[0] + '?' + (new Date().getTime()));
426 });
427
428
429 controlbox.on('command:set', function (ev) {
430 if (!ev.params[0]) return;
431
432 var setting = ev.params[0],
433 value;
434
435 // Do we have a second param to set a value?
436 if (ev.params[1]) {
437 ev.params.shift();
438
439 value = ev.params.join(' ');
440
441 // If we're setting a true boolean value..
442 if (value === 'true')
443 value = true;
444
445 // If we're setting a false boolean value..
446 if (value === 'false')
447 value = false;
448
449 // If we're setting a number..
450 if (parseInt(value, 10).toString() === value)
451 value = parseInt(value, 10);
452
453 _kiwi.global.settings.set(setting, value);
454 }
455
456 // Read the value to the user
457 _kiwi.app.panels().active.addMsg('', setting + ' = ' + _kiwi.global.settings.get(setting).toString());
458 });
459
460
461 controlbox.on('command:save', function (ev) {
462 _kiwi.global.settings.save();
463 _kiwi.app.panels().active.addMsg('', 'Settings have been saved');
464 });
465
466
467 controlbox.on('command:alias', function (ev) {
468 var name, rule;
469
470 // No parameters passed so list them
471 if (!ev.params[1]) {
472 $.each(controlbox.preprocessor.aliases, function (name, rule) {
473 _kiwi.app.panels().server.addMsg(' ', name + ' => ' + rule);
474 });
475 return;
476 }
477
478 // Deleting an alias?
479 if (ev.params[0] === 'del' || ev.params[0] === 'delete') {
480 name = ev.params[1];
481 if (name[0] !== '/') name = '/' + name;
482 delete controlbox.preprocessor.aliases[name];
483 return;
484 }
485
486 // Add the alias
487 name = ev.params[0];
488 ev.params.shift();
489 rule = ev.params.join(' ');
490
491 // Make sure the name starts with a slash
492 if (name[0] !== '/') name = '/' + name;
493
494 // Now actually add the alias
495 controlbox.preprocessor.aliases[name] = rule;
496 });
497
498
499 controlbox.on('command:ignore', function (ev) {
500 var list = _kiwi.gateway.get('ignore_list');
501
502 // No parameters passed so list them
503 if (!ev.params[0]) {
504 if (list.length > 0) {
505 _kiwi.app.panels().active.addMsg(' ', 'Ignored nicks:');
506 $.each(list, function (idx, ignored_pattern) {
507 _kiwi.app.panels().active.addMsg(' ', ignored_pattern);
508 });
509 } else {
510 _kiwi.app.panels().active.addMsg(' ', 'Not ignoring anybody');
511 }
512 return;
513 }
514
515 // We have a parameter, so add it
516 list.push(ev.params[0]);
517 _kiwi.gateway.set('ignore_list', list);
518 _kiwi.app.panels().active.addMsg(' ', 'Ignoring ' + ev.params[0]);
519 });
520
521
522 controlbox.on('command:unignore', function (ev) {
523 var list = _kiwi.gateway.get('ignore_list');
524
525 if (!ev.params[0]) {
526 _kiwi.app.panels().active.addMsg(' ', 'Specifiy which nick you wish to stop ignoring');
527 return;
528 }
529
530 list = _.reject(list, function(pattern) {
531 return pattern === ev.params[0];
532 });
533
534 _kiwi.gateway.set('ignore_list', list);
535
536 _kiwi.app.panels().active.addMsg(' ', 'Stopped ignoring ' + ev.params[0]);
537 });
538
539
540 controlbox.on('command:applet', appletCommand);
541 controlbox.on('command:settings', settingsCommand);
542 controlbox.on('command:script', scriptCommand);
543 };
544
545 // A fallback action. Send a raw command to the server
546 function unknownCommand (ev) {
547 var raw_cmd = ev.command + ' ' + ev.params.join(' ');
548 console.log('RAW: ' + raw_cmd);
549 _kiwi.gateway.raw(raw_cmd);
550 }
551
552 function allCommands (ev) {}
553
554 function joinCommand (ev) {
555 var panels, channel_names;
556
557 channel_names = ev.params.join(' ').split(',');
558 panels = that.connections.active_connection.createAndJoinChannels(channel_names);
559
560 // Show the last channel if we have one
561 if (panels)
562 panels[panels.length - 1].view.show();
563 }
564
565 function queryCommand (ev) {
566 var destination, panel;
567
568 destination = ev.params[0];
569
570 // Check if we have the panel already. If not, create it
571 panel = that.connections.active_connection.panels.getByName(destination);
572 if (!panel) {
573 panel = new _kiwi.model.Query({name: destination});
574 that.connections.active_connection.panels.add(panel);
575 }
576
577 if (panel) panel.view.show();
578
579 }
580
581 function msgCommand (ev) {
582 var destination = ev.params[0],
583 panel = that.connections.active_connection.panels.getByName(destination) || that.panels().server;
584
585 ev.params.shift();
586
587 panel.addMsg(_kiwi.app.connections.active_connection.get('nick'), ev.params.join(' '));
588 _kiwi.gateway.privmsg(destination, ev.params.join(' '));
589 }
590
591 function actionCommand (ev) {
592 if (_kiwi.app.panels().active.isServer()) {
593 return;
594 }
595
596 var panel = _kiwi.app.panels().active;
597 panel.addMsg('', '* ' + _kiwi.app.connections.active_connection.get('nick') + ' ' + ev.params.join(' '), 'action');
598 _kiwi.gateway.action(panel.get('name'), ev.params.join(' '));
599 }
600
601 function partCommand (ev) {
602 if (ev.params.length === 0) {
603 _kiwi.gateway.part(_kiwi.app.panels().active.get('name'));
604 } else {
605 _.each(ev.params, function (channel) {
606 _kiwi.gateway.part(channel);
607 });
608 }
609 }
610
611 function topicCommand (ev) {
612 var channel_name;
613
614 if (ev.params.length === 0) return;
615
616 if (that.isChannelName(ev.params[0])) {
617 channel_name = ev.params[0];
618 ev.params.shift();
619 } else {
620 channel_name = _kiwi.app.panels().active.get('name');
621 }
622
623 _kiwi.gateway.topic(channel_name, ev.params.join(' '));
624 }
625
626 function noticeCommand (ev) {
627 var destination;
628
629 // Make sure we have a destination and some sort of message
630 if (ev.params.length <= 1) return;
631
632 destination = ev.params[0];
633 ev.params.shift();
634
635 _kiwi.gateway.notice(destination, ev.params.join(' '));
636 }
637
638 function quoteCommand (ev) {
639 var raw = ev.params.join(' ');
640 _kiwi.gateway.raw(raw);
641 }
642
643 function kickCommand (ev) {
644 var nick, panel = _kiwi.app.panels().active;
645
646 if (!panel.isChannel()) return;
647
648 // Make sure we have a nick
649 if (ev.params.length === 0) return;
650
651 nick = ev.params[0];
652 ev.params.shift();
653
654 _kiwi.gateway.kick(panel.get('name'), nick, ev.params.join(' '));
655 }
656
657 function clearCommand (ev) {
658 // Can't clear a server or applet panel
659 if (_kiwi.app.panels().active.isServer() || _kiwi.app.panels().active.isApplet()) {
660 return;
661 }
662
663 if (_kiwi.app.panels().active.clearMessages) {
664 _kiwi.app.panels().active.clearMessages();
665 }
666 }
667
668 function ctcpCommand(ev) {
669 var target, type;
670
671 // Make sure we have a target and a ctcp type (eg. version, time)
672 if (ev.params.length < 2) return;
673
674 target = ev.params[0];
675 ev.params.shift();
676
677 type = ev.params[0];
678 ev.params.shift();
679
680 _kiwi.gateway.ctcp(true, type, target, ev.params.join(' '));
681 }
682
683 function settingsCommand (ev) {
684 var settings = _kiwi.model.Applet.loadOnce('kiwi_settings');
685 settings.view.show();
686 }
687
688 function scriptCommand (ev) {
689 var editor = _kiwi.model.Applet.loadOnce('kiwi_script_editor');
690 editor.view.show();
691 }
692
693 function appletCommand (ev) {
694 if (!ev.params[0]) return;
695
696 var panel = new _kiwi.model.Applet();
697
698 if (ev.params[1]) {
699 // Url and name given
700 panel.load(ev.params[0], ev.params[1]);
701 } else {
702 // Load a pre-loaded applet
703 if (_kiwi.applets[ev.params[0]]) {
704 panel.load(new _kiwi.applets[ev.params[0]]());
705 } else {
706 _kiwi.app.panels().server.addMsg('', 'Applet "' + ev.params[0] + '" does not exist');
707 return;
708 }
709 }
710
711 _kiwi.app.connections.active_connection.panels.add(panel);
712 panel.view.show();
713 }
714
715
716 function serverCommand (ev) {
717 var server, port, ssl, password, nick,
718 tmp;
719
720 if (!ev.params[0]) return;
721
722 // Port given in 'host:port' format and no specific port given after a space
723 if (ev.params[0].indexOf(':') > 0) {
724 tmp = ev.params[0].split(':');
725 server = tmp[0];
726 port = tmp[1];
727
728 password = ev.params[1] || undefined;
729
730 } else {
731 // Server + port given as 'host port'
732 server = ev.params[0];
733 port = ev.params[1] || 6667;
734
735 password = ev.params[2] || undefined;
736 }
737
738 // + in the port means SSL
739 if (port.toString()[0] === '+') {
740 ssl = true;
741 port = parseInt(port.substring(1), 10);
742 } else {
743 ssl = false;
744 }
745
746 // Default port if one wasn't found
747 port = port || 6667;
748
749 // Use the same nick as we currently have
750 nick = _kiwi.app.connections.active_connection.get('nick');
751
752 _kiwi.app.panels().active.addMsg('', 'Connecting to ' + server + ':' + port.toString() + '..');
753
754 _kiwi.gateway.newConnection({
755 nick: nick,
756 host: server,
757 port: port,
758 ssl: ssl,
759 password: password
760 }, function(err, new_connection) {
761 if (err)
762 _kiwi.app.panels().active.addMsg('', 'Error connecting to ' + server + ':' + port.toString() + ' (' + err.toString() + ')');
763 });
764 }
765
766
767
768
769
770 this.isChannelName = function (channel_name) {
771 var channel_prefix = _kiwi.gateway.get('channel_prefix');
772
773 if (!channel_name || !channel_name.length) return false;
774 return (channel_prefix.indexOf(channel_name[0]) > -1);
775 };
776
777
778 };
779
780
781 model = Backbone.Model.extend(new model());
782
783 return new model(arguments);
784 };