0e21a33b1972e16e010e4f2f2a88bdea9c0cf6cb
[KiwiIRC.git] / client / assets / dev / utils.js
1 /*jslint devel: true, browser: true, continue: true, sloppy: true, forin: true, plusplus: true, maxerr: 50, indent: 4, nomen: true, regexp: true*/
2 /*globals $, front, gateway, Utilityview */
3
4
5
6 /**
7 * Suppresses console.log
8 * @param {Boolean} debug Whether to re-enable console.log or not
9 */
10 function manageDebug(debug) {
11 var log, consoleBackUp;
12 if (window.console) {
13 consoleBackUp = window.console.log;
14 window.console.log = function () {
15 if (debug) {
16 consoleBackUp.apply(console, arguments);
17 }
18 };
19 } else {
20 log = window.opera ? window.opera.postError : alert;
21 window.console = {};
22 window.console.log = function (str) {
23 if (debug) {
24 log(str);
25 }
26 };
27 }
28 }
29
30 /**
31 * Generate a random string of given length
32 * @param {Number} string_length The length of the random string
33 * @returns {String} The random string
34 */
35 function randomString(string_length) {
36 var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz",
37 randomstring = '',
38 i,
39 rnum;
40 for (i = 0; i < string_length; i++) {
41 rnum = Math.floor(Math.random() * chars.length);
42 randomstring += chars.substring(rnum, rnum + 1);
43 }
44 return randomstring;
45 }
46
47 /**
48 * String.trim shim
49 */
50 if (typeof String.prototype.trim === 'undefined') {
51 String.prototype.trim = function () {
52 return this.replace(/^\s+|\s+$/g, "");
53 };
54 }
55
56 /**
57 * String.lpad shim
58 * @param {Number} length The length of padding
59 * @param {String} characher The character to pad with
60 * @returns {String} The padded string
61 */
62 if (typeof String.prototype.lpad === 'undefined') {
63 String.prototype.lpad = function (length, character) {
64 var padding = "",
65 i;
66 for (i = 0; i < length; i++) {
67 padding += character;
68 }
69 return (padding + this).slice(-length);
70 };
71 }
72
73
74 /**
75 * Convert seconds into hours:minutes:seconds
76 * @param {Number} secs The number of seconds to converts
77 * @returns {Object} An object representing the hours/minutes/second conversion of secs
78 */
79 function secondsToTime(secs) {
80 var hours, minutes, seconds, divisor_for_minutes, divisor_for_seconds, obj;
81 hours = Math.floor(secs / (60 * 60));
82
83 divisor_for_minutes = secs % (60 * 60);
84 minutes = Math.floor(divisor_for_minutes / 60);
85
86 divisor_for_seconds = divisor_for_minutes % 60;
87 seconds = Math.ceil(divisor_for_seconds);
88
89 obj = {
90 "h": hours,
91 "m": minutes,
92 "s": seconds
93 };
94 return obj;
95 }
96
97
98
99
100
101
102 /* Command input Alias + re-writing */
103 function InputPreProcessor () {
104 this.recursive_depth = 3;
105
106 this.aliases = {};
107 this.vars = {version: 1};
108
109 // Current recursive depth
110 var depth = 0;
111
112
113 // Takes an array of words to process!
114 this.processInput = function (input) {
115 var words = input || [],
116 alias = this.aliases[words[0]],
117 alias_len,
118 current_alias_word = '',
119 compiled = [];
120
121 // If an alias wasn't found, return the original input
122 if (!alias) return input;
123
124 // Split the alias up into useable words
125 alias = alias.split(' ');
126 alias_len = alias.length;
127
128 // Iterate over each word and pop them into the final compiled array.
129 // Any $ words are processed with the result ending into the compiled array.
130 for (var i=0; i<alias_len; i++) {
131 current_alias_word = alias[i];
132
133 // Non $ word
134 if (current_alias_word[0] !== '$') {
135 compiled.push(current_alias_word);
136 continue;
137 }
138
139 // Refering to an input word ($N)
140 if (!isNaN(current_alias_word[1])) {
141 var num = current_alias_word.match(/\$(\d+)(\+)?(\d+)?/);
142
143 // Did we find anything or does the word it refers to non-existant?
144 if (!num || !words[num[1]]) continue;
145
146 if (num[2] === '+' && num[3]) {
147 // Add X number of words
148 compiled = compiled.concat(words.slice(parseInt(num[1], 10), parseInt(num[1], 10) + parseInt(num[3], 10)));
149 } else if (num[2] === '+') {
150 // Add the remaining of the words
151 compiled = compiled.concat(words.slice(parseInt(num[1], 10)));
152 } else {
153 // Add a single word
154 compiled.push(words[parseInt(num[1], 10)]);
155 }
156
157 continue;
158 }
159
160
161 // Refering to a variable
162 if (typeof this.vars[current_alias_word.substr(1)] !== 'undefined') {
163
164 // Get the variable
165 compiled.push(this.vars[current_alias_word.substr(1)]);
166
167 continue;
168 }
169
170 }
171
172 return compiled;
173 };
174
175
176 this.process = function (input) {
177 input = input || '';
178
179 var words = input.split(' ');
180
181 depth++;
182 if (depth >= this.recursive_depth) {
183 depth--;
184 return input;
185 }
186
187 if (this.aliases[words[0]]) {
188 words = this.processInput(words);
189
190 if (this.aliases[words[0]]) {
191 words = this.process(words.join(' ')).split(' ');
192 }
193
194 }
195
196 depth--;
197 return words.join(' ');
198 };
199 }
200
201
202
203
204
205
206
207
208
209
210
211 /**
212 * Convert HSL to RGB formatted colour
213 */
214 function hsl2rgb(h, s, l) {
215 var m1, m2, hue;
216 var r, g, b
217 s /=100;
218 l /= 100;
219 if (s == 0)
220 r = g = b = (l * 255);
221 else {
222 function HueToRgb(m1, m2, hue) {
223 var v;
224 if (hue < 0)
225 hue += 1;
226 else if (hue > 1)
227 hue -= 1;
228
229 if (6 * hue < 1)
230 v = m1 + (m2 - m1) * hue * 6;
231 else if (2 * hue < 1)
232 v = m2;
233 else if (3 * hue < 2)
234 v = m1 + (m2 - m1) * (2/3 - hue) * 6;
235 else
236 v = m1;
237
238 return 255 * v;
239 }
240 if (l <= 0.5)
241 m2 = l * (s + 1);
242 else
243 m2 = l + s - l * s;
244 m1 = l * 2 - m2;
245 hue = h / 360;
246 r = HueToRgb(m1, m2, hue + 1/3);
247 g = HueToRgb(m1, m2, hue);
248 b = HueToRgb(m1, m2, hue - 1/3);
249 }
250 return [r,g,b];
251 }
252
253
254
255
256
257 /**
258 * Formats a message. Adds bold, underline and colouring
259 * @param {String} msg The message to format
260 * @returns {String} The HTML formatted message
261 */
262 function formatIRCMsg (msg) {
263 "use strict";
264 var out = '',
265 currentTag = '',
266 openTags = {
267 bold: false,
268 italic: false,
269 underline: false,
270 colour: false
271 },
272 anyOpen = function () {
273 return (openTags.bold || openTags.italic || openTags.underline || openTags.colour);
274 },
275 spanFromOpen = function () {
276 var style = '',
277 colours;
278 if (!anyOpen()) {
279 return '';
280 } else {
281 style += (openTags.bold) ? 'font-weight: bold; ' : '';
282 style += (openTags.italic) ? 'font-style: italic; ' : '';
283 style += (openTags.underline) ? 'text-decoration: underline; ' : '';
284 if (openTags.colour) {
285 colours = openTags.colour.split(',');
286 style += 'color: ' + colours[0] + ((colours[1]) ? '; background-color: ' + colours[1] + ';' : '');
287 }
288 return '<span class="formatSpan" style="' + style + '">';
289 }
290 },
291 colourMatch = function (str) {
292 var re = /^\x03(([0-9][0-9]?)(,([0-9][0-9]?))?)/;
293 return re.exec(str);
294 },
295 hexFromNum = function (num) {
296 switch (parseInt(num, 10)) {
297 case 0:
298 return '#FFFFFF';
299 case 1:
300 return '#000000';
301 case 2:
302 return '#000080';
303 case 3:
304 return '#008000';
305 case 4:
306 return '#FF0000';
307 case 5:
308 return '#800040';
309 case 6:
310 return '#800080';
311 case 7:
312 return '#FF8040';
313 case 8:
314 return '#FFFF00';
315 case 9:
316 return '#80FF00';
317 case 10:
318 return '#008080';
319 case 11:
320 return '#00FFFF';
321 case 12:
322 return '#0000FF';
323 case 13:
324 return '#FF55FF';
325 case 14:
326 return '#808080';
327 case 15:
328 return '#C0C0C0';
329 default:
330 return null;
331 }
332 },
333 i = 0,
334 colours = [],
335 match;
336
337 for (i = 0; i < msg.length; i++) {
338 switch (msg[i]) {
339 case '\x02':
340 if (anyOpen()) {
341 out += currentTag + '</span>';
342 }
343 openTags.bold = !openTags.bold;
344 currentTag = spanFromOpen();
345 break;
346 case '\x1D':
347 if (anyOpen()) {
348 out += currentTag + '</span>';
349 }
350 openTags.italic = !openTags.italic;
351 currentTag = spanFromOpen();
352 break;
353 case '\x1F':
354 if (anyOpen()) {
355 out += currentTag + '</span>';
356 }
357 openTags.underline = !openTags.underline;
358 currentTag = spanFromOpen();
359 break;
360 case '\x03':
361 if (anyOpen()) {
362 out += currentTag + '</span>';
363 }
364 match = colourMatch(msg.substr(i, 6));
365 if (match) {
366 i += match[1].length;
367 // 2 & 4
368 colours[0] = hexFromNum(match[2]);
369 if (match[4]) {
370 colours[1] = hexFromNum(match[4]);
371 }
372 openTags.colour = colours.join(',');
373 } else {
374 openTags.colour = false;
375 }
376 currentTag = spanFromOpen();
377 break;
378 case '\x0F':
379 if (anyOpen()) {
380 out += currentTag + '</span>';
381 }
382 openTags.bold = openTags.italic = openTags.underline = openTags.colour = false;
383 break;
384 default:
385 if (anyOpen()) {
386 currentTag += msg[i];
387 } else {
388 out += msg[i];
389 }
390 break;
391 }
392 }
393 if (anyOpen()) {
394 out += currentTag + '</span>';
395 }
396 return out;
397 }
398
399
400
401
402 function formatDate (d) {
403 d = d || new Date();
404 return d.toLocaleDateString() + ', ' + d.getHours().toString() + ':' + d.getMinutes().toString() + ':' + d.getSeconds().toString();
405 }
406
407
408
409
410
411
412
413
414 /*
415 PLUGINS
416 Each function in each object is looped through and ran. The resulting text
417 is expected to be returned.
418 */
419 var plugins = [
420 {
421 name: "images",
422 onaddmsg: function (event, opts) {
423 if (!event.msg) {
424 return event;
425 }
426
427 event.msg = event.msg.replace(/^((https?\:\/\/|ftp\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?(\.jpg|\.jpeg|\.gif|\.bmp|\.png)$/gi, function (url) {
428 // Don't let any future plugins change it (ie. html_safe plugins)
429 event.event_bubbles = false;
430
431 var img = '<img class="link_img_a" src="' + url + '" height="100%" width="100%" />';
432 return '<a class="link_ext link_img" target="_blank" rel="nofollow" href="' + url + '" style="height:50px;width:50px;display:block">' + img + '<div class="tt box"></div></a>';
433 });
434
435 return event;
436 }
437 },
438
439 {
440 name: "html_safe",
441 onaddmsg: function (event, opts) {
442 event.msg = $('<div/>').text(event.msg).html();
443 event.nick = $('<div/>').text(event.nick).html();
444
445 return event;
446 }
447 },
448
449 {
450 name: "activity",
451 onaddmsg: function (event, opts) {
452 //if (_kiwi.front.cur_channel.name.toLowerCase() !== _kiwi.front.tabviews[event.tabview.toLowerCase()].name) {
453 // _kiwi.front.tabviews[event.tabview].activity();
454 //}
455
456 return event;
457 }
458 },
459
460 {
461 name: "highlight",
462 onaddmsg: function (event, opts) {
463 //var tab = Tabviews.getTab(event.tabview.toLowerCase());
464
465 // If we have a highlight...
466 //if (event.msg.toLowerCase().indexOf(_kiwi.gateway.nick.toLowerCase()) > -1) {
467 // if (Tabview.getCurrentTab() !== tab) {
468 // tab.highlight();
469 // }
470 // if (_kiwi.front.isChannel(tab.name)) {
471 // event.msg = '<span style="color:red;">' + event.msg + '</span>';
472 // }
473 //}
474
475 // If it's a PM, highlight
476 //if (!_kiwi.front.isChannel(tab.name) && tab.name !== "server"
477 // && Tabview.getCurrentTab().name.toLowerCase() !== tab.name
478 //) {
479 // tab.highlight();
480 //}
481
482 return event;
483 }
484 },
485
486
487
488 {
489 //Following method taken from: http://snipplr.com/view/13533/convert-text-urls-into-links/
490 name: "linkify_plain",
491 onaddmsg: function (event, opts) {
492 if (!event.msg) {
493 return event;
494 }
495
496 event.msg = event.msg.replace(/((https?\:\/\/|ftp\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/gi, function (url) {
497 var nice;
498 // If it's any of the supported images in the images plugin, skip it
499 if (url.match(/(\.jpg|\.jpeg|\.gif|\.bmp|\.png)$/)) {
500 return url;
501 }
502
503 nice = url;
504 if (url.match('^https?:\/\/')) {
505 //nice = nice.replace(/^https?:\/\//i,'')
506 nice = url; // Shutting up JSLint...
507 } else {
508 url = 'http://' + url;
509 }
510
511 //return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '<div class="tt box"></div></a>';
512 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '</a>';
513 });
514
515 return event;
516 }
517 },
518
519 {
520 name: "lftobr",
521 onaddmsg: function (event, opts) {
522 if (!event.msg) {
523 return event;
524 }
525
526 event.msg = event.msg.replace(/\n/gi, function (txt) {
527 return '<br/>';
528 });
529
530 return event;
531 }
532 },
533
534
535 /*
536 * Disabled due to many websites closing kiwi with iframe busting
537 {
538 name: "inBrowser",
539 oninit: function (event, opts) {
540 $('#windows a.link_ext').live('mouseover', this.mouseover);
541 $('#windows a.link_ext').live('mouseout', this.mouseout);
542 $('#windows a.link_ext').live('click', this.mouseclick);
543 },
544
545 onunload: function (event, opts) {
546 // TODO: make this work (remove all .link_ext_browser as created in mouseover())
547 $('#windows a.link_ext').die('mouseover', this.mouseover);
548 $('#windows a.link_ext').die('mouseout', this.mouseout);
549 $('#windows a.link_ext').die('click', this.mouseclick);
550 },
551
552
553
554 mouseover: function (e) {
555 var a = $(this),
556 tt = $('.tt', a),
557 tooltip;
558
559 if (tt.text() === '') {
560 tooltip = $('<a class="link_ext_browser">Open in _kiwi..</a>');
561 tt.append(tooltip);
562 }
563
564 tt.css('top', -tt.outerHeight() + 'px');
565 tt.css('left', (a.outerWidth() / 2) - (tt.outerWidth() / 2));
566 },
567
568 mouseout: function (e) {
569 var a = $(this),
570 tt = $('.tt', a);
571 },
572
573 mouseclick: function (e) {
574 var a = $(this),
575 t;
576
577 switch (e.target.className) {
578 case 'link_ext':
579 case 'link_img_a':
580 return true;
581 //break;
582 case 'link_ext_browser':
583 t = new Utilityview('Browser');
584 t.topic = a.attr('href');
585
586 t.iframe = $('<iframe border="0" class="utility_view" src="" style="width:100%;height:100%;border:none;"></iframe>');
587 t.iframe.attr('src', a.attr('href'));
588 t.div.append(t.iframe);
589 t.show();
590 break;
591 }
592 return false;
593
594 }
595 },
596 */
597
598 {
599 name: "nick_colour",
600 onaddmsg: function (event, opts) {
601 if (!event.msg) {
602 return event;
603 }
604
605 //if (typeof _kiwi.front.tabviews[event.tabview].nick_colours === 'undefined') {
606 // _kiwi.front.tabviews[event.tabview].nick_colours = {};
607 //}
608
609 //if (typeof _kiwi.front.tabviews[event.tabview].nick_colours[event.nick] === 'undefined') {
610 // _kiwi.front.tabviews[event.tabview].nick_colours[event.nick] = this.randColour();
611 //}
612
613 //var c = _kiwi.front.tabviews[event.tabview].nick_colours[event.nick];
614 var c = this.randColour();
615 event.nick = '<span style="color:' + c + ';">' + event.nick + '</span>';
616
617 return event;
618 },
619
620
621
622 randColour: function () {
623 var h = this.rand(-250, 0),
624 s = this.rand(30, 100),
625 l = this.rand(20, 70);
626 return 'hsl(' + h + ',' + s + '%,' + l + '%)';
627 },
628
629
630 rand: function (min, max) {
631 return parseInt(Math.random() * (max - min + 1), 10) + min;
632 }
633 },
634
635 {
636 name: "kiwitest",
637 oninit: function (event, opts) {
638 console.log('registering namespace');
639 $(gateway).bind("_kiwi.lol.browser", function (e, data) {
640 console.log('YAY kiwitest');
641 console.log(data);
642 });
643 }
644 }
645 ];
646
647
648
649
650
651
652
653
654 /**
655 * @constructor
656 * @param {String} data_namespace The namespace for the data store
657 */
658 _kiwi.dataStore = function (data_namespace) {
659 var namespace = data_namespace;
660
661 this.get = function (key) {
662 return $.jStorage.get(data_namespace + '_' + key);
663 };
664
665 this.set = function (key, value) {
666 return $.jStorage.set(data_namespace + '_' + key, value);
667 };
668 };
669
670 _kiwi.data = new _kiwi.dataStore('kiwi');
671
672
673
674
675 /*
676 * jQuery jStorage plugin
677 * https://github.com/andris9/jStorage/
678 */
679 (function(f){if(!f||!(f.toJSON||Object.toJSON||window.JSON)){throw new Error("jQuery, MooTools or Prototype needs to be loaded before jStorage!")}var g={},d={jStorage:"{}"},h=null,j=0,l=f.toJSON||Object.toJSON||(window.JSON&&(JSON.encode||JSON.stringify)),e=f.evalJSON||(window.JSON&&(JSON.decode||JSON.parse))||function(m){return String(m).evalJSON()},i=false;_XMLService={isXML:function(n){var m=(n?n.ownerDocument||n:0).documentElement;return m?m.nodeName!=="HTML":false},encode:function(n){if(!this.isXML(n)){return false}try{return new XMLSerializer().serializeToString(n)}catch(m){try{return n.xml}catch(o){}}return false},decode:function(n){var m=("DOMParser" in window&&(new DOMParser()).parseFromString)||(window.ActiveXObject&&function(p){var q=new ActiveXObject("Microsoft.XMLDOM");q.async="false";q.loadXML(p);return q}),o;if(!m){return false}o=m.call("DOMParser" in window&&(new DOMParser())||window,n,"text/xml");return this.isXML(o)?o:false}};function k(){if("localStorage" in window){try{if(window.localStorage){d=window.localStorage;i="localStorage"}}catch(p){}}else{if("globalStorage" in window){try{if(window.globalStorage){d=window.globalStorage[window.location.hostname];i="globalStorage"}}catch(o){}}else{h=document.createElement("link");if(h.addBehavior){h.style.behavior="url(#default#userData)";document.getElementsByTagName("head")[0].appendChild(h);h.load("jStorage");var n="{}";try{n=h.getAttribute("jStorage")}catch(m){}d.jStorage=n;i="userDataBehavior"}else{h=null;return}}}b()}function b(){if(d.jStorage){try{g=e(String(d.jStorage))}catch(m){d.jStorage="{}"}}else{d.jStorage="{}"}j=d.jStorage?String(d.jStorage).length:0}function c(){try{d.jStorage=l(g);if(h){h.setAttribute("jStorage",d.jStorage);h.save("jStorage")}j=d.jStorage?String(d.jStorage).length:0}catch(m){}}function a(m){if(!m||(typeof m!="string"&&typeof m!="number")){throw new TypeError("Key name must be string or numeric")}return true}f.jStorage={version:"0.1.5.1",set:function(m,n){a(m);if(_XMLService.isXML(n)){n={_is_xml:true,xml:_XMLService.encode(n)}}g[m]=n;c();return n},get:function(m,n){a(m);if(m in g){if(g[m]&&typeof g[m]=="object"&&g[m]._is_xml&&g[m]._is_xml){return _XMLService.decode(g[m].xml)}else{return g[m]}}return typeof(n)=="undefined"?null:n},deleteKey:function(m){a(m);if(m in g){delete g[m];c();return true}return false},flush:function(){g={};c();return true},storageObj:function(){function m(){}m.prototype=g;return new m()},index:function(){var m=[],n;for(n in g){if(g.hasOwnProperty(n)){m.push(n)}}return m},storageSize:function(){return j},currentBackend:function(){return i},storageAvailable:function(){return !!i},reInit:function(){var m,o;if(h&&h.addBehavior){m=document.createElement("link");h.parentNode.replaceChild(m,h);h=m;h.style.behavior="url(#default#userData)";document.getElementsByTagName("head")[0].appendChild(h);h.load("jStorage");o="{}";try{o=h.getAttribute("jStorage")}catch(n){}d.jStorage=o;i="userDataBehavior"}b()}};k()})(window.jQuery||window.$);