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