Refactoring and re-plumbing.
[KiwiIRC.git] / client / 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 /**
4 * @namespace
5 */
6 kiwi.front = {
7 /**
8 * The current channel
9 * @type Object
10 */
11 cur_channel: null,
12 /**
13 * A list of windows
14 * @type Object
15 */
16 windows: {},
17 /**
18 * A list of Tabviews
19 * @type Object
20 */
21 tabviews: {},
22 /**
23 * A list of Utilityviews
24 * @type Object
25 */
26 utilityviews: {},
27 /**
28 * A list of Boxes
29 * @type Object
30 */
31 boxes: {},
32
33 /**
34 * The command history
35 * @type Array
36 */
37 buffer: [],
38 /**
39 * The current command history position
40 * @type Number
41 */
42 buffer_pos: 0,
43
44 /**
45 * Container for misc data (eg. userlist generation)
46 * @type Object
47 */
48 cache: {original_topic: '', userlist: {}},
49
50 /**
51 * Initialisation function
52 */
53 init: function () {
54 /*global Box, touch_scroll:true, Tabview */
55 var about_info, supportsOrientationChange, orientationEvent, scroll_opts, server_tabview;
56 kiwi.gateway.nick = 'kiwi_' + Math.ceil(100 * Math.random()) + Math.ceil(100 * Math.random());
57 kiwi.gateway.session_id = null;
58
59 // Bind to the gateway events
60 kiwi.front.events.bindAll();
61
62 // Build the about box
63 kiwi.front.boxes.about = new Box("about");
64 about_info = 'UI adapted for ' + agent;
65 if (touchscreen) {
66 about_info += ' touchscreen ';
67 }
68 about_info += 'usage';
69 $('#tmpl_about_box').tmpl({
70 about: about_info
71 }).appendTo(kiwi.front.boxes.about.content);
72
73 //$(window).bind("beforeunload", function(){ kiwi.gateway.quit(); });
74
75 if (touchscreen) {
76 $('#kiwi').addClass('touchscreen');
77
78 // Single touch scrolling through scrollback for touchscreens
79 scroll_opts = {};
80 touch_scroll = new iScroll('windows', scroll_opts);
81 }
82
83 kiwi.front.ui.registerKeys();
84
85 $('#kiwi .toolbars').resize(kiwi.front.ui.doLayoutSize);
86 $(window).resize(kiwi.front.ui.doLayoutSize);
87
88 // Add the resizer for the userlist
89 $('<div id="nicklist_resize"></div>').appendTo('#kiwi');
90 $('#nicklist_resize').draggable({axis: "x", drag: function () {
91 var t = $(this),
92 ul = $('#kiwi .userlist'),
93 new_width = $(document).width() - parseInt(t.css('left'), 10);
94
95 new_width = new_width - parseInt(ul.css('margin-left'), 10);
96 new_width = new_width - parseInt(ul.css('margin-right'), 10);
97
98 // Make sure we don't remove the userlist alltogether
99 if (new_width < 20) {
100 $(this).data('draggable').offset.click.left = 10;
101 console.log('whoaa');
102 }
103
104 var members = kiwi.currentPanel.get("members");
105 if (members) {
106 $(members.view.el).width(new_width);
107 }
108 $('#windows').css('right', ul.outerWidth(true));
109 }});
110
111
112 $('#kiwi .formconnectwindow').submit(function () {
113 var netsel = $('#kiwi .formconnectwindow .network'),
114 netport = $('#kiwi .formconnectwindow .port'),
115 netssl = $('#kiwi .formconnectwindow .ssl'),
116 netpass = $('#kiwi .formconnectwindow .password'),
117 nick = $('#kiwi .formconnectwindow .nick'),
118 tmp,
119 forwardKeys;
120
121 if (nick.val() === '') {
122 nick.val('Nick please!');
123 nick.focus();
124 return false;
125 }
126
127 tmp = nick.val().split(' ');
128 kiwi.gateway.nick = tmp[0];
129
130 init_data.channel = $('#channel').val();
131
132 kiwi.front.ui.doLayout();
133 try {
134 kiwi.gateway.connect(netsel.val(), netport.val(), netssl.is(':checked'), netpass.val(), function () {
135 setTimeout(function () {
136 kiwi.channels.server.set({"name": netsel.val()});
137 kiwi.channels.view.render();
138 }, 0);
139 });
140 } catch (e) {
141 console.log(e);
142 }
143
144 $('#kiwi .connectwindow').slideUp('', kiwi.front.ui.barsShow);
145
146 /**
147 * Listen for keyboard activity on any window, and forward it to the
148 * input box so users can type even if the input box is not in focus
149 * @inner
150 * @param {eventObject} event The event to forward
151 */
152 forwardKeys = function (event) {
153 $('#kiwi_msginput').focus();
154 $('#kiwi_msginput').trigger(event);
155 };
156 $('#kiwi_msginput').attr('tabindex', 0);
157 $('#kiwi_msginput').focus();
158 $('#windows').attr('tabindex',100);
159 $('#windows').keydown(forwardKeys).keypress(forwardKeys).keyup(forwardKeys);
160
161 return false;
162 });
163
164 supportsOrientationChange = (typeof window.onorientationchange !== undefined);
165 orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
166 if (window.addEventListener) {
167 window.addEventListener(orientationEvent, kiwi.front.ui.doLayoutSize, false);
168 } else {
169 // < IE9
170 window.attachEvent(orientationEvent, kiwi.front.ui.doLayoutSize, false);
171 }
172 //$('#kiwi').bind("resize", kiwi.front.ui.doLayoutSize, false);
173
174 kiwi.front.ui.doLayout();
175 kiwi.front.ui.barsHide();
176
177 kiwi.channels = new kiwi.model.PanelList();
178
179
180 //server_tabview = new Tabview('server');
181 //server_tabview.userlist.setWidth(0); // Disable the userlist
182 //server_tabview.setIcon('/img/app_menu.png');
183 //$('.icon', server_tabview.tab).tipTip({
184 // delay: 0,
185 // keepAlive: true,
186 // content: $('#tmpl_network_menu').tmpl({}).html(),
187 // activation: 'click'
188 //});
189
190 // Any pre-defined nick?
191 if (typeof window.init_data.nick === "string") {
192 $('#kiwi .formconnectwindow .nick').val(init_data.nick);
193 }
194
195 // Any pre-defined channels?
196 if (typeof window.init_data.channel === 'string') {
197 $('#channel').val(init_data.channel);
198 }
199
200 // Fix for Opera inserting a spurious <br/>
201 $('#kiwi .cur_topic br').remove();
202
203 $('#kiwi .cur_topic').keydown(function (e) {
204 if (e.which === 13) {
205 // enter
206 e.preventDefault();
207 $(this).change();
208 $('#kiwi_msginput').focus();
209 } else if (e.which === 27) {
210 // escape
211 e.preventDefault();
212 $(this).text(kiwi.front.cache.original_topic);
213 $('#kiwi_msginput').focus();
214 }
215 });
216 /*$('.cur_topic').live('keypress', function(e) {
217 if (e.keyCode === 13) {
218 // enter
219 e.preventDefault();
220 $(this).change();
221 $('#kiwi_msginput').focus();
222 } else if (e.keyCode === 27) {
223 // escape
224 e.preventDefault();
225 $(this).text(kiwi.front.cache.original_topic);
226 }
227 });*/
228 $('.cur_topic').live('change', function () {
229 var chan, text;
230 text = $(this).text();
231 if (text !== kiwi.front.cache.original_topic) {
232 if (kiwi.currentPanel.isChannel) {
233 kiwi.gateway.topic(kiwi.currentPannel.get("name"), text);
234 }
235 }
236 });
237
238
239 $('#windows a.chan').live('click', function () {
240 kiwi.front.joinChannel($(this).text());
241 return false;
242 });
243
244 kiwi.data.set('chanList', []);
245
246 // Load any client plugins
247 (function () {
248 var i;
249 for (i in plugins) {
250 kiwi.plugs.loadPlugin(plugins[i]);
251 }
252 }());
253 },
254
255
256
257 /**
258 * Joins a channel
259 * @param {String} chan_name The name of the channel to join
260 */
261 joinChannel: function (chan_name) {
262 var chans = chan_name.split(','),
263 i,
264 chan,
265 panel;
266 for (i in chans) {
267 chan = chans[i];
268 panel = kiwi.channels.getByName(chan);
269 if (!panel) {
270 kiwi.gateway.join(chan);
271 }
272 }
273 },
274
275 /**
276 * Parses and executes text and commands entered into the input msg box
277 * @param {String} msg The message string to parse
278 */
279 run: function (msg) {
280 var parts, dest, t, pos, textRange, plugin_event, msg_sliced, tab, nick, panel;
281
282 // Run through any plugins
283 plugin_event = {command: msg};
284 plugin_event = kiwi.plugs.run('command_run', plugin_event);
285 if (!plugin_event || typeof plugin_event.command === 'undefined') {
286 return;
287 }
288
289 // Update msg if it's been changed by any plugins
290 msg = plugin_event.command.toString();
291
292
293 if (msg.substring(0, 1) === '/') {
294 console.log("running " + msg);
295 parts = msg.split(' ');
296 switch (parts[0].toLowerCase()) {
297 case '/j':
298 case '/join':
299 kiwi.front.joinChannel(parts[1]);
300 break;
301
302 case '/connect':
303 case '/server':
304 if (typeof parts[1] === 'undefined') {
305 alert('Usage: /connect servername [port] [ssl] [password]');
306 break;
307 }
308
309 if (typeof parts[2] === 'undefined') {
310 parts[2] = 6667;
311 }
312
313 if ((typeof parts[3] === 'undefined') || !parts[3] || (parts[3] === 'false') || (parts[3] === 'no')) {
314 parts[3] = false;
315 } else {
316 parts[3] = true;
317 }
318
319 kiwi.channels.server.addMsg(null, ' ', '=== Connecting to ' + parts[1] + ' on port ' + parts[2] + (parts[3] ? ' using SSL' : '') + '...', 'status');
320 console.log('Connecting to ' + parts[1] + ' on port ' + parts[2] + (parts[3] ? ' using SSL' : '') + '...');
321 kiwi.gateway.connect(parts[1], parts[2], parts[3], parts[4]);
322 break;
323
324 case '/nick':
325 console.log("/nick");
326 if (parts[1] === undefined) {
327 console.log("calling show nick");
328 kiwi.front.ui.showChangeNick();
329 } else {
330 console.log("sending nick");
331 kiwi.gateway.changeNick(parts[1]);
332 }
333 break;
334
335 case '/part':
336 if (typeof parts[1] === "undefined") {
337 if (kiwi.currentPanel.isChannel) {
338 kiwi.gateway.part(kiwi.currentPanel.get("name"));
339 }
340 } else {
341 kiwi.gateway.part(msg.substring(6));
342 }
343 break;
344
345 case '/names':
346 if (typeof parts[1] !== "undefined") {
347 kiwi.gateway.raw(msg.substring(1));
348 } else {
349 if (kiwi.currentPanel.isChannel) {
350 kiwi.gateway.raw("NAMES " + kiwi.currentPanel.get("name"));
351 }
352 }
353 break;
354
355 case '/debug':
356 kiwi.gateway.debug();
357 break;
358
359 case '/q':
360 case '/query':
361 if (typeof parts[1] !== "undefined") {
362 //tab = new Tabview(parts[1]);
363 }
364 break;
365
366
367 case '/m':
368 case '/msg':
369 if (typeof parts[1] !== "undefined") {
370 msg_sliced = msg.split(' ').slice(2).join(' ');
371 kiwi.gateway.privmsg(parts[1], msg_sliced);
372
373 // TODO: Queries
374 panel = kiwi.channels.getByName(parts[1]);
375 if (panel) {
376 panel.addMsg(null, kiwi.gateway.nick, msg_sliced);
377 }
378 }
379 break;
380
381 case '/k':
382 case '/kick':
383 if (typeof parts[1] === 'undefined') {
384 return;
385 }
386 t = msg.split(' ', 3);
387 nick = t[1];
388 //kiwi.gateway.kick(Tabview.getCurrentTab().name, nick, t[2]);
389 break;
390
391 case '/quote':
392 kiwi.gateway.raw(msg.replace(/^\/quote /i, ''));
393 break;
394
395 case '/me':
396 if (kiwi.currentPanel.isChannel) {
397 kiwi.gateway.ctcp(true, 'ACTION', tab.name, msg.substring(4), function () {
398 kiwi.currentPanel.addMsg(null, ' ', '* ' + kiwi.gateway.nick + ' ' + msg.substring(4), 'action', 'color:#555;');
399 });
400 }
401 //TODO: Queries
402 break;
403
404 case '/notice':
405 dest = parts[1];
406 msg = parts.slice(2).join(' ');
407
408 kiwi.gateway.notice(dest, msg, function () {
409 kiwi.front.events.onNotice({}, {nick: kiwi.gateway.nick, channel: dest, msg: msg});
410 });
411 break;
412
413 /*case '/win':
414 if (parts[1] !== undefined) {
415 kiwi.front.ui.windowsShow(parseInt(parts[1], 10));
416 }
417 break;*/
418
419 case '/quit':
420 kiwi.gateway.quit(parts.slice(1).join(' '));
421 break;
422
423 case '/topic':
424 if (parts[1] === undefined) {
425 t = $('.cur_topic');
426 if (t.createTextRange) {
427 pos = t.text().length();
428 textRange = t.createTextRange();
429 textRange.collapse(true);
430 textRange.moveEnd(pos);
431 textRange.moveStart(pos);
432 textRange.select();
433 } else if (t.setSelectionRange) {
434 t.setSelectionRange(pos, pos);
435 }
436 } else {
437 if (kiwi.currentPanel.isChannel) {
438 kiwi.gateway.topic(kiwi.currentPanel.get("name"), msg.split(' ', 2)[1]);
439 }
440 }
441 break;
442
443 case '/kiwi':
444 kiwi.gateway.ctcp(true, 'KIWI', kiwi.currentPanel.get("name"), msg.substring(6));
445 break;
446
447 case '/ctcp':
448 parts = parts.slice(1);
449 dest = parts.shift();
450 t = parts.shift();
451 msg = parts.join(' ');
452 console.log(parts);
453
454 kiwi.gateway.ctcp(true, t, dest, msg, function () {
455 kiwi.channels.server.addMsg(null, 'CTCP Request', '[to ' + dest + '] ' + t + ' ' + msg, 'ctcp');
456 });
457 break;
458 default:
459 kiwi.currentPanel.addMsg(null, ' ', '--> Invalid command: '+parts[0].substring(1));
460 kiwi.gateway.raw(msg.substring(1));
461 break;
462 }
463
464 } else {
465 //alert('Sending message: '+msg);
466 if (msg.trim() === '') {
467 return;
468 }
469 if (kiwi.currentPanel.isChannel) {
470 kiwi.gateway.privmsg(kiwi.currentPanel.get("name"), msg, function () {
471 kiwi.currentPanel.addMsg(null, kiwi.gateway.nick, msg);
472 });
473 }
474 }
475 },
476
477
478
479
480
481 /**
482 * Sort the window list
483 */
484 sortWindowList: function () {
485 var win_list = $('#kiwi .windowlist ul'),
486 listitems = win_list.children('li').get();
487
488 /*listitems.sort(function (a, b) {
489 if (a === Tabview.getServerTab().tab[0]) {
490 return -1;
491 }
492 if (b === Tabview.getServerTab().tab[0]) {
493 return 1;
494 }
495 var compA = $(a).text().toUpperCase(),
496 compB = $(b).text().toUpperCase();
497 return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
498 });
499
500 $.each(listitems, function(idx, itm) {
501 win_list.append(itm);
502 });*/
503 },
504
505
506
507
508 /**
509 * Syncs with the Kiwi server
510 * Not implemented
511 */
512 sync: function () {
513 kiwi.gateway.sync();
514 },
515
516 /**
517 * Checks if a given name is the name of a channel
518 * @param {String} name The name to check
519 * @returns {Boolean} True if name is the name of a channel, false if it is not
520 */
521 isChannel: function (name) {
522 var prefix, is_chan;
523 prefix = name.charAt(0);
524 if (kiwi.gateway.channel_prefix.indexOf(prefix) > -1) {
525 is_chan = true;
526 } else {
527 is_chan = false;
528 }
529
530 return is_chan;
531 },
532
533 /**
534 * Formats a message. Adds bold, underline and colouring
535 * @param {String} msg The message to format
536 * @returns {String} The HTML formatted message
537 */
538 formatIRCMsg: function (msg) {
539 var re, next;
540
541 if ((!msg) || (typeof msg !== 'string')) {
542 return '';
543 }
544
545 // bold
546 if (msg.indexOf(String.fromCharCode(2)) !== -1) {
547 next = '<b>';
548 while (msg.indexOf(String.fromCharCode(2)) !== -1) {
549 msg = msg.replace(String.fromCharCode(2), next);
550 next = (next === '<b>') ? '</b>' : '<b>';
551 }
552 if (next === '</b>') {
553 msg = msg + '</b>';
554 }
555 }
556
557 // underline
558 if (msg.indexOf(String.fromCharCode(31)) !== -1) {
559 next = '<u>';
560 while (msg.indexOf(String.fromCharCode(31)) !== -1) {
561 msg = msg.replace(String.fromCharCode(31), next);
562 next = (next === '<u>') ? '</u>' : '<u>';
563 }
564 if (next === '</u>') {
565 msg = msg + '</u>';
566 }
567 }
568
569 // colour
570 /**
571 * @inner
572 */
573 msg = (function (msg) {
574 var replace, colourMatch, col, i, match, to, endCol, fg, bg, str;
575 replace = '';
576 /**
577 * @inner
578 */
579 colourMatch = function (str) {
580 var re = /^\x03([0-9][0-5]?)(,([0-9][0-5]?))?/;
581 return re.exec(str);
582 };
583 /**
584 * @inner
585 */
586 col = function (num) {
587 switch (parseInt(num, 10)) {
588 case 0:
589 return '#FFFFFF';
590 case 1:
591 return '#000000';
592 case 2:
593 return '#000080';
594 case 3:
595 return '#008000';
596 case 4:
597 return '#FF0000';
598 case 5:
599 return '#800040';
600 case 6:
601 return '#800080';
602 case 7:
603 return '#FF8040';
604 case 8:
605 return '#FFFF00';
606 case 9:
607 return '#80FF00';
608 case 10:
609 return '#008080';
610 case 11:
611 return '#00FFFF';
612 case 12:
613 return '#0000FF';
614 case 13:
615 return '#FF55FF';
616 case 14:
617 return '#808080';
618 case 15:
619 return '#C0C0C0';
620 default:
621 return null;
622 }
623 };
624 if (msg.indexOf('\x03') !== -1) {
625 i = msg.indexOf('\x03');
626 replace = msg.substr(0, i);
627 while (i < msg.length) {
628 /**
629 * @inner
630 */
631 match = colourMatch(msg.substr(i, 6));
632 if (match) {
633 //console.log(match);
634 // Next colour code
635 to = msg.indexOf('\x03', i + 1);
636 endCol = msg.indexOf(String.fromCharCode(15), i + 1);
637 if (endCol !== -1) {
638 if (to === -1) {
639 to = endCol;
640 } else {
641 to = ((to < endCol) ? to : endCol);
642 }
643 }
644 if (to === -1) {
645 to = msg.length;
646 }
647 //console.log(i, to);
648 fg = col(match[1]);
649 bg = col(match[3]);
650 str = msg.substring(i + 1 + match[1].length + ((bg !== null) ? match[2].length + 1 : 0), to);
651 //console.log(str);
652 replace += '<span style="' + ((fg !== null) ? 'color: ' + fg + '; ' : '') + ((bg !== null) ? 'background-color: ' + bg + ';' : '') + '">' + str + '</span>';
653 i = to;
654 } else {
655 if ((msg[i] !== '\x03') && (msg[i] !== String.fromCharCode(15))) {
656 replace += msg[i];
657 }
658 i++;
659 }
660 }
661 return replace;
662 }
663 return msg;
664 }(msg));
665
666 return msg;
667 },
668
669 /**
670 * Registers Kiwi IRC as a handler for the irc:// protocol in the browser
671 */
672 registerProtocolHandler: function () {
673 var state, uri;
674 url = kiwi_server.replace(/\/kiwi$/, '/?ircuri=%s');
675 try {
676 //state = window.navigator.isProtocolHandlerRegistered('irc', url);
677 //if (state !== 'registered') {
678 window.navigator.registerProtocolHandler('irc', url, 'Kiwi IRC');
679 //}
680 } catch (e) {
681 console.log('Unable to register Kiwi IRC as a handler for irc:// links');
682 console.error(e);
683 }
684 }
685
686 };
687
688
689
690
691
692 /**
693 * @constructor
694 */
695 var ChannelList = function () {
696 /*globals Utilityview */
697 var chanList, view, table, obj, renderTable, waiting;
698 chanList = [];
699
700 view = new Utilityview('Channel List');
701 view.div.css('overflow-y', 'scroll');
702
703 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>');
704 table = table.appendTo(view.div);
705
706 waiting = false;
707 /**
708 * @inner
709 */
710 renderTable = function () {
711 var tbody;
712 tbody = table.children('tbody:first').detach();
713 /*tbody.children().each(function (child) {
714 var i, chan;
715 child = $(child);
716 chan = child.children('td:first').text();
717 for (i = 0; i < chanList.length; i++) {
718 if (chanList[i].channel === chan) {
719 chanList[i].html = child.detach();
720 break;
721 }
722 }
723 });*/
724 _.each(chanList, function (chan) {
725 chan.html = $(chan.html).appendTo(tbody);
726 });
727 table = table.append(tbody);
728 waiting = false;
729 };
730 /**
731 * @lends ChannelList
732 */
733 return {
734 /**
735 * Adds a channel or channels to the list
736 * @param {Object} channels The channel or Array of channels to add
737 */
738 addChannel: function (channels) {
739 if (!_.isArray(channels)) {
740 channels = [channels];
741 }
742 _.each(channels, function (chan) {
743 var html, channel;
744 html = $('<tr><td><a class="chan">' + chan.channel + '</a></td><td class="num_users" style="text-align: center;">' + chan.num_users + '</td><td style="padding-left: 2em;">' + kiwi.front.formatIRCMsg(chan.topic) + '</td></tr>');
745 chan.html = html;
746 chanList.push(chan);
747 });
748 chanList.sort(function (a, b) {
749 return b.num_users - a.num_users;
750 });
751 if (!waiting) {
752 waiting = true;
753 _.defer(renderTable);
754 }
755 },
756 /**
757 * Show the {@link UtilityView} that will display this channel list
758 */
759 show: function () {
760 view.show();
761 },
762 /**
763 * @private
764 */
765 prototype: {constructor: this}
766 };
767 };
768
769 /*
770 * MISC VIEW
771 */
772 /**
773 * @constructor
774 * A tab to show non-channel and non-query windows to the user
775 * @param {String} name The name of the view
776 */
777 var Utilityview = function (name) {
778 var rand_name = randomString(15),
779 tmp_divname = 'kiwi_window_' + rand_name,
780 tmp_tabname = 'kiwi_tab_' + rand_name;
781
782 this.name = rand_name;
783 this.title = name;
784 this.topic = ' ';
785 this.panel = $('#panel1');
786
787 if (typeof $('.scroller', this.panel)[0] === 'undefined') {
788 this.panel.append('<div id="' + tmp_divname + '" class="messages"></div>');
789 } else {
790 $('.scroller', this.panel).append('<div id="' + tmp_divname + '" class="messages"></div>');
791 }
792
793 this.tab = $('<li id="' + tmp_tabname + '">' + this.title + '</li>');
794 this.tab.click(function () {
795 kiwi.front.utilityviews[rand_name.toLowerCase()].show();
796 });
797 $('#kiwi .utilityviewlist ul').append(this.tab);
798 kiwi.front.ui.doLayoutSize();
799
800 this.div = $('#' + tmp_divname);
801 this.div.css('overflow', 'hidden');
802
803 kiwi.front.utilityviews[rand_name.toLowerCase()] = this;
804 };
805
806 Utilityview.prototype.name = null;
807 Utilityview.prototype.title = null;
808 Utilityview.prototype.div = null;
809 Utilityview.prototype.tab = null;
810 Utilityview.prototype.topic = ' ';
811 Utilityview.prototype.panel = null;
812 /**
813 * Brings this view to the foreground
814 */
815 Utilityview.prototype.show = function () {
816 $('.messages', this.panel).removeClass("active");
817 $('#kiwi .userlist ul').removeClass("active");
818 $('#kiwi .toolbars ul li').removeClass("active");
819
820 this.panel.css('overflow-y', 'hidden');
821 $('#windows').css('right', 0);
822 // Activate this tab!
823 this.div.addClass('active');
824 this.tab.addClass('active');
825
826 this.addPartImage();
827
828 kiwi.front.ui.setTopicText(this.topic);
829 kiwi.front.cur_channel = this;
830
831 // If we're using fancy scrolling, refresh it
832 if (touch_scroll) {
833 touch_scroll.refresh();
834 }
835 };
836 /**
837 * Sets a new panel to be this view's parent
838 * @param {JQuery} new_panel The new parent
839 */
840 Utilityview.prototype.setPanel = function (new_panel) {
841 this.div.detach();
842 this.panel = new_panel;
843 this.panel.append(this.div);
844 this.show();
845 };
846 /**
847 * Removes the panel from the UI and destroys its contents
848 */
849 Utilityview.prototype.close = function () {
850 this.div.remove();
851 this.tab.remove();
852
853 if (Tabview.getCurrentTab() === this) {
854 kiwi.front.tabviews.server.show();
855 }
856 delete kiwi.front.utilityviews[this.name.toLowerCase()];
857 };
858 /**
859 * Adds the close image to the tab
860 */
861 Utilityview.prototype.addPartImage = function () {
862 this.clearPartImage();
863
864 // We can't close this tab, so don't have the close image
865 if (this.name === 'server') {
866 return;
867 }
868
869 var del_html = '<img src="/img/redcross.png" class="tab_part" />';
870 this.tab.append(del_html);
871
872 $('.tab_part', this.tab).click(function () {
873 if (Tabview.getCurrentTab().name !== 'server') {
874 Tabview.getCurrentTab().close();
875 }
876 });
877 };
878 /**
879 * Removes the close image from the tab
880 */
881 Utilityview.prototype.clearPartImage = function () {
882 $('#kiwi .toolbars .tab_part').remove();
883 };
884
885 /**
886 * @constructor
887 * Floating message box
888 * @param {String} classname The CSS classname to apply to the box
889 */
890 var Box = function (classname) {
891 this.id = randomString(10);
892 var tmp = $('<div id="' + this.id + '" class="box ' + classname + '"><div class="boxarea"></div></div>');
893 $('#kiwi').append(tmp);
894 this.box = $('#' + this.id);
895 this.content = $('#' + this.id + ' .boxarea');
896
897 this.box.draggable({ stack: ".box" });
898 this.content.click(function () {});
899 //this.box.click(function(){ $(this)..css });
900 };
901 Box.prototype.create = function (name, classname) {
902
903 };
904 Box.prototype.id = null;
905 Box.prototype.box = null;
906 Box.prototype.content = null;
907 Box.prototype.destroy = function () {
908 var name;
909 this.box.remove();
910 for (name in kiwi.front.boxes) {
911 if (kiwi.front.boxes[name].id === this.id) {
912 delete kiwi.front.boxes[name];
913 }
914 }
915 };
916 Box.prototype.height = function () {
917 return this.box.height();
918 };