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