5276cea41d22dacfa3073f597f1c2cbc47d4485e
[KiwiIRC.git] / js / front.js
1 /*jslint white:true, regexp: true, nomen: true, devel: true, undef: true, browser: true, continue: true, sloppy: true, forin: true, newcap: true, plusplus: true, maxerr: 50, indent: 4 */
2 /*global kiwi, _, io, $, iScroll, agent, touchscreen, init_data, plugs, plugins, registerTouches, randomString */
3 kiwi.front = {
4 cur_channel: '',
5 windows: {},
6 tabviews: {},
7 utilityviews: {},
8 boxes: {},
9
10 buffer: [],
11 buffer_pos: 0,
12
13 cache: {},
14
15 original_topic: '',
16
17 init: function () {
18 /*global Box, touch_scroll:true, Tabview */
19 var about_info, supportsOrientationChange, orientationEvent, scroll_opts, server_tabview;
20 kiwi.gateway.nick = 'kiwi_' + Math.ceil(100 * Math.random()) + Math.ceil(100 * Math.random());
21 kiwi.gateway.session_id = null;
22
23 $(kiwi.gateway).bind("onmsg", kiwi.front.onMsg);
24 $(kiwi.gateway).bind("onnotice", kiwi.front.onNotice);
25 $(kiwi.gateway).bind("onaction", kiwi.front.onAction);
26 $(kiwi.gateway).bind("onmotd", kiwi.front.onMOTD);
27 $(kiwi.gateway).bind("onoptions", kiwi.front.onOptions);
28 $(kiwi.gateway).bind("onconnect", kiwi.front.onConnect);
29 $(kiwi.gateway).bind("onconnect_fail", kiwi.front.onConnectFail);
30 $(kiwi.gateway).bind("ondisconnect", kiwi.front.onDisconnect);
31 $(kiwi.gateway).bind("onreconnecting", kiwi.front.onReconnecting);
32 $(kiwi.gateway).bind("onnick", kiwi.front.onNick);
33 $(kiwi.gateway).bind("onuserlist", kiwi.front.onUserList);
34 $(kiwi.gateway).bind("onuserlist_end", kiwi.front.onUserListEnd);
35 $(kiwi.gateway).bind("onlist_start", kiwi.front.onChannelListStart);
36 $(kiwi.gateway).bind("onlist_channel", kiwi.front.onChannelList);
37 $(kiwi.gateway).bind("onlist_end", kiwi.front.onChannelListEnd);
38 $(kiwi.gateway).bind("onjoin", kiwi.front.onJoin);
39 $(kiwi.gateway).bind("ontopic", kiwi.front.onTopic);
40 $(kiwi.gateway).bind("onpart", kiwi.front.onPart);
41 $(kiwi.gateway).bind("onkick", kiwi.front.onKick);
42 $(kiwi.gateway).bind("onquit", kiwi.front.onQuit);
43 $(kiwi.gateway).bind("onmode", kiwi.front.onMode);
44 $(kiwi.gateway).bind("onwhois", kiwi.front.onWhois);
45 $(kiwi.gateway).bind("onsync", kiwi.front.onSync);
46 $(kiwi.gateway).bind("onchannel_redirect", kiwi.front.onChannelRedirect);
47 $(kiwi.gateway).bind("ondebug", kiwi.front.onDebug);
48 $(kiwi.gateway).bind("onctcp_request", kiwi.front.onCTCPRequest);
49 $(kiwi.gateway).bind("onctcp_response", kiwi.front.onCTCPResponse);
50 $(kiwi.gateway).bind("onirc_error", kiwi.front.onIRCError);
51 $(kiwi.gateway).bind("onkiwi", kiwi.front.onKiwi);
52
53 this.buffer = [];
54
55 // Build the about box
56 kiwi.front.boxes.about = new Box("about");
57 about_info = 'UI adapted for ' + agent;
58 if (touchscreen) {
59 about_info += ' touchscreen ';
60 }
61 about_info += 'usage';
62 $('#tmpl_about_box').tmpl({
63 about: about_info
64 }).appendTo(kiwi.front.boxes.about.content);
65
66 //$(window).bind("beforeunload", function(){ kiwi.gateway.quit(); });
67
68 if (touchscreen) {
69 $('#kiwi').addClass('touchscreen');
70
71 // Single touch scrolling through scrollback for touchscreens
72 scroll_opts = {};
73 touch_scroll = new iScroll('windows', scroll_opts);
74 }
75
76 kiwi.front.registerKeys();
77
78 $('#kiwi .toolbars').resize(kiwi.front.doLayoutSize);
79 $(window).resize(kiwi.front.doLayoutSize);
80
81 // Add the resizer for the userlist
82 $('<div id="nicklist_resize" style="position:absolute; cursor:w-resize; width:5px;"></div>').appendTo('#kiwi');
83 $('#nicklist_resize').draggable({axis: "x", drag: function () {
84 var t = $(this),
85 ul = $('#kiwi .userlist'),
86 new_width = $(document).width() - parseInt(t.css('left'), 10);
87
88 new_width = new_width - parseInt(ul.css('margin-left'), 10);
89 new_width = new_width - parseInt(ul.css('margin-right'), 10);
90
91 // Make sure we don't remove the userlist alltogether
92 if (new_width < 20) {
93 $(this).data('draggable').offset.click.left = 10;
94 console.log('whoaa');
95 }
96
97 Tabview.getCurrentTab().userlist.setWidth(new_width);
98 $('#windows').css('right', ul.outerWidth(true));
99 }});
100
101
102 $('#kiwi .formconnectwindow').submit(function () {
103 var netsel = $('#kiwi .formconnectwindow .network'),
104 netport = $('#kiwi .formconnectwindow .port'),
105 netssl = $('#kiwi .formconnectwindow .ssl'),
106 nick = $('#kiwi .formconnectwindow .nick'),
107 tmp;
108
109 if (nick.val() === '') {
110 nick.val('Nick please!');
111 nick.focus();
112 return false;
113 }
114
115 tmp = nick.val().split(' ');
116 kiwi.gateway.nick = tmp[0];
117
118 init_data.channel = $('#channel').val();
119
120 kiwi.front.doLayout();
121 try {
122 kiwi.front.run('/connect ' + netsel.val() + ' ' + netport.val() + ' ' + (netssl.attr('checked') ? 'true' : ''));
123 } catch (e) {
124 console.log(e);
125 }
126
127 $('#kiwi .connectwindow').slideUp('', kiwi.front.barsShow);
128 $('#windows').click(function () { $('#kiwi_msginput').focus(); });
129
130 return false;
131 });
132
133 supportsOrientationChange = (typeof window.onorientationchange !== undefined);
134 orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
135 if (window.addEventListener) {
136 window.addEventListener(orientationEvent, kiwi.front.doLayoutSize, false);
137 } else {
138 // < IE9
139 window.attachEvent(orientationEvent, kiwi.front.doLayoutSize, false);
140 }
141 //$('#kiwi').bind("resize", kiwi.front.doLayoutSize, false);
142
143 kiwi.front.doLayout();
144 kiwi.front.barsHide();
145
146 server_tabview = new Tabview('server');
147 server_tabview.userlist.setWidth(0); // Disable the userlist
148
149 // Any pre-defined nick?
150 if (typeof window.init_data.nick === "string") {
151 $('#kiwi .formconnectwindow .nick').val(init_data.nick);
152 }
153
154 // Any pre-defined channels?
155 if (typeof window.init_data.channel === 'string') {
156 $('#channel').val(init_data.channel);
157 }
158
159 // Fix for Opera inserting a spurious <br/>
160 $('#kiwi .cur_topic br').remove();
161
162 $('#kiwi .cur_topic').keydown(function (e) {
163 if (e.which === 13) {
164 // enter
165 e.preventDefault();
166 $(this).change();
167 $('#kiwi_msginput').focus();
168 } else if (e.which === 27) {
169 // escape
170 e.preventDefault();
171 $(this).text(kiwi.front.original_topic);
172 $('#kiwi_msginput').focus();
173 }
174 });
175 /*$('.cur_topic').live('keypress', function(e) {
176 if (e.keyCode === 13) {
177 // enter
178 e.preventDefault();
179 $(this).change();
180 $('#kiwi_msginput').focus();
181 } else if (e.keyCode === 27) {
182 // escape
183 e.preventDefault();
184 $(this).text(kiwi.front.original_topic);
185 }
186 });*/
187 $('.cur_topic').live('change', function () {
188 var chan, text;
189 text = $(this).text();
190 if (text !== kiwi.front.original_topic) {
191 chan = Tabview.getCurrentTab().name;
192 kiwi.gateway.setTopic(chan, text);
193 }
194 });
195
196
197 $('#windows a.chan').live('click', function () {
198 kiwi.front.joinChannel($(this).text());
199 return false;
200 });
201
202 kiwi.data.set('chanList', []);
203
204 (function () {
205 var i;
206 for (i in plugins) {
207 kiwi.plugs.loadPlugin(plugins[i]);
208 }
209 }());
210 },
211
212 doLayoutSize: function () {
213 var kiwi, toolbars, ul, n_top, n_bottom, nl;
214 kiwi = $('#kiwi');
215
216 if (kiwi.width() < 330 && !kiwi.hasClass('small_kiwi')) {
217 console.log("switching to small kiwi");
218 kiwi.removeClass('large_kiwi');
219 kiwi.addClass('small_kiwi');
220 } else if (kiwi.width() >= 330 && !kiwi.hasClass('large_kiwi')) {
221 kiwi.removeClass('small_kiwi');
222 kiwi.addClass('large_kiwi');
223 }
224
225 toolbars = $('#kiwi .cur_topic');
226 ul = $('#kiwi .userlist');
227
228 n_top = parseInt(toolbars.offset().top, 10) + parseInt(toolbars.outerHeight(true), 10);
229 n_bottom = $(document).height() - parseInt($('#kiwi .control').offset().top, 10);
230
231 $('#kiwi .windows').css({top: n_top + 'px', bottom: n_bottom + 'px'});
232 ul.css({top: n_top + 'px', bottom: n_bottom + 'px'});
233
234 nl = $('#nicklist_resize');
235 nl.css({top: n_top + 'px', bottom: n_bottom + 'px', left: $(document).width() - ul.outerWidth(true)});
236 },
237
238
239 doLayout: function () {
240 $('#kiwi .msginput .nick a').text(kiwi.gateway.nick);
241 $('#kiwi_msginput').val(' ');
242 $('#kiwi_msginput').focus();
243 },
244
245
246 joinChannel: function (chan_name) {
247 var chans = chan_name.split(','),
248 i,
249 chan,
250 tab;
251 for (i in chans) {
252 chan = chans[i];
253 tab = Tabview.getTab(chan);
254 if ((!tab) || (tab.safe_to_close === true)) {
255 kiwi.gateway.join(chan);
256 tab = new Tabview(chan);
257 } else {
258 tab.show();
259 }
260 }
261 },
262
263
264 run: function (msg) {
265 var parts, dest, t, pos, textRange, d, plugin_event, msg_sliced, tab;
266
267 // Run through any plugins
268 plugin_event = {command: msg};
269 plugin_event = kiwi.plugs.run('command_run', plugin_event);
270 if (!plugin_event || typeof plugin_event.command === 'undefined') {
271 return;
272 }
273
274 // Update msg if it's been changed by any plugins
275 msg = plugin_event.command.toString();
276
277
278 if (msg.substring(0, 1) === '/') {
279 console.log("running " + msg);
280 parts = msg.split(' ');
281 switch (parts[0].toLowerCase()) {
282 case '/j':
283 case '/join':
284 kiwi.front.joinChannel(parts[1]);
285 break;
286
287 case '/connect':
288 case '/server':
289 if (typeof parts[1] === 'undefined') {
290 alert('Usage: /connect servername [port] [ssl]');
291 break;
292 }
293
294 if (typeof parts[2] === 'undefined') {
295 parts[2] = 6667;
296 }
297
298 if ((typeof parts[3] === 'undefined') || !parts[3] || (parts[3] === 'false') || (parts[3] === 'no')) {
299 parts[3] = false;
300 } else {
301 parts[3] = true;
302 }
303
304 Tabview.getCurrentTab().addMsg(null, ' ', '=== Connecting to ' + parts[1] + ' on port ' + parts[2] + (parts[3] ? ' using SSL' : '') + '...', 'status');
305 kiwi.gateway.connect(parts[1], parts[2], parts[3]);
306 break;
307
308 case '/nick':
309 console.log("/nick");
310 if (parts[1] === undefined) {
311 console.log("calling show nick");
312 kiwi.front.showChangeNick();
313 } else {
314 console.log("sending raw");
315 kiwi.gateway.raw(msg.substring(1));
316 }
317 break;
318
319 case '/part':
320 if (typeof parts[1] === "undefined") {
321 if (Tabview.getCurrentTab().safe_to_close) {
322 Tabview.getCurrentTab().close();
323 } else {
324 kiwi.gateway.raw(msg.substring(1) + ' ' + Tabview.getCurrentTab().name);
325 }
326 } else {
327 kiwi.gateway.raw(msg.substring(1));
328 }
329 break;
330
331 case '/names':
332 if (typeof parts[1] !== "undefined") {
333 kiwi.gateway.raw(msg.substring(1));
334 }
335 break;
336
337 case '/debug':
338 kiwi.gateway.debug();
339 break;
340
341 case '/q':
342 case '/query':
343 if (typeof parts[1] !== "undefined") {
344 tab = new Tabview(parts[1]);
345 }
346 break;
347
348
349 case '/m':
350 case '/msg':
351 if (typeof parts[1] !== "undefined") {
352 msg_sliced = msg.split(' ').slice(2).join(' ');
353 kiwi.gateway.msg(parts[1], msg_sliced);
354
355 tab = Tabview.getTab(parts[1]);
356 if (!tab) {
357 tab = new Tabview(parts[1]);
358 }
359 tab.addMsg(null, kiwi.gateway.nick, msg_sliced);
360 }
361 break;
362
363 case '/k':
364 case '/kick':
365 if (typeof parts[1] === 'undefined') {
366 return;
367 }
368 kiwi.gateway.raw('KICK ' + Tabview.getCurrentTab().name + ' ' + msg.split(' ', 2)[1]);
369 break;
370
371 case '/quote':
372 kiwi.gateway.raw(msg.replace(/^\/quote /i, ''));
373 break;
374
375 case '/me':
376 tab = Tabview.getCurrentTab();
377 kiwi.gateway.action(tab.name, msg.substring(4));
378 //tab.addMsg(null, ' ', '* '+data.nick+' '+data.msg, 'color:green;');
379 tab.addMsg(null, ' ', '* ' + kiwi.gateway.nick + ' ' + msg.substring(4), 'action', 'color:#555;');
380 break;
381
382 case '/notice':
383 dest = parts[1];
384 msg = parts.slice(2).join(' ');
385
386 kiwi.gateway.notice(dest, msg);
387 this.onNotice({}, {nick: kiwi.gateway.nick, channel: dest, msg: msg});
388 break;
389
390 case '/win':
391 if (parts[1] !== undefined) {
392 kiwi.front.windowsShow(parseInt(parts[1], 10));
393 }
394 break;
395
396 case '/quit':
397 kiwi.gateway.quit(parts.slice(1).join(' '));
398 break;
399
400 case '/topic':
401 if (parts[1] === undefined) {
402 t = $('.cur_topic');
403 if (t.createTextRange) {
404 pos = t.text().length();
405 textRange = t.createTextRange();
406 textRange.collapse(true);
407 textRange.moveEnd(pos);
408 textRange.moveStart(pos);
409 textRange.select();
410 } else if (t.setSelectionRange) {
411 t.setSelectionRange(pos, pos);
412 }
413 } else {
414 kiwi.gateway.setTopic(Tabview.getCurrentTab().name, msg.split(' ', 2)[1]);
415 //kiwi.gateway.raw('TOPIC ' + Tabview.getCurrentTab().name + ' :' + msg.split(' ', 2)[1]);
416 }
417 break;
418
419 case '/kiwi':
420 kiwi.gateway.kiwi(Tabview.getCurrentTab().name, msg.substring(6));
421 break;
422
423 case '/ctcp':
424 parts = parts.slice(1);
425 dest = parts.shift();
426 msg = parts.join(' ');
427
428 kiwi.gateway.msg(dest, String.fromCharCode(1) + msg + String.fromCharCode(1));
429 Tabview.getServerTab().addMsg(null, 'CTCP Request', '[to ' + dest + '] ' + msg, 'ctcp');
430 break;
431 default:
432 //Tabview.getCurrentTab().addMsg(null, ' ', '--> Invalid command: '+parts[0].substring(1));
433 kiwi.gateway.raw(msg.substring(1));
434 }
435
436 } else {
437 //alert('Sending message: '+msg);
438 if (msg.trim() === '') {
439 return;
440 }
441 if (Tabview.getCurrentTab().name !== 'server') {
442 kiwi.gateway.msg(Tabview.getCurrentTab().name, msg);
443 d = new Date();
444 d = d.getHours() + ":" + d.getMinutes();
445 //kiwi.front.addMsg(d, kiwi.gateway.nick, msg);
446 Tabview.getCurrentTab().addMsg(null, kiwi.gateway.nick, msg);
447 }
448 }
449 },
450
451
452 onMsg: function (e, data) {
453 var destination, plugin_event, tab;
454 // Is this message from a user?
455 if (data.channel === kiwi.gateway.nick) {
456 destination = data.nick.toLowerCase();
457 } else {
458 destination = data.channel.toLowerCase();
459 }
460
461 plugin_event = {nick: data.nick, msg: data.msg, destination: destination};
462 plugin_event = kiwi.plugs.run('msg_recieved', plugin_event);
463 if (!plugin_event) {
464 return;
465 }
466 tab = Tabview.getTab(plugin_event.destination);
467 if (!tab) {
468 tab = new Tabview(plugin_event.destination);
469 }
470 tab.addMsg(null, plugin_event.nick, plugin_event.msg);
471 },
472
473 onDebug: function (e, data) {
474 var tab = Tabview.getTab('kiwi_debug');
475 if (!tab) {
476 tab = new Tabview('kiwi_debug');
477 }
478 tab.addMsg(null, ' ', data.msg);
479 },
480
481 onAction: function (e, data) {
482 var destination, tab;
483 // Is this message from a user?
484 if (data.channel === kiwi.gateway.nick) {
485 destination = data.nick;
486 } else {
487 destination = data.channel;
488 }
489
490 tab = Tabview.getTab(destination);
491 if (!tab) {
492 tab = new Tabview(destination);
493 }
494 tab.addMsg(null, ' ', '* ' + data.nick + ' ' + data.msg, 'action', 'color:#555;');
495 },
496
497 onTopic: function (e, data) {
498 var tab = Tabview.getTab(data.channel);
499 if (tab) {
500 tab.changeTopic(data.topic);
501 }
502 },
503
504 onNotice: function (e, data) {
505 var nick = (data.nick === undefined) ? '' : data.nick,
506 enick = '[' + nick + ']',
507 tab;
508
509 if (Tabview.tabExists(data.target)) {
510 Tabview.getTab(data.target).addMsg(null, enick, data.msg, 'notice');
511 } else if (Tabview.tabExists(nick)) {
512 Tabview.getTab(nick).addMsg(null, enick, data.msg, 'notice');
513 } else {
514 Tabview.getServerTab().addMsg(null, enick, data.msg, 'notice');
515 }
516 },
517
518 onCTCPRequest: function (e, data) {
519 var msg = data.msg.split(" ", 2);
520 switch (msg[0]) {
521 case 'PING':
522 if (typeof msg[1] === 'undefined') {
523 msg[1] = '';
524 }
525 kiwi.gateway.notice(data.nick, String.fromCharCode(1) + 'PING ' + msg[1] + String.fromCharCode(1));
526 break;
527 case 'TIME':
528 kiwi.gateway.notice(data.nick, String.fromCharCode(1) + 'TIME ' + (new Date()).toLocaleString() + String.fromCharCode(1));
529 break;
530 }
531 Tabview.getServerTab().addMsg(null, 'CTCP Request', '[from ' + data.nick + '] ' + data.msg, 'ctcp');
532 },
533
534 onCTCPResponse: function (e, data) {
535 Tabview.getServerTab().addMsg(null, 'CTCP Reply', '[from ' + data.nick + '] ' + data.msg, 'ctcp');
536 },
537
538 onKiwi: function (e, data) {
539 //console.log(data);
540 },
541
542 onConnect: function (e, data) {
543 var err_box, channels;
544
545 if (data.connected) {
546 // Did we disconnect?
547 err_box = $('.messages .msg.error.disconnect .text');
548 if (typeof err_box[0] !== 'undefined') {
549 err_box.text('Reconnected OK :)');
550 err_box.parent().removeClass('disconnect');
551
552 // Rejoin channels
553 channels = '';
554 _.each(Tabview.getAllTabs(), function (tabview) {
555 if (tabview.name === 'server') {
556 return;
557 }
558 channels += tabview.name + ',';
559 });
560 console.log('Rejoining: ' + channels);
561 kiwi.gateway.join(channels);
562 return;
563 }
564
565 if (kiwi.gateway.nick !== data.nick) {
566 kiwi.gateway.nick = data.nick;
567 kiwi.front.doLayout();
568 }
569
570 Tabview.getServerTab().addMsg(null, ' ', '=== Connected OK :)', 'status');
571 if (typeof init_data.channel === "string") {
572 kiwi.front.joinChannel(init_data.channel);
573 }
574 kiwi.plugs.run('connect', {success: true});
575 } else {
576 Tabview.getServerTab().addMsg(null, ' ', '=== Failed to connect :(', 'status');
577 kiwi.plugs.run('connect', {success: false});
578 }
579 },
580 onConnectFail: function (e, data) {
581 var reason = (typeof data.reason === 'string') ? data.reason : '';
582 Tabview.getServerTab().addMsg(null, '', 'There\'s a problem connecting! (' + reason + ')', 'error');
583 kiwi.plugs.run('connect', {success: false});
584 },
585 onDisconnect: function (e, data) {
586 var tab, tabs;
587 tabs = Tabview.getAllTabs();
588 for (tab in tabs) {
589 tabs[tab].addMsg(null, '', 'Disconnected from server!', 'error disconnect');
590 }
591 kiwi.plugs.run('disconnect', {success: false});
592 },
593 onReconnecting: function (e, data) {
594 var err_box, f, msg;
595
596 err_box = $('.messages .msg.error.disconnect .text');
597 if (!err_box) {
598 return;
599 }
600
601 f = function (num) {
602 switch (num) {
603 case 1: return 'First';
604 case 2: return 'Second';
605 case 3: return 'Third';
606 case 4: return 'Fourth';
607 case 5: return 'Fifth';
608 case 6: return 'Sixth';
609 case 7: return 'Seventh';
610 default: return 'Next';
611 }
612 };
613
614 // TODO: convert seconds to mins:secs
615 msg = f(data.attempts) + ' attempt at reconnecting in ' + (data.delay / 1000).toString() + ' seconds..';
616 err_box.text(msg);
617 },
618 onOptions: function (e, data) {
619 if (typeof kiwi.gateway.network_name === "string" && kiwi.gateway.network_name !== "") {
620 Tabview.getServerTab().tab.text(kiwi.gateway.network_name);
621 }
622 },
623 onMOTD: function (e, data) {
624 Tabview.getServerTab().addMsg(null, data.server, data.msg, 'motd');
625 },
626 onWhois: function (e, data) {
627 var d, tab;
628 tab = Tabview.getCurrentTab();
629 if (data.msg) {
630 tab.addMsg(null, data.nick, data.msg, 'whois');
631 } else if (data.logon) {
632 d = new Date();
633 d.setTime(data.logon * 1000);
634 d = d.toLocaleString();
635 tab.addMsg(null, data.nick, 'idle for ' + data.idle + ' second' + ((data.idle !== 1) ? 's' : '') + ', signed on ' + d, 'whois');
636 } else {
637 tab.addMsg(null, data.nick, 'idle for ' + data.idle + ' seconds', 'whois');
638 }
639 },
640 onMode: function (e, data) {
641 var tab;
642 if ((typeof data.channel === 'string') && (typeof data.effected_nick === 'string')) {
643 tab = Tabview.getTab(data.channel);
644 tab.addMsg(null, ' ', '[' + data.mode + '] ' + data.effected_nick + ' by ' + data.nick, 'mode', '');
645 if (tab.userlist.hasUser(data.effected_nick)) {
646 tab.userlist.changeUserMode(data.effected_nick, data.mode.substr(1), (data.mode[0] === '+'));
647 }
648 }
649
650 // TODO: Other mode changes that aren't +/- qaohv. - JA
651 },
652 onUserList: function (e, data) {
653 var tab;
654
655 tab = Tabview.getTab(data.channel);
656 if (!tab) {
657 return;
658 }
659
660 if ((!kiwi.front.cache.userlist) || (!kiwi.front.cache.userlist.updating)) {
661 if (!kiwi.front.cache.userlist) {
662 kiwi.front.cache.userlist = {updating: true};
663 } else {
664 kiwi.front.cache.userlist.updating = true;
665 }
666 tab.userlist.empty();
667 }
668
669 tab.userlist.addUser(data.users);
670
671 },
672 onUserListEnd: function (e, data) {
673 if (!kiwi.front.cache.userlist) {
674 kiwi.front.cache.userlist = {};
675 }
676 kiwi.front.cache.userlist.updating = false;
677 },
678
679 onChannelListStart: function (e, data) {
680 /*global Utilityview */
681 var tab, table;
682
683 tab = new Utilityview('Channel List');
684 tab.div.css('overflow-y', 'scroll');
685 table = $('<table style="margin:1em 2em;"><thead style="font-weight: bold;"><tr><td>Channel Name</td><td>Members</td><td style="padding-left: 2em;">Topic</td></tr></thead><tbody style="vertical-align: top;"></tbody>');
686 tab.div.append(table);
687
688 kiwi.front.cache.list = {chans: [], tab: tab, table: table,
689 update: function (newChans) {
690 var body = this.table.children('tbody:first').detach(),
691 chan,
692 html;
693
694 html = '';
695 for (chan in newChans) {
696 this.chans.push(newChans[chan]);
697 html += newChans[chan].html;
698 }
699 body.append(html);
700 this.table.append(body);
701
702 },
703 finalise: function () {
704 var body = this.table.children('tbody:first').detach(),
705 list,
706 chan;
707
708 list = $.makeArray($(body).children());
709
710 for (chan in list) {
711 list[chan] = $(list[chan]).detach();
712 }
713
714 list = _.sortBy(list, function (channel) {
715 return parseInt(channel.children('.num_users').first().text(), 10);
716 }).reverse();
717
718 for (chan in list) {
719 body.append(list[chan]);
720 }
721
722 this.table.append(body);
723
724 }};
725 },
726 onChannelList: function (e, data) {
727 var chans;
728 data = data.chans;
729 //data = [data];
730 for (chans in data) {
731 data[chans] = {data: data[chans], html: '<tr><td><a class="chan">' + data[chans].channel + '</a></td><td class="num_users" style="text-align: center;">' + data[chans].num_users + '</td><td style="padding-left: 2em;">' + kiwi.front.formatIRCMsg(data[chans].topic) + '</td></tr>'};
732 }
733 kiwi.front.cache.list.update(data);
734 },
735 onChannelListEnd: function (e, data) {
736 kiwi.front.cache.list.finalise();
737 kiwi.front.cache.list.tab.show();
738 },
739
740
741 onJoin: function (e, data) {
742 var tab = Tabview.getTab(data.channel);
743 if (!tab) {
744 tab = new Tabview(data.channel.toLowerCase());
745 }
746
747 tab.addMsg(null, ' ', '--> ' + data.nick + ' has joined', 'action join', 'color:#009900;');
748
749 if (data.nick === kiwi.gateway.nick) {
750 return; // Not needed as it's already in nicklist
751 }
752
753 tab.userlist.addUser({nick: data.nick, modes: []});
754 },
755 onPart: function (e, data) {
756 var tab = Tabview.getTab(data.channel);
757 if (tab) {
758 // If this is us, close the tabview
759 if (data.nick === kiwi.gateway.nick) {
760 tab.close();
761 Tabview.getServerTab().show();
762 return;
763 }
764
765 tab.addMsg(null, ' ', '<-- ' + data.nick + ' has left (' + data.message + ')', 'action part', 'color:#990000;');
766 tab.userlist.removeUser(data.nick);
767 }
768 },
769 onKick: function (e, data) {
770 var tab = Tabview.getTab(data.channel);
771 if (tab) {
772 // If this is us, close the tabview
773 if (data.kicked === kiwi.gateway.nick) {
774 //tab.close();
775 tab.addMsg(null, ' ', '=== You have been kicked from ' + data.channel + '. ' + data.message, 'status kick');
776 tab.safe_to_close = true;
777 tab.userlist.remove();
778 return;
779 }
780
781 tab.addMsg(null, ' ', '<-- ' + data.kicked + ' kicked by ' + data.nick + '(' + data.message + ')', 'action kick', 'color:#990000;');
782 tab.userlist.removeUser(data.nick);
783 }
784 },
785 onNick: function (e, data) {
786 if (data.nick === kiwi.gateway.nick) {
787 kiwi.gateway.nick = data.newnick;
788 kiwi.front.doLayout();
789 }
790
791 _.each(Tabview.getAllTabs(), function (tab) {
792 if (tab.userlist.hasUser(data.nick)) {
793 tab.userlist.renameUser(data.nick, data.newnick);
794 tab.addMsg(null, ' ', '=== ' + data.nick + ' is now known as ' + data.newnick, 'action changenick');
795 }
796 });
797 },
798 onQuit: function (e, data) {
799 _.each(Tabview.getAllTabs(), function (tab) {
800 if (tab.userlist.hasUser(data.nick)) {
801 tab.userlist.removeUser(data.nick);
802 tab.addMsg(null, ' ', '<-- ' + data.nick + ' has quit (' + data.message + ')', 'action quit', 'color:#990000;');
803 }
804 });
805 },
806 onChannelRedirect: function (e, data) {
807 var tab = Tabview.getTab(data.from);
808 tab.close();
809 tab = new Tabview(data.to);
810 tab.addMsg(null, ' ', '=== Redirected from ' + data.from, 'action');
811 },
812
813 onIRCError: function (e, data) {
814 var t_view,
815 tab = Tabview.getTab(data.channel);
816 if (data.channel !== undefined && tab) {
817 t_view = data.channel;
818 } else {
819 t_view = 'server';
820 tab = Tabview.getServerTab();
821 }
822
823 switch (data.error) {
824 case 'banned_from_channel':
825 tab.addMsg(null, ' ', '=== You are banned from ' + data.channel + '. ' + data.reason, 'status');
826 if (t_view !== 'server') {
827 tab.safe_to_close = true;
828 }
829 break;
830 case 'bad_channel_key':
831 tab.addMsg(null, ' ', '=== Bad channel key for ' + data.channel, 'status');
832 if (t_view !== 'server') {
833 tab.safe_to_close = true;
834 }
835 break;
836 case 'invite_only_channel':
837 tab.addMsg(null, ' ', '=== ' + data.channel + ' is invite only.', 'status');
838 if (t_view !== 'server') {
839 tab.safe_to_close = true;
840 }
841 break;
842 case 'channel_is_full':
843 tab.addMsg(null, ' ', '=== ' + data.channel + ' is full.', 'status');
844 if (t_view !== 'server') {
845 tab.safe_to_close = true;
846 }
847 break;
848 case 'chanop_privs_needed':
849 tab.addMsg(null, ' ', '=== ' + data.reason, 'status');
850 break;
851 case 'no_such_nick':
852 Tabview.getServerTab().addMsg(null, ' ', '=== ' + data.nick + ': ' + data.reason, 'status');
853 break;
854 case 'nickname_in_use':
855 Tabview.getServerTab().addMsg(null, ' ', '=== The nickname ' + data.nick + ' is already in use. Please select a new nickname', 'status');
856 kiwi.front.showChangeNick('That nick is already taken');
857 break;
858 default:
859 // We don't know what data contains, so don't do anything with it.
860 //kiwi.front.tabviews.server.addMsg(null, ' ', '=== ' + data, 'status');
861 }
862 },
863
864 registerKeys: function () {
865 var tabcomplete = {active: false, data: [], prefix: ''};
866 $('#kiwi_msginput').bind('keydown', function (e) {
867 var windows, meta, num, msg, data, self;
868 windows = $('#windows');
869 //var meta = e.altKey;
870 meta = e.ctrlKey;
871
872 if (e.which !== 9) {
873 tabcomplete.active = false;
874 tabcomplete.data = [];
875 tabcomplete.prefix = '';
876 }
877
878 switch (true) {
879 case (e.which >= 48) && (e.which <= 57):
880 if (meta) {
881 num = e.which - 48;
882 if (num === 0) {
883 num = 10;
884 }
885 num = num - 1;
886 kiwi.front.windowsShow(num);
887 return false;
888 }
889 break;
890 case e.which === 27: // escape
891 return false;
892 case e.which === 13: // return
893 msg = $('#kiwi_msginput').val();
894 msg = msg.trim();
895
896 kiwi.front.buffer.push(msg);
897 kiwi.front.buffer_pos = kiwi.front.buffer.length;
898
899 kiwi.front.run(msg);
900 $('#kiwi_msginput').val('');
901
902 break;
903 case e.which === 33: // page up
904 console.log("page up");
905 windows[0].scrollTop = windows[0].scrollTop - windows.height();
906 return false;
907 case e.which === 34: // page down
908 windows[0].scrollTop = windows[0].scrollTop + windows.height();
909 return false;
910 case e.which === 37: // left
911 if (meta) {
912 kiwi.front.windowsPrevious();
913 return false;
914 }
915 break;
916 case e.which === 38: // up
917 if (kiwi.front.buffer_pos > 0) {
918 kiwi.front.buffer_pos--;
919 $('#kiwi_msginput').val(kiwi.front.buffer[kiwi.front.buffer_pos]);
920 }
921 break;
922 case e.which === 39: // right
923 if (meta) {
924 kiwi.front.windowsNext();
925 return false;
926 }
927 break;
928 case e.which === 40: // down
929 if (kiwi.front.buffer_pos < kiwi.front.buffer.length) {
930 kiwi.front.buffer_pos++;
931 $('#kiwi_msginput').val(kiwi.front.buffer[kiwi.front.buffer_pos]);
932 }
933 break;
934
935 case e.which === 9: // tab
936 tabcomplete.active = true;
937 if (_.isEqual(tabcomplete.data, [])) {
938 // Get possible autocompletions
939 data = [];
940 Tabview.getCurrentTab().userlist.listUsers(false).each(function () {
941 var nick;
942 nick = kiwi.front.nickStripPrefix($('a.nick', this).text());
943 data.push(nick);
944 });
945 data = _.sortBy(data, function (nick) {
946 return nick;
947 });
948 tabcomplete.data = data;
949 }
950
951 if (this.value[this.selectionStart - 1] === ' ') {
952 return false;
953 }
954 self = this;
955 (function () {
956 var tokens = self.value.substring(0, self.selectionStart).split(" "),
957 val,
958 p1,
959 newnick,
960 range,
961 nick = tokens[tokens.length - 1];
962 if (tabcomplete.prefix === '') {
963 tabcomplete.prefix = nick;
964 }
965
966 tabcomplete.data = _.select(tabcomplete.data, function (n) {
967 return (n.toLowerCase().indexOf(tabcomplete.prefix.toLowerCase()) === 0);
968 });
969
970 if (tabcomplete.data.length > 0) {
971 p1 = self.selectionStart - (nick.length);
972 val = self.value.substr(0, p1);
973 newnick = tabcomplete.data.shift();
974 tabcomplete.data.push(newnick);
975 val += newnick;
976 val += self.value.substr(self.selectionStart);
977 self.value = val;
978 if (self.setSelectionRange) {
979 self.setSelectionRange(p1 + newnick.length, p1 + newnick.length);
980 } else if (self.createTextRange) { // not sure if this bit is actually needed....
981 range = self.createTextRange();
982 range.collapse(true);
983 range.moveEnd('character', p1 + newnick.length);
984 range.moveStart('character', p1 + newnick.length);
985 range.select();
986 }
987 }
988 }());
989 return false;
990 }
991 });
992
993
994 $('#kiwi .control .msginput .nick').click(function () {
995 kiwi.front.showChangeNick();
996 });
997
998
999
1000
1001
1002 $('#kiwi .plugins .load_plugin_file').click(function () {
1003 if (typeof kiwi.front.boxes.plugins !== "undefined") {
1004 return;
1005 }
1006
1007 kiwi.front.boxes.plugins = new Box("plugin_file");
1008 $('#tmpl_plugins').tmpl({}).appendTo(kiwi.front.boxes.plugins.content);
1009 kiwi.front.boxes.plugins.box.css('top', -(kiwi.front.boxes.plugins.height + 40));
1010
1011 // Populate the plugin list..
1012 function enumPlugins() {
1013 var lst, j, txt;
1014 lst = $('#plugin_list');
1015 lst.find('option').remove();
1016 for (j in kiwi.plugs.loaded) {
1017 txt = kiwi.plugs.loaded[j].name;
1018 lst.append('<option value="' + txt + '">' + txt + '</option>');
1019 }
1020 }
1021 enumPlugins();
1022
1023 // Event bindings
1024 $('#kiwi .plugin_file').submit(function () {
1025 $('<div></div>').load($('.txtpluginfile').val(), function (e) {
1026 enumPlugins();
1027 });
1028 return false;
1029 });
1030 $('#kiwi .cancelpluginfile').click(function () {
1031 kiwi.front.boxes.plugins.destroy();
1032 });
1033
1034 $('#kiwi #plugins_list_unload').click(function () {
1035 var selected_plugin;
1036 selected_plugin = $('#plugin_list').val();
1037 kiwi.plugs.unloadPlugin(selected_plugin);
1038 enumPlugins();
1039 });
1040
1041 $('#kiwi .txtpluginfile').focus();
1042
1043 });
1044
1045 $('#kiwi .plugins .reload_css').click(function () {
1046 var links = document.getElementsByTagName("link"),
1047 i;
1048 for (i = 0; i < links.length; i++) {
1049 if (links[i].rel === "stylesheet") {
1050 if (links[i].href.indexOf("?") === -1) {
1051 links[i].href += "?";
1052 }
1053 links[i].href += "x";
1054 }
1055 }
1056 });
1057
1058
1059 $('#kiwi .about .about_close').click(function () {
1060 $('#kiwi .about').css('display', 'none');
1061 });
1062
1063
1064 $('#kiwi .poweredby').click(function () {
1065 $('#kiwi .about').css('display', 'block');
1066 });
1067
1068 },
1069
1070
1071 showChangeNick: function (caption) {
1072 caption = (typeof caption !== 'undefined') ? caption : '';
1073
1074 $('#kiwi').append($('#tmpl_change_nick').tmpl({}));
1075
1076 $('#kiwi .newnick .caption').text(caption);
1077
1078 $('#kiwi .form_newnick').submit(function () {
1079 kiwi.front.run('/NICK ' + $('#kiwi .txtnewnick').val());
1080 $('#kiwi .newnick').remove();
1081 return false;
1082 });
1083
1084 $('#kiwi .txtnewnick').keypress(function (ev) {
1085 if (!this.first_press) {
1086 this.first_press = true;
1087 return false;
1088 }
1089 });
1090
1091 $('#kiwi .txtnewnick').keydown(function (ev) {
1092 if (ev.which === 27) { // ESC
1093 $('#kiwi_msginput').focus();
1094 $('#kiwi .newnick').remove();
1095 }
1096 });
1097
1098 $('#kiwi .cancelnewnick').click(function () {
1099 $('#kiwi .newnick').remove();
1100 });
1101
1102 $('#kiwi .txtnewnick').focus();
1103 },
1104
1105
1106 sync: function () {
1107 kiwi.gateway.sync();
1108 },
1109
1110 onSync: function (e, data) {
1111 // Set any settings
1112 if (data.nick !== undefined) {
1113 kiwi.gateway.nick = data.nick;
1114 }
1115
1116 // Add the tabviews
1117 if (data.tabviews !== undefined) {
1118 _.each(data.tabviews, function (tab) {
1119 var newTab;
1120 if (!Tabview.tabExists(tab.name)) {
1121 newTab = new Tabview(kiwi.gateway.channel_prefix + tab.name);
1122
1123 if (tab.userlist !== undefined) {
1124 kiwi.front.onUserList({'channel': kiwi.gateway.channel_prefix + tab.name, 'users': tab.userlist.getUsers(false)});
1125 }
1126 }
1127 });
1128 }
1129
1130 kiwi.front.doLayout();
1131 },
1132
1133
1134 setTopicText: function (new_topic) {
1135 kiwi.front.original_topic = new_topic;
1136 $('#kiwi .cur_topic .topic').text(new_topic);
1137 kiwi.front.doLayoutSize();
1138 },
1139
1140 nickStripPrefix: function (nick) {
1141 var tmp = nick, i, j, k;
1142 i = 0;
1143 for (j = 0; j < nick.length; j++) {
1144 for (k = 0; k < kiwi.gateway.user_prefixes.length; k++) {
1145 if (nick.charAt(j) === kiwi.gateway.user_prefixes[k].symbol) {
1146 i++;
1147 break;
1148 }
1149 }
1150 }
1151
1152 return tmp.substr(i);
1153 },
1154
1155 nickGetPrefix: function (nick) {
1156 var tmp = nick, i, j, k;
1157 i = 0;
1158 for (j = 0; j < nick.length; j++) {
1159 for (k = 0; k < kiwi.gateway.user_prefixes.length; k++) {
1160 if (nick.charAt(j) === kiwi.gateway.user_prefixes[k].symbol) {
1161 i++;
1162 break;
1163 }
1164 }
1165 }
1166
1167 return tmp.substr(0, i);
1168 },
1169
1170 isChannel: function (name) {
1171 var prefix, is_chan;
1172 prefix = name.charAt(0);
1173 if (kiwi.gateway.channel_prefix.indexOf(prefix) > -1) {
1174 is_chan = true;
1175 } else {
1176 is_chan = false;
1177 }
1178
1179 return is_chan;
1180 },
1181
1182 tabviewsNext: function () {
1183 var wl = $('#kiwi .windowlist ul'),
1184 next_left = parseInt(wl.css('text-indent').replace('px', ''), 10) + 170;
1185 wl.css('text-indent', next_left);
1186 },
1187
1188 tabviewsPrevious: function () {
1189 var wl = $('#kiwi .windowlist ul'),
1190 next_left = parseInt(wl.css('text-indent').replace('px', ''), 10) - 170;
1191 wl.css('text-indent', next_left);
1192 },
1193
1194 windowsNext: function () {
1195 var tab, tabs, curTab, next;
1196 next = false;
1197 tabs = Tabview.getAllTabs();
1198 curTab = Tabview.getCurrentTab();
1199 for (tab in tabs) {
1200 if (!next) {
1201 if (tabs[tab] === curTab) {
1202 next = true;
1203 continue;
1204 }
1205 } else {
1206 tabs[tab].show();
1207 return;
1208 }
1209 }
1210 },
1211
1212 windowsPrevious: function () {
1213 var tab, tabs, curTab, prev_tab, next;
1214 next = false;
1215 tabs = Tabview.getAllTabs();
1216 curTab = Tabview.getCurrentTab();
1217 for (tab in tabs) {
1218 if (tabs[tab] === curTab) {
1219 if (prev_tab) {
1220 prev_tab.show();
1221 }
1222 return;
1223 }
1224 prev_tab = tabs[tab];
1225 }
1226 },
1227
1228 windowsShow: function (num) {
1229 num = parseInt(num, 10);
1230 console.log('Showing window ' + num.toString());
1231 var i = 0, tab, tabs;
1232 tabs = Tabview.getAllTabs();
1233 for (tab in tabs) {
1234 if (i === num) {
1235 tabs[tab].show();
1236 return;
1237 }
1238 i++;
1239 }
1240 },
1241
1242
1243
1244 barsShow: function () {
1245 $('#kiwi .toolbars').slideDown();
1246 $('#kiwi .control').slideDown();
1247 },
1248
1249 barsHide: function () {
1250 $('#kiwi .toolbars').slideUp();
1251 $('#kiwi .control').slideUp();
1252 },
1253
1254 formatIRCMsg: function (msg) {
1255 var re, next;
1256
1257 if ((!msg) || (typeof msg !== 'string')) {
1258 return;
1259 }
1260
1261 // bold
1262 if (msg.indexOf(String.fromCharCode(2)) !== -1) {
1263 next = '<b>';
1264 while (msg.indexOf(String.fromCharCode(2)) !== -1) {
1265 msg = msg.replace(String.fromCharCode(2), next);
1266 next = (next === '<b>') ? '</b>' : '<b>';
1267 }
1268 if (next === '</b>') {
1269 msg = msg + '</b>';
1270 }
1271 }
1272
1273 // underline
1274 if (msg.indexOf(String.fromCharCode(31)) !== -1) {
1275 next = '<u>';
1276 while (msg.indexOf(String.fromCharCode(31)) !== -1) {
1277 msg = msg.replace(String.fromCharCode(31), next);
1278 next = (next === '<u>') ? '</u>' : '<u>';
1279 }
1280 if (next === '</u>') {
1281 msg = msg + '</u>';
1282 }
1283 }
1284
1285 // colour
1286 re = /\x03([0-9][0-5]?)(,([0-9][0-5]?))?(.*?)\x03/g;
1287
1288 msg = msg.replace(re, function (str, p1, p2, p3, p4) {
1289 var fg, bg,
1290 col = function (num) {
1291 switch (parseInt(num, 10)) {
1292 case 0:
1293 return '#FFFFFF';
1294 case 1:
1295 return '#000000';
1296 case 2:
1297 return '#000080';
1298 case 3:
1299 return '#008000';
1300 case 4:
1301 return '#FF0000';
1302 case 5:
1303 return '#800040';
1304 case 6:
1305 return '#800080';
1306 case 7:
1307 return '#FF8040';
1308 case 8:
1309 return '#FFFF00';
1310 case 9:
1311 return '#80FF00';
1312 case 10:
1313 return '#008080';
1314 case 11:
1315 return '#00FFFF';
1316 case 12:
1317 return '#0000FF';
1318 case 13:
1319 return '#FF55FF';
1320 case 14:
1321 return '#808080';
1322 case 15:
1323 return '#C0C0C0';
1324 default:
1325 return null;
1326 }
1327 };
1328 fg = col(p1);
1329 bg = col(p3);
1330 return '<span style="' + ((fg !== null) ? 'color: ' + fg + '; ' : '') + ((bg !== null) ? 'background-color: ' + bg + ';' : '') + '">' + p4 + '</span>';
1331 });
1332 return msg;
1333 }
1334
1335 };
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346 var UserList = function (name) {
1347 var userlist, list_html, sortUsers, sortModes, getPrefix;
1348
1349 userlist = [];
1350
1351 $('#kiwi .userlist').append($('<ul id="kiwi_userlist_' + name + '"></ul>'));
1352 list_html = $('#kiwi_userlist_' + name);
1353 $('a.nick', list_html[0]).live('click', this.clickHandler);
1354
1355 sortUsers = function () {
1356 var parent;
1357 parent = list_html.parent();
1358 list_html = list_html.detach();
1359
1360 // Not sure this is needed.
1361 // It's O(n^2) as well, so need to test to see if it works without.
1362 // Alternative to test: list_html.children('li').detach();
1363 list_html.children().each(function (child) {
1364 var i, nick;
1365 child = $(child);
1366 nick = child.data('nick');
1367 for (i = 0; i < userlist.length; i++) {
1368 if (userlist[i].nick === nick) {
1369 userlist[i].html = child.detach();
1370 break;
1371 }
1372 }
1373 });
1374
1375 userlist.sort(function (a, b) {
1376 var i, a_idx, b_idx, a_nick, b_nick;
1377 // Try to sort by modes first
1378 if (a.modes.length > 0) {
1379 // a has modes, but b doesn't so a should appear first
1380 if (b.modes.length === 0) {
1381 return -1;
1382 }
1383 a_idx = b_idx = -1;
1384 // Compare the first (highest) mode
1385 for (i = 0; i < kiwi.gateway.user_prefixes.length; i++) {
1386 if (kiwi.gateway.user_prefixes[i].mode === a.modes[0]) {
1387 a_idx = i;
1388 }
1389 }
1390 for (i = 0; i < kiwi.gateway.user_prefixes.length; i++) {
1391 if (kiwi.gateway.user_prefixes[i].mode === b.modes[0]) {
1392 b_idx = i;
1393 }
1394 }
1395 if (a_idx < b_idx) {
1396 return -1;
1397 } else if (a_idx > b_idx) {
1398 return 1;
1399 }
1400 // If we get to here both a and b have the same highest mode so have to resort to lexicographical sorting
1401
1402 } else if (b.modes.length > 0) {
1403 // b has modes but a doesn't so b should appear first
1404 return 1;
1405 }
1406 a_nick = a.nick.toLocaleUpperCase();
1407 b_nick = b.nick.toLocaleUpperCase();
1408 // Lexicographical sorting
1409 if (a_nick < b_nick) {
1410 return -1;
1411 } else if (a_nick > b_nick) {
1412 return 1;
1413 } else {
1414 // This should never happen; both users have the same nick.
1415 console.log('Something\'s gone wrong somewhere - two users have the same nick!');
1416 return 0;
1417 }
1418 });
1419 _.each(userlist, function (user) {
1420 user.html = user.html.appendTo(list_html);
1421 });
1422
1423 list_html = list_html.appendTo(parent);
1424 };
1425
1426 sortModes = function (modes) {
1427 return modes.sort(function (a, b) {
1428 var a_idx, b_idx, i;
1429 for (i = 0; i < kiwi.gateway.user_prefixes.length; i++) {
1430 if (kiwi.gateway.user_prefixes[i].mode === a) {
1431 a_idx = i;
1432 }
1433 }
1434 for (i = 0; i < kiwi.gateway.user_prefixes.length; i++) {
1435 if (kiwi.gateway.user_prefixes[i].mode === b) {
1436 b_idx = i;
1437 }
1438 }
1439 if (a_idx < b_idx) {
1440 return -1;
1441 } else if (a_idx > b_idx) {
1442 return 1;
1443 } else {
1444 return 0;
1445 }
1446 });
1447 };
1448
1449 getPrefix = function (modes) {
1450 var prefix = '';
1451 if (typeof modes[0] !== 'undefined') {
1452 prefix = _.detect(kiwi.gateway.user_prefixes, function (prefix) {
1453 return prefix.mode === modes[0];
1454 });
1455 prefix = (prefix) ? prefix.symbol : '';
1456 }
1457 return prefix;
1458 };
1459
1460 this.addUser = function (users) {
1461 if (!_.isArray(users)) {
1462 users = [users];
1463 }
1464 _.each(users, function (user) {
1465 var html, prefix = '';
1466 user.nick = kiwi.front.nickStripPrefix(user.nick);
1467 user.modes = sortModes(user.modes);
1468 if (typeof user.modes[0] !== 'undefined') {
1469 prefix = _.detect(kiwi.gateway.user_prefixes, function (prefix) {
1470 return prefix.mode === user.modes[0];
1471 });
1472 prefix = (prefix) ? prefix.symbol : '';
1473 }
1474 html = $('<li><a class="nick">' + prefix + user.nick + '</a></li>');
1475 userlist.push({nick: user.nick, modes: user.modes, html: html});
1476 });
1477 sortUsers();
1478
1479 return this;
1480 };
1481
1482 this.removeUser = function (nicks) {
1483 var toRemove;
1484 if (!_.isArray(nicks)) {
1485 nicks = [nicks];
1486 }
1487 toRemove = _.select(userlist, function (user) {
1488 return _.any(nicks, function (n) {
1489 return n === user.nick;
1490 });
1491 });
1492
1493 _.each(toRemove, function (user) {
1494 user.html.remove();
1495 });
1496
1497 userlist = _.difference(userlist, toRemove);
1498
1499 return this;
1500 };
1501
1502 this.renameUser = function (oldNick, newNick) {
1503 var user = _.detect(userlist, function (u) {
1504 return u.nick === oldNick;
1505 });
1506 if (user) {
1507 user.nick = newNick;
1508 user.html.text(getPrefix(user.modes) + newNick);
1509 }
1510
1511 sortUsers();
1512
1513 return this;
1514 };
1515
1516 this.listUsers = function (modesort, modes) {
1517 var users = userlist;
1518 if (modes) {
1519 users = _.select(users, function (user) {
1520 return _.any(modes, function (m) {
1521 return _.any(user.modes, function (um) {
1522 return m === um;
1523 });
1524 });
1525 });
1526 }
1527 if ((modesort === true) || (typeof modesort === undefined)) {
1528 return users;
1529 } else {
1530 return _.sortBy(users, function (user) {
1531 return user.nick;
1532 });
1533 }
1534 };
1535
1536 this.remove = function () {
1537 list_html.remove();
1538 list_html = null;
1539 userlist = null;
1540 };
1541
1542 this.empty = function () {
1543 list_html.children().remove();
1544 userlist = [];
1545
1546 return this;
1547 };
1548
1549 this.hasUser = function (nick) {
1550 return _.any(userlist, function (user) {
1551 return user.nick === nick;
1552 });
1553 };
1554
1555 this.active = function (active) {
1556 if ((arguments.length === 0) || (active)) {
1557 list_html.addClass('active');
1558 list_html.show();
1559 } else {
1560 list_html.removeClass('active');
1561 list_html.hide();
1562 }
1563
1564 return this;
1565 };
1566
1567 this.changeUserMode = function (nick, mode, add) {
1568 var user;
1569 if (this.hasUser(nick)) {
1570 user = _.detect(userlist, function (u) {
1571 return u.nick === nick;
1572 });
1573
1574 if ((arguments.length < 3) || (add)) {
1575 user.modes.push(mode);
1576 } else {
1577 user.modes = _.reject(user.modes, function (m) {
1578 return m === mode;
1579 });
1580 }
1581 user.modes = sortModes(user.modes);
1582 user.html.children('a:first').text(getPrefix(user.modes) + user.nick);
1583 sortUsers();
1584 }
1585
1586 return this;
1587 };
1588 };
1589 UserList.prototype.width = 100; // 0 to disable
1590 UserList.prototype.setWidth = function (newWidth) {
1591 var w, u;
1592 if (typeof newWidth === 'number') {
1593 this.width = newWidth;
1594 }
1595
1596 w = $('#windows');
1597 u = $('#kiwi .userlist');
1598
1599 u.width(this.width);
1600
1601 return this;
1602 };
1603
1604 UserList.prototype.clickHandler = function () {
1605 var li = $(this).parent();
1606
1607 // Remove any existing userboxes
1608 $('#kiwi .userbox').remove();
1609
1610 if ($(li).data('userbox') === this) {
1611 $(li).removeData('userbox');
1612 } else {
1613 $('#tmpl_user_box').tmpl({nick: kiwi.front.nickStripPrefix($(this).text())}).appendTo(li);
1614
1615 $('#kiwi .userbox .userbox_query').click(function (ev) {
1616 var nick = $('#kiwi .userbox_nick').val();
1617 kiwi.front.run('/query ' + nick);
1618 });
1619
1620 $('#kiwi .userbox .userbox_whois').click(function (ev) {
1621 var nick = $('#kiwi .userbox_nick').val();
1622 kiwi.front.run('/whois ' + nick);
1623 });
1624 $(li).data('userbox', this);
1625 }
1626 };
1627
1628
1629
1630
1631
1632 /*
1633 * MISC VIEW
1634 */
1635
1636 var Utilityview = function (name) {
1637 var rand_name = randomString(15),
1638 tmp_divname = 'kiwi_window_' + rand_name,
1639 tmp_tabname = 'kiwi_tab_' + rand_name;
1640
1641 this.name = rand_name;
1642 this.title = name;
1643 this.topic = ' ';
1644 this.panel = $('#panel1');
1645
1646 if (typeof $('.scroller', this.panel)[0] === 'undefined') {
1647 this.panel.append('<div id="' + tmp_divname + '" class="messages"></div>');
1648 } else {
1649 $('.scroller', this.panel).append('<div id="' + tmp_divname + '" class="messages"></div>');
1650 }
1651
1652 this.tab = $('<li id="' + tmp_tabname + '">' + this.title + '</li>');
1653 this.tab.click(function () {
1654 kiwi.front.utilityviews[rand_name.toLowerCase()].show();
1655 });
1656 $('#kiwi .utilityviewlist ul').append(this.tab);
1657 kiwi.front.doLayoutSize();
1658
1659 this.div = $('#' + tmp_divname);
1660 this.div.css('overflow', 'hidden');
1661
1662 kiwi.front.utilityviews[rand_name.toLowerCase()] = this;
1663 };
1664
1665 Utilityview.prototype.name = null;
1666 Utilityview.prototype.title = null;
1667 Utilityview.prototype.div = null;
1668 Utilityview.prototype.tab = null;
1669 Utilityview.prototype.topic = ' ';
1670 Utilityview.prototype.panel = null;
1671 Utilityview.prototype.show = function () {
1672 $('.messages', this.panel).removeClass("active");
1673 $('#kiwi .userlist ul').removeClass("active");
1674 $('#kiwi .toolbars ul li').removeClass("active");
1675
1676 this.panel.css('overflow-y', 'hidden');
1677 $('#windows').css('right', 0);
1678 // Activate this tab!
1679 this.div.addClass('active');
1680 this.tab.addClass('active');
1681
1682 this.addPartImage();
1683
1684 kiwi.front.setTopicText(this.topic);
1685 kiwi.front.cur_channel = this;
1686
1687 // If we're using fancy scrolling, refresh it
1688 if (touch_scroll) {
1689 touch_scroll.refresh();
1690 }
1691 };
1692
1693 Utilityview.prototype.setPanel = function (new_panel) {
1694 this.div.detach();
1695 this.panel = new_panel;
1696 this.panel.append(this.div);
1697 this.show();
1698 };
1699
1700 Utilityview.prototype.close = function () {
1701 this.div.remove();
1702 this.tab.remove();
1703
1704 if (Tabview.getCurrentTab() === this) {
1705 kiwi.front.tabviews.server.show();
1706 }
1707 delete kiwi.front.utilityviews[this.name.toLowerCase()];
1708 };
1709
1710 Utilityview.prototype.addPartImage = function () {
1711 this.clearPartImage();
1712
1713 // We can't close this tab, so don't have the close image
1714 if (this.name === 'server') {
1715 return;
1716 }
1717
1718 var del_html = '<img src="/img/redcross.png" class="tab_part" />';
1719 this.tab.append(del_html);
1720
1721 $('.tab_part', this.tab).click(function () {
1722 if (Tabview.getCurrentTab().name !== 'server') {
1723 Tabview.getCurrentTab().close();
1724 }
1725 });
1726 };
1727
1728 Utilityview.prototype.clearPartImage = function () {
1729 $('#kiwi .toolbars .tab_part').remove();
1730 };
1731
1732
1733
1734
1735
1736 /*
1737 *
1738 * TABVIEWS
1739 *
1740 */
1741
1742
1743 var Tabview = function (v_name) {
1744 /*global Tabview, UserList */
1745 var re, htmlsafe_name, tmp_divname, tmp_userlistname, tmp_tabname, userlist_enabled = true;
1746
1747 if (v_name.charAt(0) === kiwi.gateway.channel_prefix) {
1748 //if (v_name[0] === kiwi.gateway.channel_prefix) {
1749 re = new RegExp(kiwi.gateway.channel_prefix, "g");
1750 htmlsafe_name = v_name.replace(re, 'pre');
1751 htmlsafe_name = "chan_" + htmlsafe_name;
1752 } else {
1753 htmlsafe_name = 'query_' + v_name;
1754 userlist_enabled = false;
1755 }
1756
1757 tmp_divname = 'kiwi_window_' + htmlsafe_name;
1758 tmp_userlistname = 'kiwi_userlist_' + htmlsafe_name;
1759 tmp_tabname = 'kiwi_tab_' + htmlsafe_name;
1760
1761 if (!Tabview.tabExists(v_name)) {
1762 $('#kiwi .windows .scroller').append('<div id="' + tmp_divname + '" class="messages"></div>');
1763 //$('#kiwi .userlist').append('<ul id="' + tmp_userlistname + '"></ul>');
1764 $('#kiwi .windowlist ul').append('<li id="' + tmp_tabname + '">' + v_name + '</li>');
1765 $('li', $('#kiwi .windowlist ul')[0]).last().bind('click', function () {
1766 var tab = Tabview.getTab(v_name);
1767 if (tab) {
1768 tab.show();
1769 }
1770 });
1771 }
1772 //$('#kiwi .windowlist ul .window_'+v_name).click(function(){ kiwi.front.windowShow(v_name); });
1773 //kiwi.front.windowShow(v_name);
1774
1775 kiwi.front.tabviews[v_name.toLowerCase()] = this;
1776 this.name = v_name;
1777 this.div = $('#' + tmp_divname);
1778 this.userlist = new UserList(htmlsafe_name);
1779 this.tab = $('#' + tmp_tabname);
1780 this.panel = $('#panel1');
1781
1782 if (!userlist_enabled) {
1783 this.userlist.setWidth(0);
1784 }
1785 this.show();
1786
1787 if (typeof registerTouches === "function") {
1788 //alert("Registering touch interface");
1789 //registerTouches($('#'+tmp_divname));
1790 registerTouches(document.getElementById(tmp_divname));
1791 }
1792
1793 kiwi.front.doLayoutSize();
1794 };
1795 Tabview.prototype.name = null;
1796 Tabview.prototype.div = null;
1797 Tabview.prototype.userlist = null;
1798 Tabview.prototype.tab = null;
1799 Tabview.prototype.topic = "";
1800 Tabview.prototype.safe_to_close = false; // If we have been kicked/banned/etc from this channel, don't wait for a part message
1801 Tabview.prototype.panel = null;
1802
1803 Tabview.prototype.show = function () {
1804 var w, u;
1805
1806 $('.messages', this.panel).removeClass("active");
1807 $('#kiwi .userlist ul').removeClass("active");
1808 $('#kiwi .toolbars ul li').removeClass("active");
1809
1810 w = $('#windows');
1811 u = $('#kiwi .userlist');
1812
1813 this.panel.css('overflow-y', 'scroll');
1814
1815 // Set the window size accordingly
1816 if (this.userlist.width > 0) {
1817 this.userlist.setWidth();
1818 w.css('right', u.outerWidth(true));
1819 this.userlist.active(true);
1820 // Enable the userlist resizer
1821 $('#nicklist_resize').css('display', 'block');
1822 } else {
1823 w.css('right', 0);
1824 // Disable the userlist resizer
1825 $('#nicklist_resize').css('display', 'none');
1826 }
1827
1828 this.div.addClass('active');
1829 this.tab.addClass('active');
1830
1831 // Add the part image to the tab
1832 this.addPartImage();
1833
1834 this.clearHighlight();
1835 kiwi.front.setTopicText(this.topic);
1836 kiwi.front.cur_channel = this;
1837
1838 // If we're using fancy scrolling, refresh it
1839 if (touch_scroll) {
1840 touch_scroll.refresh();
1841 }
1842
1843 this.scrollBottom();
1844 if (!touchscreen) {
1845 $('#kiwi_msginput').focus();
1846 }
1847 };
1848
1849 Tabview.prototype.close = function () {
1850 this.div.remove();
1851 this.userlist.remove();
1852 this.userlist = null;
1853 this.tab.remove();
1854
1855 if (kiwi.front.cur_channel === this) {
1856 kiwi.front.tabviews.server.show();
1857 }
1858 delete kiwi.front.tabviews[this.name.toLowerCase()];
1859 };
1860
1861 Tabview.prototype.addPartImage = function () {
1862 this.clearPartImage();
1863
1864 // We can't close this tab, so don't have the close image
1865 if (this.name === 'server') {
1866 return;
1867 }
1868
1869 var del_html = '<img src="/img/redcross.png" class="tab_part" />';
1870 this.tab.append(del_html);
1871
1872 $('.tab_part', this.tab).click(function () {
1873 if (kiwi.front.isChannel($(this).parent().text())) {
1874 kiwi.front.run("/part");
1875 } else {
1876 // Make sure we don't close the server tab
1877 if (kiwi.front.cur_channel.name !== 'server') {
1878 kiwi.front.cur_channel.close();
1879 }
1880 }
1881 });
1882 };
1883
1884 Tabview.prototype.clearPartImage = function () {
1885 $('#kiwi .toolbars .tab_part').remove();
1886 };
1887
1888 Tabview.prototype.addMsg = function (time, nick, msg, type, style) {
1889 var self, tmp, d, re, line_msg;
1890
1891 self = this;
1892
1893 tmp = {msg: msg, time: time, nick: nick, tabview: this.name};
1894 tmp = kiwi.plugs.run('addmsg', tmp);
1895 if (!tmp) {
1896 return;
1897 }
1898
1899
1900 msg = tmp.msg;
1901 time = tmp.time;
1902 nick = tmp.nick;
1903
1904 if (time === null) {
1905 d = new Date();
1906 time = d.getHours().toString().lpad(2, "0") + ":" + d.getMinutes().toString().lpad(2, "0") + ":" + d.getSeconds().toString().lpad(2, "0");
1907 }
1908
1909 // The CSS class (action, topic, notice, etc)
1910 if (typeof type !== "string") {
1911 type = '';
1912 }
1913
1914 // Make sure we don't have NaN or something
1915 if (typeof msg !== "string") {
1916 msg = '';
1917 }
1918
1919 // Make the channels clickable
1920 re = new RegExp('\\B(' + kiwi.gateway.channel_prefix + '[^ ,.\\007]+)', 'g');
1921 msg = msg.replace(re, function (match) {
1922 return '<a class="chan">' + match + '</a>';
1923 });
1924
1925 msg = kiwi.front.formatIRCMsg(msg);
1926
1927 // Build up and add the line
1928 line_msg = $('<div class="msg ' + type + '"><div class="time">' + time + '</div><div class="nick">' + nick + '</div><div class="text" style="' + style + '">' + msg + ' </div></div>');
1929 this.div.append(line_msg);
1930
1931 if (!touchscreen) {
1932 this.scrollBottom();
1933 } else {
1934 touch_scroll.refresh();
1935 //console.log(this.div.attr("scrollHeight") +" - "+ $('#windows').height());
1936 this.scrollBottom();
1937 //if(this.div.attr("scrollHeight") > $('#windows').height()){
1938 // touch_scroll.scrollTo(0, this.div.height());
1939 //}
1940 }
1941 };
1942
1943 Tabview.prototype.scrollBottom = function () {
1944 var panel = this.panel;
1945 panel[0].scrollTop = panel[0].scrollHeight;
1946 };
1947
1948 Tabview.prototype.changeNick = function (newNick, oldNick) {
1949 var inChan = this.userlist.hasUser(oldNick);
1950 if (inChan) {
1951 this.userlist.renameUser(oldNick, newNick);
1952 this.addMsg(null, ' ', '=== ' + oldNick + ' is now known as ' + newNick, 'action changenick');
1953 }
1954 };
1955 Tabview.prototype.highlight = function () {
1956 this.tab.addClass('highlight');
1957 };
1958 Tabview.prototype.activity = function () {
1959 this.tab.addClass('activity');
1960 };
1961 Tabview.prototype.clearHighlight = function () {
1962 this.tab.removeClass('highlight');
1963 this.tab.removeClass('activity');
1964 };
1965 Tabview.prototype.changeTopic = function (new_topic) {
1966 this.topic = new_topic;
1967 this.addMsg(null, ' ', '=== Topic for ' + this.name + ' is: ' + new_topic, 'topic');
1968 if (kiwi.front.cur_channel.name === this.name) {
1969 kiwi.front.setTopicText(new_topic);
1970 }
1971 };
1972 // Static functions
1973 Tabview.tabExists = function (name) {
1974 var ret = (typeof kiwi.front.tabviews[name.toLowerCase()] !== 'undefined');
1975 return ret;
1976 };
1977 Tabview.getTab = function (name) {
1978 if (Tabview.tabExists(name)) {
1979 var ret = kiwi.front.tabviews[name.toLowerCase()];
1980 return ret;
1981 } else {
1982 return null;
1983 }
1984 };
1985 Tabview.getServerTab = function () {
1986 return kiwi.front.tabviews.server;
1987 };
1988 Tabview.getAllTabs = function () {
1989 return kiwi.front.tabviews;
1990 };
1991 Tabview.getCurrentTab = function () {
1992 return kiwi.front.cur_channel;
1993 };
1994
1995 var Box = function (classname) {
1996 this.id = randomString(10);
1997 var tmp = $('<div id="' + this.id + '" class="box ' + classname + '"><div class="boxarea"></div></div>');
1998 $('#kiwi').append(tmp);
1999 this.box = $('#' + this.id);
2000 this.content = $('#' + this.id + ' .boxarea');
2001
2002 this.box.draggable({ stack: ".box" });
2003 this.content.click(function () {});
2004 //this.box.click(function(){ $(this)..css });
2005 };
2006 Box.prototype.create = function (name, classname) {
2007
2008 };
2009 Box.prototype.id = null;
2010 Box.prototype.box = null;
2011 Box.prototype.content = null;
2012 Box.prototype.destroy = function () {
2013 var name;
2014 this.box.remove();
2015 for (name in kiwi.front.boxes) {
2016 if (kiwi.front.boxes[name].id === this.id) {
2017 delete kiwi.front.boxes[name];
2018 }
2019 }
2020 };
2021 Box.prototype.height = function () {
2022 return this.box.height();
2023 };