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