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