Merge https://github.com/M2Ys4U/KiwiIRC
[KiwiIRC.git] / js / front.js
1 /*jslint devel: true, undef: true, browser: true, continue: true, sloppy: true, forin: true, newcap: true, plusplus: true, maxerr: 50, indent: 4 */
2 /*global gateway, io, $, iScroll, agent, touchscreen, init_data, plugs, plugins, registerTouches, randomString */
3 var 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 original_topic: '',
16
17 init: function () {
18 /*global Box, touch_scroll:true */
19 var about_info, supportsOrientationChange, orientationEvent, scroll_opts;
20 gateway.nick = 'kiwi_' + Math.ceil(100 * Math.random()) + Math.ceil(100 * Math.random());
21 gateway.session_id = null;
22
23 $(gateway).bind("onmsg", front.onMsg);
24 $(gateway).bind("onnotice", front.onNotice);
25 $(gateway).bind("onaction", front.onAction);
26 $(gateway).bind("onmotd", front.onMOTD);
27 $(gateway).bind("onoptions", front.onOptions);
28 $(gateway).bind("onconnect", front.onConnect);
29 $(gateway).bind("onconnect_fail", front.onConnectFail);
30 $(gateway).bind("ondisconnect", front.onDisconnect);
31 $(gateway).bind("onnick", front.onNick);
32 $(gateway).bind("onuserlist", front.onUserList);
33 $(gateway).bind("onuserlist_end", front.onUserListEnd);
34 $(gateway).bind("onjoin", front.onJoin);
35 $(gateway).bind("ontopic", front.onTopic);
36 $(gateway).bind("onpart", front.onPart);
37 $(gateway).bind("onkick", front.onKick);
38 $(gateway).bind("onquit", front.onQuit);
39 $(gateway).bind("onmode", front.onMode);
40 $(gateway).bind("onwhois", front.onWhois);
41 $(gateway).bind("onsync", front.onSync);
42 $(gateway).bind("onchannel_redirect", front.onChannelRedirect);
43 $(gateway).bind("ondebug", front.onDebug);
44 $(gateway).bind("onctcp_request", front.onCTCPRequest);
45 $(gateway).bind("onctcp_response", front.onCTCPResponse);
46 $(gateway).bind("onirc_error", front.onIRCError);
47 $(gateway).bind("onkiwi", front.onKiwi);
48
49 this.buffer = [];
50
51 // Build the about box
52 front.boxes.about = new Box("about");
53 about_info = 'UI adapted for ' + agent;
54 if (touchscreen) {
55 about_info += ' touchscreen ';
56 }
57 about_info += 'usage';
58 $('#tmpl_about_box').tmpl({
59 about: about_info,
60 front_revision: front.revision,
61 gateway_revision: gateway.revision
62 }).appendTo(front.boxes.about.content);
63
64 //$(window).bind("beforeunload", function(){ gateway.quit(); });
65
66 if (touchscreen) {
67 $('#kiwi').addClass('touchscreen');
68
69 // Single touch scrolling through scrollback for touchscreens
70 scroll_opts = {};
71 touch_scroll = new iScroll('windows', scroll_opts);
72 }
73
74 front.registerKeys();
75
76 $('#kiwi .toolbars').resize(front.doLayoutSize);
77
78 $('#kiwi .formconnectwindow').submit(function () {
79 var netsel = $('#kiwi .formconnectwindow .network'),
80 nick = $('#kiwi .formconnectwindow .nick'),
81 tmp;
82
83 if (nick.val() === '') {
84 nick.val('Nick please!');
85 nick.focus();
86 return false;
87 }
88
89 tmp = nick.val().split(' ');
90 gateway.nick = tmp[0];
91
92 init_data.channel = $('#channel').val();
93
94 front.doLayout();
95 try {
96 front.run('/connect ' + netsel.val());
97 } catch (e) {
98 alert(e);
99 }
100
101 $('#kiwi .connectwindow').slideUp('', front.barsShow);
102 $('#windows').click(function () { $('#kiwi_msginput').focus(); });
103
104 return false;
105 });
106
107 supportsOrientationChange = (typeof window.onorientationchange !== undefined);
108 orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
109 if (window.addEventListener) {
110 window.addEventListener(orientationEvent, front.doLayoutSize, false);
111 } else {
112 // < IE9
113 window.attachEvent(orientationEvent, front.doLayoutSize, false);
114 }
115 //$('#kiwi').bind("resize", front.doLayoutSize, false);
116
117 front.doLayout();
118 front.barsHide();
119
120 front.tabviewAdd('server');
121 front.tabviews.server.userlist_width = 0; // Disable the userlist
122
123 // Any pre-defined nick?
124 if (typeof window.init_data.nick === "string") {
125 $('#kiwi .formconnectwindow .nick').val(init_data.nick);
126 }
127
128 // Any pre-defined channels?
129 if (typeof window.init_data.channel === 'string') {
130 $('#channel').val(init_data.channel);
131 }
132
133 // Fix for Opera inserting a spurious <br/>
134 $('#kiwi .cur_topic br').remove();
135
136 $('#kiwi .cur_topic').keydown(function (e) {
137 if (e.which === 13) {
138 // enter
139 e.preventDefault();
140 $(this).change();
141 $('#kiwi_msginput').focus();
142 } else if (e.which === 27) {
143 // escape
144 e.preventDefault();
145 $(this).text(front.original_topic);
146 $('#kiwi_msginput').focus();
147 }
148 });
149 /*$('.cur_topic').live('keypress', function(e) {
150 if (e.keyCode === 13) {
151 // enter
152 e.preventDefault();
153 $(this).change();
154 $('#kiwi_msginput').focus();
155 } else if (e.keyCode === 27) {
156 // escape
157 e.preventDefault();
158 $(this).text(front.original_topic);
159 }
160 });*/
161 $('.cur_topic').live('change', function () {
162 var chan, text;
163 text = $(this).text();
164 if (text !== front.original_topic) {
165 chan = front.cur_channel.name;
166 gateway.setTopic(chan, text);
167 }
168 });
169
170
171 $('#windows a.chan').live('click', function () {
172 front.joinChannel($(this).text());
173 return false;
174 });
175
176 (function () {
177 var i;
178 for (i in plugins) {
179 plugs.loadPlugin(plugins[i]);
180 }
181 }());
182 },
183
184 doLayoutSize: function () {
185 var kiwi, toolbars, ul, n_top, n_bottom;
186 kiwi = $('#kiwi');
187
188 if (kiwi.width() < 330 && !kiwi.hasClass('small_kiwi')) {
189 console.log("switching to small kiwi");
190 kiwi.removeClass('large_kiwi');
191 kiwi.addClass('small_kiwi');
192 } else if (kiwi.width() >= 330 && !kiwi.hasClass('large_kiwi')) {
193 kiwi.removeClass('small_kiwi');
194 kiwi.addClass('large_kiwi');
195 }
196
197 toolbars = $('#kiwi .cur_topic');
198 ul = $('#kiwi .userlist');
199
200 n_top = parseInt(toolbars.offset().top, 10) + parseInt(toolbars.outerHeight(true), 10);
201 n_bottom = $(document).height() - parseInt($('#kiwi .control').offset().top, 10);
202
203 $('#kiwi .windows').css({top: n_top + 'px', bottom: n_bottom + 'px'});
204 $('#kiwi .userlist').css({top: n_top + 'px', bottom: n_bottom + 'px'});
205 },
206
207
208 doLayout: function () {
209 $('#kiwi .msginput .nick a').text(gateway.nick);
210 $('#kiwi_msginput').val(' ');
211 $('#kiwi_msginput').focus();
212 },
213
214
215 joinChannel: function (chan_name) {
216 var chans = chan_name.split(','),
217 i,
218 chan;
219 for (i in chans) {
220 chan = chans[i];
221 if (front.tabviews[chan.toLowerCase()] === undefined || (front.tabviews[chan.toLowerCase()] !== undefined && front.tabviews[chan.toLowerCase()].safe_to_close === true)) {
222 gateway.join(chan);
223 front.tabviewAdd(chan);
224 } else {
225 front.tabviews[chan.toLowerCase()].show();
226 }
227 }
228 },
229
230
231 run: function (msg) {
232 var parts, dest, t, pos, textRange, d, plugin_event, msg_sliced;
233
234 // Run through any plugins
235 plugin_event = {command: msg};
236 plugin_event = plugs.run('command_run', plugin_event);
237 if (!plugin_event || typeof plugin_event.command === 'undefined') {
238 return;
239 }
240
241 // Update msg if it's been changed by any plugins
242 msg = plugin_event.command.toString();
243
244 console.log("running " + msg);
245 if (msg.substring(0, 1) === '/') {
246 parts = msg.split(' ');
247 switch (parts[0].toLowerCase()) {
248 case '/j':
249 case '/join':
250 front.joinChannel(parts[1]);
251 break;
252
253 case '/connect':
254 case '/server':
255 if (parts[1] === undefined) {
256 alert('Usage: /connect servername [port]');
257 break;
258 }
259
260 if (parts[2] === undefined) {
261 parts[2] = 6667;
262 }
263 front.cur_channel.addMsg(null, ' ', '=== Connecting to ' + parts[1] + '...', 'status');
264 gateway.connect(parts[1], parts[2], 0);
265 break;
266
267 case '/nick':
268 console.log("/nick");
269 if (parts[1] === undefined) {
270 console.log("calling show nick");
271 front.showChangeNick();
272 } else {
273 console.log("sending raw");
274 gateway.raw(msg.substring(1));
275 }
276 break;
277
278 case '/part':
279 if (typeof parts[1] === "undefined") {
280 if (front.cur_channel.safe_to_close) {
281 front.cur_channel.close();
282 } else {
283 gateway.raw(msg.substring(1) + ' ' + front.cur_channel.name);
284 }
285 } else {
286 gateway.raw(msg.substring(1));
287 }
288 break;
289
290 case '/names':
291 if (typeof parts[1] !== "undefined") {
292 gateway.raw(msg.substring(1));
293 }
294 break;
295
296 case '/debug':
297 gateway.debug();
298 break;
299
300 case '/q':
301 case '/query':
302 if (typeof parts[1] !== "undefined") {
303 front.tabviewAdd(parts[1]);
304 }
305 break;
306
307
308 case '/m':
309 case '/msg':
310 if (typeof parts[1] !== "undefined") {
311 msg_sliced = msg.split(' ').slice(2).join(' ');
312 gateway.msg(parts[1], msg_sliced);
313
314 if (!front.tabviewExists(parts[1])) {
315 front.tabviewAdd(parts[1]);
316 }
317 front.tabviews[parts[1].toLowerCase()].addMsg(null, gateway.nick, msg_sliced);
318 }
319 break;
320
321 case '/k':
322 case '/kick':
323 if (typeof parts[1] === 'undefined') return;
324 gateway.raw('KICK ' + front.cur_channel.name + ' ' + msg.split(' ', 2)[1]);
325 break;
326
327 case '/quote':
328 gateway.raw(msg.replace(/^\/quote /i, ''));
329 break;
330
331 case '/me':
332 gateway.action(front.cur_channel.name, msg.substring(4));
333 //front.tabviews[destination.toLowerCase()].addMsg(null, ' ', '* '+data.nick+' '+data.msg, 'color:green;');
334 front.cur_channel.addMsg(null, ' ', '* ' + gateway.nick + ' ' + msg.substring(4), 'action', 'color:#555;');
335 break;
336
337 case '/notice':
338 dest = parts[1];
339 msg = parts.slice(2).join(' ');
340
341 gateway.notice(dest, msg);
342 this.onNotice({}, {nick: gateway.nick, channel: dest, msg: msg});
343 break;
344
345 case '/win':
346 if (parts[1] !== undefined) {
347 front.windowsShow(parseInt(parts[1], 10));
348 }
349 break;
350
351 case '/quit':
352 gateway.quit(msg.split(" ", 2)[1]);
353 break;
354
355 case '/topic':
356 if (parts[1] === undefined) {
357 t = $('.cur_topic');
358 if (t.createTextRange) {
359 pos = t.text().length();
360 textRange = t.createTextRange();
361 textRange.collapse(true);
362 textRange.moveEnd(pos);
363 textRange.moveStart(pos);
364 textRange.select();
365 } else if (t.setSelectionRange) {
366 t.setSelectionRange(pos, pos);
367 }
368 } else {
369 gateway.setTopic(front.cur_channel.name, msg.split(' ', 2)[1]);
370 //gateway.raw('TOPIC ' + front.cur_channel.name + ' :' + msg.split(' ', 2)[1]);
371 }
372 break;
373
374 case '/kiwi':
375 gateway.kiwi(front.cur_channel.name, msg.substring(6));
376 break;
377
378 default:
379 //front.cur_channel.addMsg(null, ' ', '--> Invalid command: '+parts[0].substring(1));
380 gateway.raw(msg.substring(1));
381 }
382
383 } else {
384 //alert('Sending message: '+msg);
385 if (msg.trim() === '') {
386 return;
387 }
388 if (front.cur_channel.name !== 'server') {
389 gateway.msg(front.cur_channel.name, msg);
390 d = new Date();
391 d = d.getHours() + ":" + d.getMinutes();
392 //front.addMsg(d, gateway.nick, msg);
393 front.cur_channel.addMsg(null, gateway.nick, msg);
394 }
395 }
396 },
397
398
399 onMsg: function (e, data) {
400 var destination, plugin_event;
401 // Is this message from a user?
402 if (data.channel === gateway.nick) {
403 destination = data.nick.toLowerCase();
404 } else {
405 destination = data.channel.toLowerCase();
406 }
407
408 plugin_event = {nick: data.nick, msg: data.msg, destination: destination};
409 plugin_event = plugs.run('msg_recieved', plugin_event);
410 if (!plugin_event) {
411 return;
412 }
413
414 if (!front.tabviewExists(plugin_event.destination)) {
415 front.tabviewAdd(plugin_event.destination);
416 }
417 front.tabviews[plugin_event.destination].addMsg(null, plugin_event.nick, plugin_event.msg);
418 },
419
420 onDebug: function (e, data) {
421 if (!front.tabviewExists('kiwi_debug')) {
422 front.tabviewAdd('kiwi_debug');
423 }
424 front.tabviews.kiwi_debug.addMsg(null, ' ', data.msg);
425 },
426
427 onAction: function (e, data) {
428 var destination;
429 // Is this message from a user?
430 if (data.channel === gateway.nick) {
431 destination = data.nick;
432 } else {
433 destination = data.channel;
434 }
435
436 if (!front.tabviewExists(destination)) {
437 front.tabviewAdd(destination);
438 }
439 front.tabviews[destination.toLowerCase()].addMsg(null, ' ', '* ' + data.nick + ' ' + data.msg, 'action', 'color:#555;');
440 },
441
442 onTopic: function (e, data) {
443 if (front.tabviewExists(data.channel)) {
444 front.tabviews[data.channel.toLowerCase()].changeTopic(data.topic);
445 }
446 },
447
448 onNotice: function (e, data) {
449 var nick = (data.nick === undefined) ? '' : data.nick,
450 enick = '[' + nick + ']';
451
452 if (front.tabviewExists(data.target)) {
453 front.tabviews[data.target.toLowerCase()].addMsg(null, enick, data.msg, 'notice');
454 } else if (front.tabviewExists(nick)) {
455 front.tabviews[nick.toLowerCase()].addMsg(null, enick, data.msg, 'notice');
456 } else {
457 front.tabviews.server.addMsg(null, enick, data.msg, 'notice');
458 }
459 },
460
461 onCTCPRequest: function (e, data) {
462 var msg = data.msg.split(" ", 2);
463 switch (msg[0]) {
464 case 'PING':
465 if (typeof msg[1] === 'undefined') {
466 msg[1] = '';
467 }
468 gateway.notice(data.nick, String.fromCharCode(1) + 'PING ' + msg[1] + String.fromCharCode(1));
469 break;
470 case 'TIME':
471 gateway.notice(data.nick, String.fromCharCode(1) + 'TIME ' + (new Date()).toLocaleString() + String.fromCharCode(1));
472 break;
473 }
474 front.tabviews.server.addMsg(null, 'CTCP [' + data.nick + ']', data.msg, 'ctcp');
475 },
476
477 onCTCPResponse: function (e, data) {
478 },
479
480 onKiwi: function (e, data) {
481 //console.log(data);
482 },
483
484 onConnect: function (e, data) {
485 if (data.connected) {
486 if (gateway.nick !== data.nick) {
487 gateway.nick = data.nick;
488 front.doLayout();
489 }
490
491 front.tabviews.server.addMsg(null, ' ', '=== Connected OK :)', 'status');
492 if (typeof init_data.channel === "string") {
493 front.joinChannel(init_data.channel);
494 }
495 plugs.run('connect', {success: true});
496 } else {
497 front.tabviews.server.addMsg(null, ' ', '=== Failed to connect :(', 'status');
498 plugs.run('connect', {success: false});
499 }
500 },
501 onConnectFail: function (e, data) {
502 var reason = (typeof data.reason === 'string') ? data.reason : '';
503 front.tabviews.server.addMsg(null, '', 'There\'s a problem connecting! (' + reason + ')', 'error');
504 plugs.run('connect', {success: false});
505 },
506 onDisconnect: function (e, data) {
507 var tab;
508 for (tab in front.tabviews) {
509 front.tabviews[tab].addMsg(null, '', 'Disconnected from server!', 'error');
510 }
511 plugs.run('disconnect', {success: false});
512 },
513 onOptions: function (e, data) {
514 if (typeof gateway.network_name === "string" && gateway.network_name !== "") {
515 front.tabviews.server.tab.text(gateway.network_name);
516 }
517 },
518 onMOTD: function (e, data) {
519 front.tabviews.server.addMsg(null, data.server, data.msg, 'motd');
520 },
521 onWhois: function (e, data) {
522 var d;
523 if (data.msg) {
524 front.cur_channel.addMsg(null, data.nick, data.msg, 'whois');
525 } else if (data.logon) {
526 d = new Date();
527 d.setTime(data.logon * 1000);
528 d = d.toLocaleString();
529 front.cur_channel.addMsg(null, data.nick, 'idle for ' + data.idle + ' second' + ((data.idle !== 1) ? 's' : '') + ', signed on ' + d, 'whois');
530 } else {
531 front.cur_channel.addMsg(null, data.nick, 'idle for ' + data.idle + ' seconds', 'whois');
532 }
533 },
534 onMode: function (e, data) {
535 var i, new_nick_text;
536
537 // TODO: Store the modes in the elements data, then work out the current
538 // mode symbol from the highest mode. Eg. -h may leave +o from previous modes; It
539 // doesn't simply clear it! ~Darren
540 if (typeof data.channel === 'string' && typeof data.effected_nick === 'string') {
541 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '[' + data.mode + '] ' + data.effected_nick + ' by ' + data.nick, 'mode', '');
542 front.tabviews[data.channel.toLowerCase()].userlist.children().each(function () {
543 if (front.nickStripPrefix($(this).text()) === data.effected_nick) {
544
545 if (data.mode.split('')[0] === '+') {
546 for (i in gateway.user_prefixes) {
547 if (gateway.user_prefixes[i].mode == data.mode.split('')[1]) {
548 new_nick_text = gateway.user_prefixes[i].symbol + data.effected_nick;
549 break;
550 }
551 }
552 } else if (data.mode.split('')[0] === '-') {
553 new_nick_text = data.effected_nick;
554 }
555
556 if (new_nick_text !== '') {
557 $(this).text(new_nick_text);
558 return false;
559 }
560
561 }
562 });
563 }
564 },
565 onUserList: function (e, data) {
566 var ul, nick, mode;
567 if (front.tabviews[data.channel.toLowerCase()] === undefined) {
568 return;
569 }
570 ul = front.tabviews[data.channel.toLowerCase()].userlist;
571
572 if (!document.userlist_updating) {
573 document.userlist_updating = true;
574 ul.empty();
575 }
576
577 $.each(data.users, function (i, item) {
578 nick = i; //i.match(/^.+!/g);
579 mode = item;
580 $('<li><a class="nick" onclick="front.userClick(this);">' + mode + nick + '</a></li>').appendTo(ul);
581 });
582
583 front.tabviews[data.channel.toLowerCase()].userlistSort();
584 },
585 onUserListEnd: function (e, data) {
586 document.userlist_updating = false;
587 },
588
589 onJoin: function (e, data) {
590 if (!front.tabviewExists(data.channel)) {
591 front.tabviewAdd(data.channel.toLowerCase());
592 }
593
594 if (data.nick === gateway.nick) {
595 return; // Not needed as it's already in nicklist
596 }
597 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '--> ' + data.nick + ' has joined', 'action', 'color:#009900;');
598 $('<li><a class="nick" onclick="front.userClick(this);">' + data.nick + '</a></li>').appendTo(front.tabviews[data.channel.toLowerCase()].userlist);
599 front.tabviews[data.channel.toLowerCase()].userlistSort();
600 },
601 onPart: function (e, data) {
602 if (front.tabviewExists(data.channel)) {
603 // If this is us, close the tabview
604 if (data.nick === gateway.nick) {
605 front.tabviews[data.channel.toLowerCase()].close();
606 front.tabviews.server.show();
607 return;
608 }
609
610 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '<-- ' + data.nick + ' has left (' + data.message + ')', 'action', 'color:#990000;');
611 front.tabviews[data.channel.toLowerCase()].userlist.children().each(function () {
612 if ($(this).text() === data.nick) {
613 $(this).remove();
614 }
615 });
616 }
617 },
618 onKick: function (e, data) {
619 if (front.tabviewExists(data.channel)) {
620 // If this is us, close the tabview
621 if (data.kicked === gateway.nick) {
622 //front.tabviews[data.channel.toLowerCase()].close();
623 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '=== You have been kicked from ' + data.channel + '. ' + data.message, 'status');
624 front.tabviews[data.channel.toLowerCase()].safe_to_close = true;
625 $('li', front.tabviews[data.channel.toLowerCase()].userlist).remove();
626 return;
627 }
628
629 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '<-- ' + data.kicked + ' kicked by ' + data.nick + '(' + data.message + ')', 'action', 'color:#990000;');
630 front.tabviews[data.channel.toLowerCase()].userlist.children().each(function () {
631 if ($(this).text() === data.nick) {
632 $(this).remove();
633 }
634 });
635 }
636 },
637 onNick: function (e, data) {
638 if (data.nick === gateway.nick) {
639 gateway.nick = data.newnick;
640 front.doLayout();
641 }
642
643 $.each(front.tabviews, function (i, item) {
644 $.each(front.tabviews, function (i, item) {
645 item.changeNick(data.newnick, data.nick);
646 });
647 });
648 },
649 onQuit: function (e, data) {
650 $.each(front.tabviews, function (i, item) {
651 $.each(front.tabviews, function (i, item) {
652 item.userlist.children().each(function () {
653 if ($(this).text() === data.nick) {
654 $(this).remove();
655 item.addMsg(null, ' ', '<-- ' + data.nick + ' has quit (' + data.message + ')', 'action', 'color:#990000;');
656 }
657 });
658 });
659 });
660 },
661 onChannelRedirect: function (e, data) {
662 front.tabviews[data.from.toLowerCase()].close();
663 front.tabviewAdd(data.to.toLowerCase());
664 front.tabviews[data.to.toLowerCase()].addMsg(null, ' ', '=== Redirected from ' + data.from, 'action');
665 },
666
667 onIRCError: function (e, data) {
668 var t_view;
669 if (data.channel !== undefined && front.tabviewExists(data.channel)) {
670 t_view = data.channel;
671 } else {
672 t_view = 'server';
673 }
674
675 switch (data.error) {
676 case 'banned_from_channel':
677 front.tabviews[t_view].addMsg(null, ' ', '=== You are banned from ' + data.channel + '. ' + data.reason, 'status');
678 if (t_view !== 'server') {
679 front.tabviews[t_view].safe_to_close = true;
680 }
681 break;
682 case 'bad_channel_key':
683 front.tabviews[t_view].addMsg(null, ' ', '=== Bad channel key for ' + data.channel, 'status');
684 if (t_view !== 'server') {
685 front.tabviews[t_view].safe_to_close = true;
686 }
687 break;
688 case 'invite_only_channel':
689 front.tabviews[t_view].addMsg(null, ' ', '=== ' + data.channel + ' is invite only.', 'status');
690 if (t_view !== 'server') {
691 front.tabviews[t_view].safe_to_close = true;
692 }
693 break;
694 case 'channel_is_full':
695 front.tabviews[t_view].addMsg(null, ' ', '=== ' + data.channel + ' is full.', 'status');
696 if (t_view !== 'server') {
697 front.tabviews[t_view].safe_to_close = true;
698 }
699 break;
700 case 'chanop_privs_needed':
701 front.tabviews[data.channel].addMsg(null, ' ', '=== ' + data.reason, 'status');
702 break;
703 case 'no_such_nick':
704 front.tabviews.server.addMsg(null, ' ', '=== ' + data.nick + ': ' + data.reason, 'status');
705 break;
706 case 'nickname_in_use':
707 front.tabviews.server.addMsg(null, ' ', '=== The nickname ' + data.nick + ' is already in use. Please select a new nickname', 'status');
708 front.showChangeNick();
709 break;
710 default:
711 // We don't know what data contains, so don't do anything with it.
712 //front.tabviews.server.addMsg(null, ' ', '=== ' + data, 'status');
713 }
714 },
715
716 registerKeys: function () {
717 $('#kiwi_msginput').bind('keydown', function (e) {
718 var windows, meta, num, msg, data, candidates, word_pos, word, i;
719 windows = $('#windows');
720 //var meta = e.altKey;
721 meta = e.ctrlKey;
722
723 switch (true) {
724 case (e.which >= 48) && (e.which <= 57):
725 if (meta) {
726 num = e.which - 48;
727 if (num === 0) {
728 num = 10;
729 }
730 num = num - 1;
731 front.windowsShow(num);
732 return false;
733 }
734 break;
735 case e.which === 27: // escape
736 return false;
737 case e.which === 13: // return
738 msg = $('#kiwi_msginput').val();
739 msg = msg.trim();
740
741 front.buffer.push(msg);
742 front.buffer_pos = front.buffer.length;
743
744 front.run(msg);
745 $('#kiwi_msginput').val('');
746
747 break;
748 case e.which === 33: // page up
749 console.log("page up");
750 windows[0].scrollTop = windows[0].scrollTop - windows.height();
751 return false;
752 case e.which === 34: // page down
753 windows[0].scrollTop = windows[0].scrollTop + windows.height();
754 return false;
755 case e.which === 37: // left
756 if (meta) {
757 front.windowsPrevious();
758 return false;
759 }
760 break;
761 case e.which === 38: // up
762 if (front.buffer_pos > 0) {
763 front.buffer_pos--;
764 $('#kiwi_msginput').val(front.buffer[front.buffer_pos]);
765 }
766 break;
767 case e.which === 39: // right
768 if (meta) {
769 front.windowsNext();
770 return false;
771 }
772 break;
773 case e.which === 40: // down
774 if (front.buffer_pos < front.buffer.length) {
775 front.buffer_pos++;
776 $('#kiwi_msginput').val(front.buffer[front.buffer_pos]);
777 }
778 break;
779
780 case e.which === 9: // tab
781 // Get possible autocompletions
782 data = [];
783 front.cur_channel.userlist.children().each(function () {
784 var nick;
785 nick = front.nickStripPrefix($('a.nick', this).text());
786 data.push(nick);
787 });
788
789 // Do the autocomplete
790 if (this.value.length === this.selectionStart && this.value.length === this.selectionEnd) {
791 candidates = [];
792
793 word_pos = this.value.lastIndexOf(' ');
794 word = "";
795 if (word_pos === -1) {
796 word = this.value;
797 } else {
798 word = this.value.substr(word_pos);
799 }
800 word = word.trim();
801
802 // filter data to find only strings that start with existing value
803 for (i = 0; i < data.length; i++) {
804 if (data[i].indexOf(word) === 0 && data[i].length > word.length) {
805 candidates.push(data[i]);
806 }
807 }
808
809 if (candidates.length > 0) {
810 // some candidates for autocompletion are found
811 this.value = this.value.substring(0, word_pos) + ' ' + candidates[0] + ': ';
812 this.selectionStart = this.value.length;
813 }
814 }
815 return false;
816 }
817 });
818
819
820 $('#kiwi .control .msginput .nick').click(function () {
821 front.showChangeNick();
822 });
823
824
825
826
827
828 $('#kiwi .plugins .load_plugin_file').click(function () {
829 if (typeof front.boxes.plugins !== "undefined") {
830 return;
831 }
832
833 front.boxes.plugins = new Box("plugin_file");
834 $('#tmpl_plugins').tmpl({}).appendTo(front.boxes.plugins.content);
835 front.boxes.plugins.box.css('top', -(front.boxes.plugins.height + 40));
836
837 // Populate the plugin list..
838 function enumPlugins() {
839 var lst, j, txt;
840 lst = $('#plugin_list');
841 lst.find('option').remove();
842 for (j in plugs.loaded) {
843 txt = plugs.loaded[j].name;
844 lst.append('<option value="' + txt + '">' + txt + '</option>');
845 }
846 }
847 enumPlugins();
848
849 // Event bindings
850 $('#kiwi .plugin_file').submit(function () {
851 $('<div></div>').load($('.txtpluginfile').val(), function (e) {
852 enumPlugins();
853 });
854 return false;
855 });
856 $('#kiwi .cancelpluginfile').click(function () {
857 front.boxes.plugins.destroy();
858 });
859
860 $('#kiwi #plugins_list_unload').click(function () {
861 var selected_plugin;
862 selected_plugin = $('#plugin_list').val();
863 plugs.unloadPlugin(selected_plugin);
864 enumPlugins();
865 });
866
867 $('#kiwi .txtpluginfile').focus();
868
869 });
870
871 $('#kiwi .plugins .reload_css').click(function () {
872 var links = document.getElementsByTagName("link"),
873 i;
874 for (i = 0; i < links.length; i++) {
875 if (links[i].rel === "stylesheet") {
876 if (links[i].href.indexOf("?") === -1) {
877 links[i].href += "?";
878 }
879 links[i].href += "x";
880 }
881 }
882 });
883
884
885 $('#kiwi .about .about_close').click(function () {
886 $('#kiwi .about').css('display', 'none');
887 });
888
889
890 $('#kiwi .poweredby').click(function () {
891 $('#kiwi .about').css('display', 'block');
892 });
893
894 },
895
896
897 showChangeNick: function () {
898 $('#kiwi').append($('#tmpl_change_nick').tmpl({}));
899
900 $('#kiwi .form_newnick').submit(function () {
901 front.run('/NICK ' + $('#kiwi .txtnewnick').val());
902 $('#kiwi .newnick').remove();
903 return false;
904 });
905
906 $('#kiwi .txtnewnick').keypress(function (ev) {
907 if (!this.first_press) {
908 this.first_press = true;
909 return false;
910 }
911 });
912
913 $('#kiwi .txtnewnick').keydown(function (ev) {
914 if (ev.which === 27) { // ESC
915 $('#kiwi_msginput').focus();
916 $('#kiwi .newnick').remove();
917 }
918 });
919
920 $('#kiwi .cancelnewnick').click(function () {
921 $('#kiwi .newnick').remove();
922 });
923
924 $('#kiwi .txtnewnick').focus();
925 },
926
927
928 tabviewExists: function (name) {
929 return (typeof front.tabviews[name.toLowerCase()] !== 'undefined');
930 },
931
932 tabviewAdd: function (v_name) {
933 /*global Tabview */
934 var re, htmlsafe_name, tmp_divname, tmp_userlistname, tmp_tabname, userlist_enabled = true;
935
936 if (v_name.charAt(0) === gateway.channel_prefix) {
937 re = new RegExp(gateway.channel_prefix, "g");
938 htmlsafe_name = v_name.replace(re, 'pre');
939 htmlsafe_name = "chan_" + htmlsafe_name;
940 } else {
941 htmlsafe_name = 'query_' + v_name;
942 userlist_enabled = false;
943 }
944
945 tmp_divname = 'kiwi_window_' + htmlsafe_name;
946 tmp_userlistname = 'kiwi_userlist_' + htmlsafe_name;
947 tmp_tabname = 'kiwi_tab_' + htmlsafe_name;
948
949 if (!front.tabviewExists(v_name)) {
950 $('#kiwi .windows .scroller').append('<div id="' + tmp_divname + '" class="messages"></div>');
951 $('#kiwi .userlist').append('<ul id="' + tmp_userlistname + '"></ul>');
952 $('#kiwi .windowlist ul').append('<li id="' + tmp_tabname + '" onclick="front.tabviews[\'' + v_name.toLowerCase() + '\'].show();">' + v_name + '</li>');
953 }
954 //$('#kiwi .windowlist ul .window_'+v_name).click(function(){ front.windowShow(v_name); });
955 //front.windowShow(v_name);
956
957 front.tabviews[v_name.toLowerCase()] = new Tabview();
958 front.tabviews[v_name.toLowerCase()].name = v_name;
959 front.tabviews[v_name.toLowerCase()].div = $('#' + tmp_divname);
960 front.tabviews[v_name.toLowerCase()].userlist = $('#' + tmp_userlistname);
961 front.tabviews[v_name.toLowerCase()].tab = $('#' + tmp_tabname);
962 if (!userlist_enabled) {
963 front.tabviews[v_name.toLowerCase()].userlist_width = 0;
964 }
965 front.tabviews[v_name.toLowerCase()].show();
966
967 if (typeof registerTouches === "function") {
968 //alert("Registering touch interface");
969 //registerTouches($('#'+tmp_divname));
970 registerTouches(document.getElementById(tmp_divname));
971 }
972 /*
973 front.tabviews[v_name.toLowerCase()].userlist.click(function(){
974 alert($(this).attr('id'));
975 });
976 */
977
978 front.doLayoutSize();
979 },
980
981
982 userClick: function (item) {
983 var li = $(item).parent();
984
985 // Remove any existing userboxes
986 $('#kiwi .userbox').remove();
987
988 if ($(li).data('userbox') === item) {
989 $(li).removeData('userbox');
990 } else {
991 $('#tmpl_user_box').tmpl({nick: front.nickStripPrefix($(item).text())}).appendTo(li);
992
993 $('#kiwi .userbox .userbox_query').click(function (ev) {
994 var nick = $('#kiwi .userbox_nick').val();
995 front.run('/query ' + nick);
996 });
997
998 $('#kiwi .userbox .userbox_whois').click(function (ev) {
999 var nick = $('#kiwi .userbox_nick').val();
1000 front.run('/whois ' + nick);
1001 });
1002 $(li).data('userbox', item);
1003 }
1004 },
1005
1006
1007 sync: function () {
1008 gateway.sync();
1009 },
1010
1011 onSync: function (e, data) {
1012 // Set any settings
1013 if (data.nick !== undefined) {
1014 gateway.nick = data.nick;
1015 }
1016
1017 // Add the tabviews
1018 if (data.tabviews !== undefined) {
1019 $.each(data.tabviews, function (i, tab) {
1020 if (!front.tabviewExists(tab.name)) {
1021 front.tabviewAdd(gateway.channel_prefix + tab.name);
1022
1023 if (tab.userlist !== undefined) {
1024 front.onUserList({'channel': gateway.channel_prefix + tab.name, 'users': tab.userlist});
1025 }
1026 }
1027 });
1028 }
1029
1030 front.doLayout();
1031 },
1032
1033
1034 setTopicText: function (new_topic) {
1035 front.original_topic = new_topic;
1036 $('#kiwi .cur_topic .topic').text(new_topic);
1037 front.doLayoutSize();
1038 },
1039
1040
1041
1042
1043
1044
1045
1046 nickStripPrefix: function (nick) {
1047 var tmp = nick, i, prefix;
1048
1049 prefix = tmp.charAt(0);
1050 for (i in gateway.user_prefixes) {
1051 if (gateway.user_prefixes[i].symbol === prefix) {
1052 return tmp.substring(1);
1053 }
1054 }
1055
1056 return tmp;
1057 },
1058
1059 nickGetPrefix: function (nick) {
1060 var tmp = nick, i, prefix;
1061
1062 prefix = tmp.charAt(0);
1063 for (i in gateway.user_prefixes) {
1064 if (gateway.user_prefixes[i].symbol === prefix) {
1065 return prefix;
1066 }
1067 }
1068
1069 return '';
1070 },
1071
1072 isChannel: function (name) {
1073 var prefix, is_chan;
1074 prefix = name.charAt(0);
1075 if (gateway.channel_prefix.indexOf(prefix) > -1) {
1076 is_chan = true;
1077 } else {
1078 is_chan = false;
1079 }
1080
1081 return is_chan;
1082 },
1083
1084 tabviewsNext: function () {
1085 var wl = $('#kiwi .windowlist ul'),
1086 next_left = parseInt(wl.css('text-indent').replace('px', ''), 10) + 170;
1087 wl.css('text-indent', next_left);
1088 },
1089
1090 tabviewsPrevious: function () {
1091 var wl = $('#kiwi .windowlist ul'),
1092 next_left = parseInt(wl.css('text-indent').replace('px', ''), 10) - 170;
1093 wl.css('text-indent', next_left);
1094 },
1095
1096 windowsNext: function () {
1097 var tab, next;
1098 next = false;
1099 for (tab in front.tabviews) {
1100 if (!next) {
1101 if (front.tabviews[tab] === front.cur_channel) {
1102 next = true;
1103 continue;
1104 }
1105 } else {
1106 front.tabviews[tab].show();
1107 return;
1108 }
1109 }
1110 },
1111
1112 windowsPrevious: function () {
1113 var tab, prev_tab, next;
1114 next = false;
1115 for (tab in front.tabviews) {
1116 if (front.tabviews[tab] === front.cur_channel) {
1117 if (prev_tab) {
1118 prev_tab.show();
1119 }
1120 return;
1121 }
1122 prev_tab = front.tabviews[tab];
1123 }
1124 },
1125
1126 windowsShow: function (num) {
1127 num = parseInt(num, 10);
1128 console.log('Showing window ' + num.toString());
1129 var i = 0, tab;
1130 for (tab in front.tabviews) {
1131 if (i === num) {
1132 front.tabviews[tab].show();
1133 return;
1134 }
1135 i++;
1136 }
1137 },
1138
1139
1140
1141 barsShow: function () {
1142 $('#kiwi .toolbars').slideDown();
1143 $('#kiwi .control').slideDown();
1144 },
1145
1146 barsHide: function () {
1147 $('#kiwi .toolbars').slideUp();
1148 $('#kiwi .control').slideUp();
1149 }
1150 };
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167 /*
1168 * MISC VIEW
1169 */
1170
1171 var Utilityview = function (name) {
1172 var rand_name = randomString(15),
1173 tmp_divname = 'kiwi_window_' + rand_name,
1174 tmp_userlistname = 'kiwi_userlist_' + rand_name,
1175 tmp_tabname = 'kiwi_tab_' + rand_name;
1176
1177 this.name = rand_name;
1178 this.title = name;
1179 this.topic = ' ';
1180
1181 $('#kiwi .windows .scroller').append('<div id="' + tmp_divname + '" class="messages"></div>');
1182
1183 this.tab = $('<li id="' + tmp_tabname + '">' + this.title + '</li>');
1184 this.tab.click(function () {
1185 front.utilityviews[rand_name.toLowerCase()].show();
1186 });
1187 $('#kiwi .utilityviewlist ul').append(this.tab);
1188
1189 this.div = $('#' + tmp_divname);
1190 this.div.css('overflow', 'hidden');
1191
1192 front.utilityviews[rand_name.toLowerCase()] = this;
1193 };
1194
1195 Utilityview.prototype.name = null;
1196 Utilityview.prototype.title = null;
1197 Utilityview.prototype.div = null;
1198 Utilityview.prototype.tab = null;
1199 Utilityview.prototype.topic = ' ';
1200 Utilityview.prototype.show = function () {
1201 $('#kiwi .messages').removeClass("active");
1202 $('#kiwi .userlist ul').removeClass("active");
1203 $('#kiwi .toolbars ul li').removeClass("active");
1204
1205 $('#windows').css('overflow-y', 'hidden');
1206 $('#windows').css('right', 0);
1207 // Activate this tab!
1208 this.div.addClass('active');
1209 this.tab.addClass('active');
1210
1211 this.addPartImage();
1212
1213 front.setTopicText(this.topic);
1214 front.cur_channel = this;
1215
1216 // If we're using fancy scrolling, refresh it
1217 if (touch_scroll) {
1218 touch_scroll.refresh();
1219 }
1220 };
1221
1222 Utilityview.prototype.close = function () {
1223 this.div.remove();
1224 this.tab.remove();
1225
1226 if (front.cur_channel === this) {
1227 front.tabviews.server.show();
1228 }
1229 delete front.utilityviews[this.name.toLowerCase()];
1230 };
1231
1232 Utilityview.prototype.addPartImage = function () {
1233 this.clearPartImage();
1234
1235 // We can't close this tab, so don't have the close image
1236 if (this.name === 'server') {
1237 return;
1238 }
1239
1240 var del_html = '<img src="/img/redcross.png" class="tab_part" />';
1241 this.tab.append(del_html);
1242
1243 $('.tab_part', this.tab).click(function () {
1244 if (front.cur_channel.name !== 'server') {
1245 front.cur_channel.close();
1246 }
1247 });
1248 };
1249
1250 Utilityview.prototype.clearPartImage = function () {
1251 $('#kiwi .toolbars .tab_part').remove();
1252 };
1253
1254
1255
1256
1257
1258 /*
1259 *
1260 * TABVIEWS
1261 *
1262 */
1263
1264
1265 var Tabview = function () {};
1266 Tabview.prototype.name = null;
1267 Tabview.prototype.div = null;
1268 Tabview.prototype.userlist = null;
1269 Tabview.prototype.userlist_width = 100; // 0 for disabled
1270 Tabview.prototype.tab = null;
1271 Tabview.prototype.topic = "";
1272 Tabview.prototype.safe_to_close = false; // If we have been kicked/banned/etc from this channel, don't wait for a part message
1273
1274 Tabview.prototype.show = function () {
1275 var w, u;
1276
1277 $('#kiwi .messages').removeClass("active");
1278 $('#kiwi .userlist ul').removeClass("active");
1279 $('#kiwi .toolbars ul li').removeClass("active");
1280
1281 w = $('#windows');
1282 u = $('#kiwi .userlist');
1283
1284 w.css('overflow-y', 'scroll');
1285
1286 // Set the window size accordingly
1287 if (this.userlist_width > 0) {
1288 u.width(this.userlist_width);
1289 w.css('right', u.outerWidth(true));
1290 } else {
1291 w.css('right', 0);
1292 }
1293
1294 // Activate this tab!
1295 this.div.addClass('active');
1296 if (this.userlist_width > 0) {
1297 this.userlist.addClass('active');
1298 }
1299 this.tab.addClass('active');
1300
1301 // Add the part image to the tab
1302 this.addPartImage();
1303
1304 this.clearHighlight();
1305 front.setTopicText(this.topic);
1306 front.cur_channel = this;
1307
1308 // If we're using fancy scrolling, refresh it
1309 if (touch_scroll) {
1310 touch_scroll.refresh();
1311 }
1312
1313 this.scrollBottom();
1314 if (!touchscreen) {
1315 $('#kiwi_msginput').focus();
1316 }
1317 };
1318
1319 Tabview.prototype.close = function () {
1320 this.div.remove();
1321 this.userlist.remove();
1322 this.tab.remove();
1323
1324 if (front.cur_channel === this) {
1325 front.tabviews.server.show();
1326 }
1327 delete front.tabviews[this.name.toLowerCase()];
1328 };
1329
1330 Tabview.prototype.addPartImage = function () {
1331 this.clearPartImage();
1332
1333 // We can't close this tab, so don't have the close image
1334 if (this.name === 'server') {
1335 return;
1336 }
1337
1338 var del_html = '<img src="/img/redcross.png" class="tab_part" />';
1339 this.tab.append(del_html);
1340
1341 $('.tab_part', this.tab).click(function () {
1342 if (front.isChannel($(this).parent().text())) {
1343 front.run("/part");
1344 } else {
1345 // Make sure we don't close the server tab
1346 if (front.cur_channel.name !== 'server') {
1347 front.cur_channel.close();
1348 }
1349 }
1350 });
1351 };
1352
1353 Tabview.prototype.clearPartImage = function () {
1354 $('#kiwi .toolbars .tab_part').remove();
1355 };
1356
1357 Tabview.prototype.addMsg = function (time, nick, msg, type, style) {
1358 var self, tmp, plugin_ret, i, d, re, line_msg, next;
1359
1360 self = this;
1361
1362 tmp = {msg: msg, time: time, nick: nick, tabview: this.name};
1363 tmp = plugs.run('addmsg', tmp);
1364 if (!tmp) {
1365 return;
1366 }
1367
1368
1369 msg = tmp.msg;
1370 time = tmp.time;
1371 nick = tmp.nick;
1372
1373 if (time === null) {
1374 d = new Date();
1375 time = d.getHours().toString().lpad(2, "0") + ":" + d.getMinutes().toString().lpad(2, "0") + ":" + d.getSeconds().toString().lpad(2, "0");
1376 }
1377
1378 // The CSS class (action, topic, notice, etc)
1379 if (typeof type !== "string") {
1380 type = '';
1381 }
1382
1383 // Make sure we don't have NaN or something
1384 if (typeof msg !== "string") {
1385 msg = '';
1386 }
1387
1388 // Text formatting
1389 // bold
1390 if (msg.indexOf(String.fromCharCode(2)) !== -1) {
1391 next = '<b>';
1392 while (msg.indexOf(String.fromCharCode(2)) !== -1) {
1393 msg = msg.replace(String.fromCharCode(2), next);
1394 next = (next === '<b>') ? '</b>' : '<b>';
1395 }
1396 if (next === '</b>') {
1397 msg = msg + '</b>';
1398 }
1399 }
1400
1401 // Wierd thing noticed by Dux0r on the irc.esper.net server
1402 if (typeof msg !== "string") {
1403 msg = '';
1404 }
1405
1406 // underline
1407 if (msg.indexOf(String.fromCharCode(31)) !== -1) {
1408 next = '<u>';
1409 while (msg.indexOf(String.fromCharCode(31)) !== -1) {
1410 msg = msg.replace(String.fromCharCode(31), next);
1411 next = (next === '<u>') ? '</u>' : '<u>';
1412 }
1413 if (next === '</u>') {
1414 msg = msg + '</u>';
1415 }
1416 }
1417
1418 // Make the channels clickable
1419 re = new RegExp('\\B(' + gateway.channel_prefix + '[^ ,.\\007]+)', 'g');
1420 msg = msg.replace(re, function (match) {
1421 return '<a class="chan">' + match + '</a>';
1422 });
1423
1424 // Build up and add the line
1425 line_msg = $('<div class="msg ' + type + '"><div class="time">' + time + '</div><div class="nick">' + nick + '</div><div class="text" style="' + style + '">' + msg + ' </div></div>');
1426 this.div.append(line_msg);
1427
1428 if (!touchscreen) {
1429 this.scrollBottom();
1430 } else {
1431 touch_scroll.refresh();
1432 //console.log(this.div.attr("scrollHeight") +" - "+ $('#windows').height());
1433 this.scrollBottom();
1434 //if(this.div.attr("scrollHeight") > $('#windows').height()){
1435 // touch_scroll.scrollTo(0, this.div.height());
1436 //}
1437 }
1438 };
1439
1440 Tabview.prototype.scrollBottom = function () {
1441 var w = $('#windows');
1442 w[0].scrollTop = w[0].scrollHeight;
1443 };
1444
1445 Tabview.prototype.changeNick = function (newNick, oldNick) {
1446 this.userlist.children().each(function () {
1447 var item = $('a.nick', this);
1448 if (front.nickStripPrefix(item.text()) === oldNick) {
1449 item.text(front.nickGetPrefix(item.text()) + newNick);
1450 document.temp_chan = 1;
1451 }
1452 });
1453
1454 if (typeof document.temp_chan !== "undefined") {
1455 this.addMsg(null, ' ', '=== ' + oldNick + ' is now known as ' + newNick, 'action');
1456 delete document.temp_chan;
1457 this.userlistSort();
1458 }
1459 };
1460
1461 Tabview.prototype.userlistSort = function () {
1462 var ul = this.userlist,
1463 listitems = ul.children('li').get(),
1464 prefix;
1465 listitems.sort(function (a, b) {
1466 var compA = $(a).text().toUpperCase(),
1467 compB = $(b).text().toUpperCase(),
1468 i;
1469
1470 // Sort by prefixes first
1471 for (i in gateway.user_prefixes) {
1472 prefix = gateway.user_prefixes[i].symbol;
1473
1474 if (compA.charAt(0) === prefix && compB.charAt(0) === prefix) {
1475 // Both have the same prefix, string compare time
1476 return 0;
1477 }
1478
1479 if (compA.charAt(0) === prefix && compB.charAt(0) !== prefix) {
1480 return -1;
1481 }
1482
1483 if (compA.charAt(0) !== prefix && compB.charAt(0) === prefix) {
1484 return 1;
1485 }
1486 }
1487
1488 // No prefixes, compare by string
1489 return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
1490 });
1491 $.each(listitems, function (idx, itm) { ul.append(itm); });
1492 };
1493
1494 Tabview.prototype.highlight = function () {
1495 this.tab.addClass('highlight');
1496 };
1497 Tabview.prototype.activity = function () {
1498 this.tab.addClass('activity');
1499 };
1500 Tabview.prototype.clearHighlight = function () {
1501 this.tab.removeClass('highlight');
1502 this.tab.removeClass('activity');
1503 };
1504 Tabview.prototype.changeTopic = function (new_topic) {
1505 this.topic = new_topic;
1506 this.addMsg(null, ' ', '=== Topic for ' + this.name + ' is: ' + new_topic, 'topic');
1507 if (front.cur_channel.name === this.name) {
1508 front.setTopicText(new_topic);
1509 }
1510 };
1511
1512
1513
1514
1515
1516 var Box = function (classname) {
1517 this.id = randomString(10);
1518 var tmp = $('<div id="' + this.id + '" class="box ' + classname + '"><div class="boxarea"></div></div>');
1519 $('#kiwi').append(tmp);
1520 this.box = $('#' + this.id);
1521 this.content = $('#' + this.id + ' .boxarea');
1522
1523 this.box.draggable({ stack: ".box" });
1524 this.content.click(function () {});
1525 //this.box.click(function(){ $(this)..css });
1526 };
1527 Box.prototype.create = function (name, classname) {
1528
1529 };
1530 Box.prototype.id = null;
1531 Box.prototype.box = null;
1532 Box.prototype.content = null;
1533 Box.prototype.destroy = function () {
1534 var name;
1535 this.box.remove();
1536 for (name in front.boxes) {
1537 if (front.boxes[name].id === this.id) {
1538 delete front.boxes[name];
1539 }
1540 }
1541 };
1542 Box.prototype.height = function () {
1543 return this.box.height();
1544 };