Fixed notices
[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 if (front.cur_channel.name !== 'server') {
320 gateway.msg(front.cur_channel.name, msg);
321 var d = new Date();
322 d = d.getHours() + ":" + d.getMinutes();
323 //front.addMsg(d, gateway.nick, msg);
324 front.cur_channel.addMsg(null, gateway.nick, msg);
325 }
326 }
327 },
328
329
330 onMsg: function (e, data) {
331 var destination;
332 // Is this message from a user?
333 if (data.channel === gateway.nick) {
334 destination = data.nick.toLowerCase();
335 } else {
336 destination = data.channel.toLowerCase();
337 }
338
339 if (!front.tabviewExists(destination)) {
340 front.tabviewAdd(destination);
341 }
342 front.tabviews[destination].addMsg(null, data.nick, data.msg);
343 },
344
345 onDebug: function (e, data) {
346 if (!front.tabviewExists('kiwi_debug')) {
347 front.tabviewAdd('kiwi_debug');
348 }
349 front.tabviews.kiwi_debug.addMsg(null, ' ', data.msg);
350 },
351
352 onAction: function (e, data) {
353 var destination;
354 // Is this message from a user?
355 if (data.channel === gateway.nick) {
356 destination = data.nick;
357 } else {
358 destination = data.channel;
359 }
360
361 if (!front.tabviewExists(destination)) {
362 front.tabviewAdd(destination);
363 }
364 front.tabviews[destination.toLowerCase()].addMsg(null, ' ', '* ' + data.nick + ' ' + data.msg, 'action', 'color:#555;');
365 },
366
367 onTopic: function (e, data) {
368 if (front.tabviewExists(data.channel)) {
369 front.tabviews[data.channel.toLowerCase()].changeTopic(data.topic);
370 }
371 },
372
373 onNotice: function (e, data) {
374 var nick = (data.nick === undefined || data.nick === '') ? '' : '[' + data.nick + ']';
375 if (data.channel !== undefined) {
376 if (front.tabviewExists(data.channel)) {
377 front.tabviews[data.channel.toLowerCase()].addMsg(null, nick, data.msg, 'notice');
378 } else {
379 front.tabviews.server.addMsg(null, nick, data.msg, 'notice');
380 }
381 } else {
382 front.tabviews.server.addMsg(null, nick, data.msg, 'notice');
383 }
384 },
385
386 onCTCPRequest: function (e, data) {
387 var msg = data.msg.split(" ", 2);
388 switch (msg[0]) {
389 case 'PING':
390 if(typeof msg[1] === 'undefined') msg[1] = '';
391 gateway.notice(data.nick, '\001PING ' + msg[1] + '\001');
392 break;
393 case 'TIME':
394 gateway.notice(data.nick, '\001TIME ' + (new Date()).toLocaleString() + '\001');
395 break;
396 }
397 front.tabviews.server.addMsg(null, 'CTCP ['+data.nick+']', data.msg, 'ctcp');
398 },
399
400 onCTCPResponse: function(e, data) {
401 },
402
403 onConnect: function (e, data) {
404 if (data.connected) {
405 front.tabviews.server.addMsg(null, ' ', '=== Connected OK :)', 'status');
406 if (typeof init_data.channel === "string") {
407 front.joinChannel(init_data.channel);
408 }
409 } else {
410 front.tabviews.server.addMsg(null, ' ', '=== Failed to connect :(', 'status');
411 }
412 },
413 onDisconnect: function(e, data){
414 var tab;
415 for(tab in front.tabviews){
416 front.tabviews[tab].addMsg(null, '', 'Disconnected from server!', 'error')
417 }
418 },
419 onOptions: function (e, data) {
420 if (typeof gateway.network_name === "string" && gateway.network_name !== "") {
421 front.tabviews.server.tab.text(gateway.network_name);
422 }
423 },
424 onMOTD: function (e, data) {
425 front.tabviews.server.addMsg(null, data.server, data.msg, 'motd');
426 },
427 onWhois: function (e, data) {
428 var d;
429 if (data.msg) {
430 front.cur_channel.addMsg(null, data.nick, data.msg, 'whois');
431 } else if (data.logon) {
432 d = new Date();
433 d.setTime(data.logon * 1000);
434 d = d.toLocaleString();
435 front.cur_channel.addMsg(null, data.nick, 'idle for ' + data.idle + ' second' + ((data.idle !== 1) ? 's' : '') + ', signed on ' + d, 'whois');
436 } else {
437 front.cur_channel.addMsg(null, data.nick, 'idle for ' + data.idle + ' seconds', 'whois');
438 }
439 },
440 onUserList: function (e, data) {
441 if (front.tabviews[data.channel.toLowerCase()] === undefined) {
442 return;
443 }
444 var ul = front.tabviews[data.channel.toLowerCase()].userlist;
445
446 if (!document.userlist_updating) {
447 document.userlist_updating = true;
448 ul.empty();
449 }
450
451 $.each(data.users, function (i, item) {
452 var nick = i; //i.match(/^.+!/g);
453 var mode = item;
454 $('<li><a class="nick" onclick="front.userClick(this);">' + mode + nick + '</a></li>').appendTo(ul);
455 });
456
457 front.tabviews[data.channel.toLowerCase()].userlistSort();
458 },
459 onUserListEnd: function (e, data) {
460 document.userlist_updating = false;
461 },
462
463 onJoin: function (e, data) {
464 if (!front.tabviewExists(data.channel)) {
465 front.tabviewAdd(data.channel.toLowerCase());
466 }
467
468 if (data.nick === gateway.nick) {
469 return; // Not needed as it's already in nicklist
470 }
471 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '--> ' + data.nick + ' has joined', 'action', 'color:#009900;');
472 $('<li><a class="nick" onclick="front.userClick(this);">' + data.nick + '</a></li>').appendTo(front.tabviews[data.channel.toLowerCase()].userlist);
473 front.tabviews[data.channel.toLowerCase()].userlistSort();
474 },
475 onPart: function (e, data) {
476 if (front.tabviewExists(data.channel)) {
477 // If this is us, close the tabview
478 if (data.nick === gateway.nick) {
479 front.tabviews[data.channel.toLowerCase()].close();
480 front.tabviews.server.show();
481 return;
482 }
483
484 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '<-- ' + data.nick + ' has left (' + data.message + ')', 'action', 'color:#990000;');
485 front.tabviews[data.channel.toLowerCase()].userlist.children().each(function () {
486 if ($(this).text() === data.nick) {
487 $(this).remove();
488 }
489 });
490 }
491 },
492 onKick: function (e, data) {
493 if (front.tabviewExists(data.channel)) {
494 // If this is us, close the tabview
495 if (data.kicked === gateway.nick) {
496 //front.tabviews[data.channel.toLowerCase()].close();
497 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '=== You have been kicked from ' + data.channel + '. ' + data.message, 'status');
498 front.tabviews[data.channel.toLowerCase()].safe_to_close = true;
499 $('li', front.tabviews[data.channel.toLowerCase()].userlist).remove();
500 return;
501 }
502
503 front.tabviews[data.channel.toLowerCase()].addMsg(null, ' ', '<-- ' + data.kicked + ' kicked by ' + data.nick + '(' + data.message + ')', 'action', 'color:#990000;');
504 front.tabviews[data.channel.toLowerCase()].userlist.children().each(function () {
505 if ($(this).text() === data.nick) {
506 $(this).remove();
507 }
508 });
509 }
510 },
511 onNick: function (e, data) {
512 if (data.nick === gateway.nick) {
513 gateway.nick = data.newnick;
514 front.doLayout();
515 }
516
517 $.each(front.tabviews, function (i, item) {
518 $.each(front.tabviews, function (i, item) {
519 item.changeNick(data.newnick, data.nick);
520 });
521 });
522 },
523 onQuit: function (e, data) {
524 $.each(front.tabviews, function (i, item) {
525 $.each(front.tabviews, function (i, item) {
526 item.userlist.children().each(function () {
527 if ($(this).text() === data.nick) {
528 $(this).remove();
529 item.addMsg(null, ' ', '<-- ' + data.nick + ' has quit (' + data.message + ')', 'action', 'color:#990000;');
530 }
531 });
532 });
533 });
534 },
535 onChannelRedirect: function (e, data) {
536 front.tabviews[data.from.toLowerCase()].close();
537 front.tabviewAdd(data.to.toLowerCase());
538 front.tabviews[data.to.toLowerCase()].addMsg(null, ' ', '=== Redirected from ' + data.from, 'action');
539 },
540
541 onIRCError: function (e, data) {
542 var t_view;
543 if (data.channel !== undefined && front.tabviewExists(data.channel)) {
544 t_view = data.channel;
545 } else {
546 t_view = 'server';
547 }
548
549 switch(data.error) {
550 case 'banned_from_channel':
551 front.tabviews[t_view].addMsg(null, ' ', '=== You are banned from ' + data.channel + '. ' + data.reason, 'status');
552 if (t_view !== 'server' ) front.tabviews[t_view].safe_to_close = true;
553 break;
554 case 'bad_channel_key':
555 front.tabviews[t_view].addMsg(null, ' ', '=== Bad channel key for ' + data.channel, 'status');
556 if (t_view !== 'server' ) front.tabviews[t_view].safe_to_close = true;
557 break;
558 case 'invite_only_channel':
559 front.tabviews[t_view].addMsg(null, ' ', '=== ' + data.channel + ' is invite only.', 'status');
560 if (t_view !== 'server' ) front.tabviews[t_view].safe_to_close = true;
561 break;
562 case 'channel_is_full':
563 front.tabviews[t_view].addMsg(null, ' ', '=== ' + data.channel + ' is full.', 'status');
564 if (t_view !== 'server' ) front.tabviews[t_view].safe_to_close = true;
565 break;
566 case 'chanop_privs_needed':
567 front.tabviews[data.channel].addMsg(null, ' ', '=== ' + data.reason, 'status');
568 break;
569 case 'no_such_nick':
570 front.tabviews.server.addMsg(null, ' ', '=== ' + data.nick + ': ' + data.reason, 'status');
571 break;
572 default:
573 // We don't know what data contains, so don't do anything with it.
574 //front.tabviews.server.addMsg(null, ' ', '=== ' + data, 'status');
575 }
576 },
577
578 registerKeys: function () {
579 $('#kiwi_msginput').bind('keydown', function (e) {
580 var windows = $('#windows');
581 //var meta = e.altKey;
582 var meta = e.ctrlKey;
583
584 switch (true) {
585 case (e.which >= 48) && (e.which <= 57):
586 if(meta){
587 var num = e.which - 48;
588 if(num === 0) num = 10;
589 num = num - 1;
590 front.windowsShow(num);
591 return false;
592 }
593 break;
594 case e.which === 27: // escape
595 return false;
596 case e.which === 13: // return
597 var msg = $('#kiwi_msginput').val();
598 msg = msg.trim();
599
600 front.buffer.push(msg);
601 front.buffer_pos = front.buffer.length;
602
603 front.run(msg);
604 $('#kiwi_msginput').val('');
605
606 break;
607 case e.which === 33: // page up
608 console.log("page up");
609 windows[0].scrollTop = windows[0].scrollTop - windows.height();
610 return false;
611 break;
612 case e.which === 34: // page down
613 windows[0].scrollTop = windows[0].scrollTop + windows.height();
614 return false;
615 break;
616 case e.which === 37: // left
617 if(meta){
618 front.windowsPrevious();
619 return false;
620 }
621 break;
622 case e.which === 38: // up
623 if (front.buffer_pos > 0) {
624 front.buffer_pos--;
625 $('#kiwi_msginput').val(front.buffer[front.buffer_pos]);
626 }
627 break;
628 case e.which === 39: // right
629 if(meta){
630 front.windowsNext();
631 return false;
632 }
633 break;
634 case e.which === 40: // down
635 if (front.buffer_pos < front.buffer.length) {
636 front.buffer_pos++;
637 $('#kiwi_msginput').val(front.buffer[front.buffer_pos]);
638 }
639 break;
640
641 case e.which === 9: // tab
642 // Get possible autocompletions
643 var data = [];
644 front.cur_channel.userlist.children().each(function () {
645 nick = front.nickStripPrefix($('a.nick', this).text());
646 data.push(nick);
647 });
648
649 // Do the autocomplete
650 if (this.value.length === this.selectionStart && this.value.length === this.selectionEnd) {
651 var candidates = [];
652
653 var word_pos = this.value.lastIndexOf(' ');
654 var word = "";
655 if (word_pos === -1) {
656 word = this.value;
657 } else {
658 word = this.value.substr(word_pos);
659 }
660 word = word.trim();
661
662 // filter data to find only strings that start with existing value
663 for (i = 0; i < data.length; i++) {
664 if (data[i].indexOf(word) === 0 && data[i].length > word.length) {
665 candidates.push(data[i]);
666 }
667 }
668
669 if (candidates.length > 0) {
670 // some candidates for autocompletion are found
671 this.value = this.value.substring(0, word_pos) + ' ' + candidates[0] + ': ';
672 this.selectionStart = this.value.length;
673 }
674 }
675 return false;
676 }
677 });
678
679
680 $('#kiwi .control .msginput .nick').click(function () {
681 front.showChangeNick();
682 });
683
684
685
686
687
688 $('#kiwi .plugins .load_plugin_file').click(function () {
689 if (typeof front.boxes.plugins !== "undefined") {
690 return;
691 }
692
693 front.boxes.plugins = new box("plugin_file");
694 $('#tmpl_plugins').tmpl({}).appendTo(front.boxes.plugins.content);
695 front.boxes.plugins.box.css('top', -(front.boxes.plugins.height + 40));
696
697 // Populate the plugin list..
698 var lst = $('#plugin_list');
699 lst.find('option').remove();
700 var j;
701 for (j in plugins.privmsg) {
702 var txt = plugins.privmsg[j].name;
703 lst.append('<option value="' + txt + '">' + txt + '</option>');
704 }
705
706 // Event bindings
707 $('#kiwi .plugin_file').submit(function () {
708 $.getJSON($('.txtpluginfile').val(), function (data) {
709 var plg = {};
710 plg.name = data.name;
711 eval("plg.onprivmsg = " + data.onprivmsg);
712 eval("plg.onload = " + data.onload);
713 eval("plg.onunload = " + data.onunload);
714 plugins.privmsg.push(plg);
715
716 if (plg.onload instanceof Function) {
717 plg.onload();
718 }
719 });
720 return false;
721 });
722 $('#kiwi .cancelpluginfile').click(function () {
723 front.boxes.plugins.destroy();
724 });
725
726 $('#kiwi #plugins_list_unload').click(function () {
727 var selected_plugin = $('#plugin_list').val();
728 console.log("removing plugin: "+selected_plugin);
729 for (var i in plugins.privmsg) {
730 if (plugins.privmsg[i].name === selected_plugin) {
731 if (plugins.privmsg[i].onunload instanceof Function)
732 plugins.privmsg[i].onunload();
733
734 delete plugins.privmsg[i];
735 }
736 }
737 });
738
739 $('#kiwi .txtpluginfile').focus();
740
741 });
742
743 $('#kiwi .plugins .reload_css').click(function () {
744 var links = document.getElementsByTagName("link");
745 for (var i=0; i < links.length; i++) {
746 if (links[i].rel === "stylesheet") {
747 if (links[i].href.indexOf("?")===-1) {
748 links[i].href += "?";
749 }
750 links[i].href += "x";
751 }
752 }
753 });
754
755
756 $('#kiwi .about .about_close').click(function () {
757 $('#kiwi .about').css('display', 'none');
758 });
759
760
761 $('#kiwi .poweredby').click(function () {
762 $('#kiwi .about').css('display', 'block');
763 });
764
765 },
766
767
768 showChangeNick: function(){
769 $('#kiwi').append($('#tmpl_change_nick').tmpl({}));
770
771 $('#kiwi .form_newnick').submit(function () {
772 front.run('/NICK ' + $('#kiwi .txtnewnick').val());
773 $('#kiwi .newnick').remove();
774 return false;
775 });
776
777 $('#kiwi .txtnewnick').keypress(function(ev){
778 if(!this.first_press) {
779 this.first_press=true;
780 return false;
781 }
782 });
783
784 $('#kiwi .txtnewnick').keydown(function(ev){
785 if(ev.which === 27){ // ESC
786 $('#kiwi_msginput').focus();
787 $('#kiwi .newnick').remove();
788 }
789 });
790
791 $('#kiwi .cancelnewnick').click(function () {
792 $('#kiwi .newnick').remove();
793 });
794
795 $('#kiwi .txtnewnick').focus();
796 },
797
798
799 tabviewExists: function (name) {
800 return !(front.tabviews[name.toLowerCase()] == undefined);
801 },
802
803 tabviewAdd: function (v_name) {
804 if (v_name.charAt(0) == gateway.channel_prefix) {
805 var re = new RegExp(gateway.channel_prefix,"g");
806 var htmlsafe_name = v_name.replace(re, 'pre');
807 htmlsafe_name = "chan_" + htmlsafe_name;
808 } else {
809 var htmlsafe_name = 'query_' + v_name;
810 }
811
812 var tmp_divname = 'kiwi_window_' + htmlsafe_name;
813 var tmp_userlistname = 'kiwi_userlist_' + htmlsafe_name;
814 var tmp_tabname = 'kiwi_tab_' + htmlsafe_name
815
816 if(!front.tabviewExists(v_name)){
817 $('#kiwi .windows .scroller').append('<div id="' + tmp_divname + '" class="messages"></div>');
818 $('#kiwi .userlist').append('<ul id="' + tmp_userlistname + '"></ul>');
819 $('#kiwi .windowlist ul').append('<li id="' + tmp_tabname + '" onclick="front.tabviews[\'' + v_name.toLowerCase() + '\'].show();">' + v_name + '</li>');
820 }
821 //$('#kiwi .windowlist ul .window_'+v_name).click(function(){ front.windowShow(v_name); });
822 //front.windowShow(v_name);
823
824 front.tabviews[v_name.toLowerCase()] = new tabview();
825 front.tabviews[v_name.toLowerCase()].name = v_name;
826 front.tabviews[v_name.toLowerCase()].div = $('#'+tmp_divname);
827 front.tabviews[v_name.toLowerCase()].userlist = $('#'+tmp_userlistname);
828 front.tabviews[v_name.toLowerCase()].tab = $('#'+tmp_tabname);
829 front.tabviews[v_name.toLowerCase()].show();
830
831 if (typeof registerTouches === "function") {
832 //alert("Registering touch interface");
833 //registerTouches($('#'+tmp_divname));
834 registerTouches(document.getElementById(tmp_divname));
835 }
836 /*
837 front.tabviews[v_name.toLowerCase()].userlist.click(function(){
838 alert($(this).attr('id'));
839 });
840 */
841
842 front.doLayoutSize();
843 },
844
845
846 userClick: function (item) {
847 // Remove any existing userboxes
848 $('#kiwi .userbox').remove();
849
850 var li = $(item).parent();
851 /*var html = '<div class="userbox">\
852 <input type="hidden" class="userbox_nick" value="' + front.nickStripPrefix($(item).text()) + '" />\
853 <a href="#" class="userbox_query">Message</a>\
854 <a href="#" class="userbox_whois">Info</a>\
855 </div>';
856 li.append(html);*/
857 $('#tmpl_user_box').tmpl({nick: front.nickStripPrefix($(item).text())}).appendTo(li);
858
859 $('#kiwi .userbox .userbox_query').click(function (ev) {
860 var nick = $('#kiwi .userbox_nick').val();
861 front.run('/query ' + nick);
862 });
863
864 $('#kiwi .userbox .userbox_whois').click(function (ev) {
865 var nick = $('#kiwi .userbox_nick').val();
866 front.run('/whois ' + nick);
867 });
868 },
869
870
871 sync: function () {
872 gateway.sync();
873 },
874
875 onSync: function (e, data) {
876 // Set any settings
877 if (data.nick != undefined) gateway.nick = data.nick;
878
879 // Add the tabviews
880 if (data.tabviews != undefined) {
881 $.each(data.tabviews, function (i, tab) {
882 if(!front.tabviewExists(tab.name)){
883 front.tabviewAdd(gateway.channel_prefix + tab.name);
884
885 if (tab.userlist !== undefined)
886 front.onUserList({'channel':gateway.channel_prefix + tab.name, 'users':tab.userlist});
887 }
888 });
889 }
890
891 front.doLayout();
892 },
893
894
895 setTopicText: function (new_topic) {
896 front.original_topic = new_topic;
897 $('#kiwi .cur_topic .topic').text(new_topic);
898 front.doLayoutSize();
899 },
900
901
902
903
904
905
906
907 nickStripPrefix: function (nick) {
908 var tmp = nick;
909
910 prefix = tmp.charAt(0);
911 for(var i in gateway.user_prefixes){
912 if(gateway.user_prefixes[i].symbol !== prefix) continue;
913 return tmp.substring(1);
914 }
915
916 return tmp;
917 },
918
919 nickGetPrefix: function (nick) {
920 var tmp = nick;
921
922 prefix = tmp.charAt(0);
923 for(var i in gateway.user_prefixes){
924 if(gateway.user_prefixes[i].symbol === prefix){
925 return prefix;
926 }
927 }
928
929 return '';
930 },
931
932 isChannel: function (name) {
933 prefix = name.charAt(0);
934 if (gateway.channel_prefix.indexOf(prefix) > -1) {
935 is_chan = true;
936 } else {
937 is_chan = false;
938 }
939
940 return is_chan;
941 },
942
943 tabviewsNext: function(){
944 var wl = $('#kiwi .windowlist ul');
945 var next_left = parseInt(wl.css('text-indent').replace('px', ''))+170;
946 wl.css('text-indent', next_left);
947 },
948
949 tabviewsPrevious: function(){
950 var wl = $('#kiwi .windowlist ul');
951 var next_left = parseInt(wl.css('text-indent').replace('px', ''))-170;
952 wl.css('text-indent', next_left);
953 },
954
955 windowsNext: function(){
956 var tab, next;
957 next = false;
958 for(tab in front.tabviews){
959 if(!next){
960 if(front.tabviews[tab] == front.cur_channel){
961 next = true;
962 continue;
963 }
964 } else {
965 front.tabviews[tab].show();
966 return;
967 }
968 };
969 },
970
971 windowsPrevious: function(){
972 var tab, prev_tab, next;
973 next = false;
974 for(tab in front.tabviews){
975 if(front.tabviews[tab] == front.cur_channel){
976 if(prev_tab) prev_tab.show();
977 return;
978 }
979 prev_tab = front.tabviews[tab];
980 };
981 },
982
983 windowsShow: function(num){
984 num = parseInt(num);
985 console.log('Showing window '+num.toString());
986 var i = 0, tab;
987 for(tab in front.tabviews){
988 if(i === num){
989 front.tabviews[tab].show();
990 return;
991 }
992 i++;
993 }
994 }
995 }
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013 /*
1014 *
1015 * TABVIEWS
1016 *
1017 */
1018
1019
1020 var tabview = function(){}
1021 tabview.prototype.name = null;
1022 tabview.prototype.div = null;
1023 tabview.prototype.userlist = null;
1024 tabview.prototype.tab = null;
1025 tabview.prototype.topic = "";
1026 tabview.prototype.safe_to_close = false; // If we have been kicked/banned/etc from this channel, don't wait for a part message
1027
1028 tabview.prototype.show = function(){
1029 $('#kiwi .messages').removeClass("active");
1030 $('#kiwi .userlist ul').removeClass("active");
1031 $('#kiwi .windowlist ul li').removeClass("active");
1032
1033 // Activate this tab!
1034 this.div.addClass('active');
1035 this.userlist.addClass('active');
1036 this.tab.addClass('active');
1037
1038 document.tmp = this.div;
1039 // Add the part image to the tab
1040 this.addPartImage();
1041
1042 this.clearHighlight();
1043 front.setTopicText(this.topic);
1044 front.cur_channel = this;
1045
1046 // If we're using fancy scrolling, refresh it
1047 if(touch_scroll) touch_scroll.refresh();
1048
1049 this.scrollBottom();
1050 if(!touchscreen) $('#kiwi_msginput').focus();
1051 }
1052
1053 tabview.prototype.close = function(){
1054 this.div.remove();
1055 this.userlist.remove();
1056 this.tab.remove();
1057
1058 if(front.cur_channel == this) front.tabviews['server'].show();
1059 delete front.tabviews[this.name.toLowerCase()];
1060 }
1061
1062 tabview.prototype.addPartImage = function(){
1063 this.clearPartImage();
1064
1065 // We can't close this tab, so don't have the close image
1066 if(this.name == 'server') return;
1067
1068 var del_html = '<img src="img/redcross.png" class="tab_part" />';
1069 this.tab.append(del_html);
1070
1071 $('.tab_part', this.tab).click(function(){
1072 if(front.isChannel($(this).parent().text())){
1073 front.run("/part");
1074 } else {
1075 // Make sure we don't close the server tab
1076 if(front.cur_channel.name != 'server') front.cur_channel.close();
1077 }
1078 });
1079 }
1080
1081 tabview.prototype.clearPartImage = function(){
1082 $('#kiwi .windowlist .tab_part').remove();
1083 }
1084
1085 tabview.prototype.addMsg = function(time, nick, msg, type, style){
1086 //if(nick.charAt(0) != "[" && nick != ""){
1087 // var html_nick = $('<div/>').text('<'+nick+'>').html();
1088 //} else {
1089 var html_nick = $('<div/>').text(nick).html();
1090 //}
1091
1092 var self = this;
1093
1094 var tmp = msg;
1095 var plugin_ret = '';
1096 for(var i in plugins.privmsg){
1097 if ((plugins.privmsg[i].onprivmsg instanceof Function)) {
1098 plugin_ret = '';
1099 try {
1100 plugin_ret = plugins.privmsg[i].onprivmsg(tmp, this.name);
1101
1102 // If this plugin has returned false, do not add this message
1103 if(plugin_ret === false) return;
1104 } catch (e){}
1105
1106 // If we actually have a string from the plugin, use it
1107 if(typeof plugin_ret == "string") tmp = plugin_ret;
1108 }
1109 }
1110 msg = tmp;
1111
1112 //var html_msg = $('<div/>').text(msg).html()+' '; // Add the space so the styling always has at least 1 character to go from
1113 if(time == null){
1114 var d = new Date();
1115 time = d.getHours().toString().lpad(2, "0") + ":" + d.getMinutes().toString().lpad(2, "0") + ":" + d.getSeconds().toString().lpad(2, "0");
1116 }
1117
1118 // The CSS class (action, topic, notice, etc)
1119 if(typeof type != "string") type = '';
1120
1121 // Make sure we don't have NaN or something
1122 if(typeof msg != "string") msg = '';
1123
1124 // Text formatting
1125 // bold
1126 if(msg.indexOf(String.fromCharCode(2))){
1127 next = '<b>';
1128 while(msg.indexOf(String.fromCharCode(2)) != -1){
1129 msg = msg.replace(String.fromCharCode(2), next);
1130 next = (next=='<b>') ? '</b>' : '<b>';
1131 }
1132 if(next == '</b>') msg =+ '</b>';
1133 }
1134
1135 // Wierd thing noticed by Dux0r on the irc.esper.net server
1136 if(typeof msg != "string") msg = '';
1137
1138 // underline
1139 if(msg.indexOf(String.fromCharCode(31))){
1140 next = '<u>';
1141 while(msg.indexOf(String.fromCharCode(31)) != -1){
1142 msg = msg.replace(String.fromCharCode(31), next);
1143 next = (next=='<u>') ? '</u>' : '<u>';
1144 }
1145 if(next == '</u>') msg =+ '</u>';
1146 }
1147
1148 var re = '\\B(' + gateway.channel_prefix + '[^ ,.\\007]+)';
1149 re = new RegExp(re, 'g');
1150
1151 msg = msg.replace(re, function(match) {
1152 return '<a class="chan" href="#">' + match + '</a>';
1153 });
1154
1155 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>');
1156 this.div.append(line_msg);
1157
1158 if(!touchscreen){
1159 this.scrollBottom();
1160 } else {
1161 touch_scroll.refresh();
1162 //console.log(this.div.attr("scrollHeight") +" - "+ $('#windows').height());
1163 this.scrollBottom();
1164 //if(this.div.attr("scrollHeight") > $('#windows').height()){
1165 // touch_scroll.scrollTo(0, this.div.height());
1166 //}
1167 }
1168 }
1169
1170 tabview.prototype.scrollBottom = function(){
1171 var w = $('#windows');
1172 w[0].scrollTop = w[0].scrollHeight;
1173 }
1174
1175 tabview.prototype.changeNick = function(newNick, oldNick){
1176 this.userlist.children().each(function(){
1177 var item = $('a.nick', this);
1178 if(front.nickStripPrefix(item.text()) == oldNick){
1179 item.text(front.nickGetPrefix(item.text())+newNick);
1180 document.temp_chan = 1;
1181 }
1182 });
1183
1184 if(typeof document.temp_chan != "undefined"){
1185 this.addMsg(null, ' ', '=== '+oldNick+' is now known as '+newNick, 'action');
1186 delete document.temp_chan;
1187 this.userlistSort();
1188 }
1189 }
1190
1191 tabview.prototype.userlistSort = function(){
1192 var ul = this.userlist;
1193 var listitems = ul.children('li').get();
1194 listitems.sort(function(a, b) {
1195 var compA = $(a).text().toUpperCase();
1196 var compB = $(b).text().toUpperCase();
1197
1198 // Sort by prefixes first
1199 for (var i in gateway.user_prefixes) {
1200 prefix = gateway.user_prefixes[i].symbol;
1201
1202 if(compA.charAt(0) == prefix && compB.charAt(0) == prefix){
1203 // Both have the same prefix, string compare time
1204 return 0;
1205 }
1206
1207 if(compA.charAt(0) == prefix && compB.charAt(0) != prefix){
1208 return -1;
1209 }
1210
1211 if(compA.charAt(0) != prefix && compB.charAt(0) == prefix){
1212 return 1;
1213 }
1214 }
1215
1216 // No prefixes, compare by string
1217 return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
1218 })
1219 $.each(listitems, function(idx, itm) { ul.append(itm); });
1220 }
1221
1222 tabview.prototype.highlight = function(){
1223 this.tab.addClass('highlight');
1224 }
1225 tabview.prototype.activity = function(){
1226 this.tab.addClass('activity');
1227 }
1228 tabview.prototype.clearHighlight = function(){
1229 this.tab.removeClass('highlight');
1230 this.tab.removeClass('activity');
1231 }
1232 tabview.prototype.changeTopic = function(new_topic){
1233 this.topic = new_topic;
1234 this.addMsg(null, ' ', '=== Topic for '+this.name+' is: '+new_topic, 'topic');
1235 if(front.cur_channel.name == this.name) front.setTopicText(new_topic);
1236 }
1237
1238
1239
1240
1241
1242 var box = function(classname){
1243 this.id = randomString(10);
1244 var tmp = $('<div id="'+this.id+'" class="box '+classname+'"><div class="boxarea"></div></div>');
1245 $('#kiwi').append(tmp);
1246 this.box = $('#'+this.id);
1247 this.content = $('#'+this.id+' .boxarea');
1248
1249 this.box.draggable({ stack: ".box" });
1250 this.content.click(function(){});
1251 //this.box.click(function(){ $(this)..css });
1252 }
1253 box.prototype.create = function(name, classname){
1254
1255 }
1256 box.prototype.id = null;
1257 box.prototype.box = null;
1258 box.prototype.content = null;
1259 box.prototype.destroy = function(){
1260 this.box.remove();
1261 for (var name in front.boxes) if(front.boxes[name].id = this.id) delete front.boxes[name];
1262 }
1263 box.prototype.height = function(){ return this.box.height(); }