Merge remote-tracking branch 'origin/development' into M2ys4U-server-time
[KiwiIRC.git] / client / src / helpers / 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 * Generate a random string of given length
8 * @param {Number} string_length The length of the random string
9 * @returns {String} The random string
10 */
11 function randomString(string_length) {
12 var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz",
13 randomstring = '',
14 i,
15 rnum;
16 for (i = 0; i < string_length; i++) {
17 rnum = Math.floor(Math.random() * chars.length);
18 randomstring += chars.substring(rnum, rnum + 1);
19 }
20 return randomstring;
21 }
22
23 /**
24 * String.trim shim
25 */
26 if (typeof String.prototype.trim === 'undefined') {
27 String.prototype.trim = function () {
28 return this.replace(/^\s+|\s+$/g, "");
29 };
30 }
31
32 /**
33 * String.lpad shim
34 * @param {Number} length The length of padding
35 * @param {String} characher The character to pad with
36 * @returns {String} The padded string
37 */
38 if (typeof String.prototype.lpad === 'undefined') {
39 String.prototype.lpad = function (length, character) {
40 var padding = "",
41 i;
42 for (i = 0; i < length; i++) {
43 padding += character;
44 }
45 return (padding + this).slice(-length);
46 };
47 }
48
49
50 /**
51 * Convert seconds into hours:minutes:seconds
52 * @param {Number} secs The number of seconds to converts
53 * @returns {Object} An object representing the hours/minutes/second conversion of secs
54 */
55 function secondsToTime(secs) {
56 var hours, minutes, seconds, divisor_for_minutes, divisor_for_seconds, obj;
57 hours = Math.floor(secs / (60 * 60));
58
59 divisor_for_minutes = secs % (60 * 60);
60 minutes = Math.floor(divisor_for_minutes / 60);
61
62 divisor_for_seconds = divisor_for_minutes % 60;
63 seconds = Math.ceil(divisor_for_seconds);
64
65 obj = {
66 "h": hours,
67 "m": minutes,
68 "s": seconds
69 };
70 return obj;
71 }
72
73
74 /* Command input Alias + re-writing */
75 function InputPreProcessor () {
76 this.recursive_depth = 3;
77
78 this.aliases = {};
79 this.vars = {version: 1};
80
81 // Current recursive depth
82 var depth = 0;
83
84
85 // Takes an array of words to process!
86 this.processInput = function (input) {
87 var words = input || [],
88 alias = this.aliases[words[0]],
89 alias_len,
90 current_alias_word = '',
91 compiled = [];
92
93 // If an alias wasn't found, return the original input
94 if (!alias) return input;
95
96 // Split the alias up into useable words
97 alias = alias.split(' ');
98 alias_len = alias.length;
99
100 // Iterate over each word and pop them into the final compiled array.
101 // Any $ words are processed with the result ending into the compiled array.
102 for (var i=0; i<alias_len; i++) {
103 current_alias_word = alias[i];
104
105 // Non $ word
106 if (current_alias_word[0] !== '$') {
107 compiled.push(current_alias_word);
108 continue;
109 }
110
111 // Refering to an input word ($N)
112 if (!isNaN(current_alias_word[1])) {
113 var num = current_alias_word.match(/\$(\d+)(\+)?(\d+)?/);
114
115 // Did we find anything or does the word it refers to non-existant?
116 if (!num || !words[num[1]]) continue;
117
118 if (num[2] === '+' && num[3]) {
119 // Add X number of words
120 compiled = compiled.concat(words.slice(parseInt(num[1], 10), parseInt(num[1], 10) + parseInt(num[3], 10)));
121 } else if (num[2] === '+') {
122 // Add the remaining of the words
123 compiled = compiled.concat(words.slice(parseInt(num[1], 10)));
124 } else {
125 // Add a single word
126 compiled.push(words[parseInt(num[1], 10)]);
127 }
128
129 continue;
130 }
131
132
133 // Refering to a variable
134 if (typeof this.vars[current_alias_word.substr(1)] !== 'undefined') {
135
136 // Get the variable
137 compiled.push(this.vars[current_alias_word.substr(1)]);
138
139 continue;
140 }
141
142 }
143
144 return compiled;
145 };
146
147
148 this.process = function (input) {
149 input = input || '';
150
151 var words = input.split(' ');
152
153 depth++;
154 if (depth >= this.recursive_depth) {
155 depth--;
156 return input;
157 }
158
159 if (this.aliases[words[0]]) {
160 words = this.processInput(words);
161
162 if (this.aliases[words[0]]) {
163 words = this.process(words.join(' ')).split(' ');
164 }
165
166 }
167
168 depth--;
169 return words.join(' ');
170 };
171 }
172
173
174 /**
175 * Convert HSL to RGB formatted colour
176 */
177 function hsl2rgb(h, s, l) {
178 var m1, m2, hue;
179 var r, g, b
180 s /=100;
181 l /= 100;
182 if (s == 0)
183 r = g = b = (l * 255);
184 else {
185 function HueToRgb(m1, m2, hue) {
186 var v;
187 if (hue < 0)
188 hue += 1;
189 else if (hue > 1)
190 hue -= 1;
191
192 if (6 * hue < 1)
193 v = m1 + (m2 - m1) * hue * 6;
194 else if (2 * hue < 1)
195 v = m2;
196 else if (3 * hue < 2)
197 v = m1 + (m2 - m1) * (2/3 - hue) * 6;
198 else
199 v = m1;
200
201 return 255 * v;
202 }
203 if (l <= 0.5)
204 m2 = l * (s + 1);
205 else
206 m2 = l + s - l * s;
207 m1 = l * 2 - m2;
208 hue = h / 360;
209 r = HueToRgb(m1, m2, hue + 1/3);
210 g = HueToRgb(m1, m2, hue);
211 b = HueToRgb(m1, m2, hue - 1/3);
212 }
213 return [r,g,b];
214 }
215
216
217 /**
218 * Formats a message. Adds bold, underline and colouring
219 * @param {String} msg The message to format
220 * @returns {String} The HTML formatted message
221 */
222 function formatIRCMsg (msg) {
223 "use strict";
224 var out = '',
225 currentTag = '',
226 openTags = {
227 bold: false,
228 italic: false,
229 underline: false,
230 colour: false
231 },
232 spanFromOpen = function () {
233 var style = '',
234 colours;
235 if (!(openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
236 return '';
237 } else {
238 style += (openTags.bold) ? 'font-weight: bold; ' : '';
239 style += (openTags.italic) ? 'font-style: italic; ' : '';
240 style += (openTags.underline) ? 'text-decoration: underline; ' : '';
241 if (openTags.colour) {
242 colours = openTags.colour.split(',');
243 style += 'color: ' + colours[0] + ((colours[1]) ? '; background-color: ' + colours[1] + ';' : '');
244 }
245 return '<span class="format_span" style="' + style + '">';
246 }
247 },
248 colourMatch = function (str) {
249 var re = /^\x03(([0-9][0-9]?)(,([0-9][0-9]?))?)/;
250 return re.exec(str);
251 },
252 hexFromNum = function (num) {
253 switch (parseInt(num, 10)) {
254 case 0:
255 return '#FFFFFF';
256 case 1:
257 return '#000000';
258 case 2:
259 return '#000080';
260 case 3:
261 return '#008000';
262 case 4:
263 return '#FF0000';
264 case 5:
265 return '#800040';
266 case 6:
267 return '#800080';
268 case 7:
269 return '#FF8040';
270 case 8:
271 return '#FFFF00';
272 case 9:
273 return '#80FF00';
274 case 10:
275 return '#008080';
276 case 11:
277 return '#00FFFF';
278 case 12:
279 return '#0000FF';
280 case 13:
281 return '#FF55FF';
282 case 14:
283 return '#808080';
284 case 15:
285 return '#C0C0C0';
286 default:
287 return null;
288 }
289 },
290 i = 0,
291 colours = [],
292 match;
293
294 for (i = 0; i < msg.length; i++) {
295 switch (msg[i]) {
296 case '\x02':
297 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
298 out += currentTag + '</span>';
299 }
300 openTags.bold = !openTags.bold;
301 currentTag = spanFromOpen();
302 break;
303 case '\x1D':
304 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
305 out += currentTag + '</span>';
306 }
307 openTags.italic = !openTags.italic;
308 currentTag = spanFromOpen();
309 break;
310 case '\x1F':
311 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
312 out += currentTag + '</span>';
313 }
314 openTags.underline = !openTags.underline;
315 currentTag = spanFromOpen();
316 break;
317 case '\x03':
318 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
319 out += currentTag + '</span>';
320 }
321 match = colourMatch(msg.substr(i, 6));
322 if (match) {
323 i += match[1].length;
324 // 2 & 4
325 colours[0] = hexFromNum(match[2]);
326 if (match[4]) {
327 colours[1] = hexFromNum(match[4]);
328 }
329 openTags.colour = colours.join(',');
330 } else {
331 openTags.colour = false;
332 }
333 currentTag = spanFromOpen();
334 break;
335 case '\x0F':
336 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
337 out += currentTag + '</span>';
338 }
339 openTags.bold = openTags.italic = openTags.underline = openTags.colour = false;
340 break;
341 default:
342 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
343 currentTag += msg[i];
344 } else {
345 out += msg[i];
346 }
347 break;
348 }
349 }
350 if ((openTags.bold || openTags.italic || openTags.underline || openTags.colour)) {
351 out += currentTag + '</span>';
352 }
353 return out;
354 }
355
356
357 function formatDate (d) {
358 d = d || new Date();
359 return d.toLocaleDateString() + ', ' + d.getHours().toString() + ':' + d.getMinutes().toString() + ':' + d.getSeconds().toString();
360 }
361
362 function escapeRegex (str) {
363 return str.replace(/[\[\\\^\$\.\|\?\*\+\(\)]/g, '\\$&');
364 }
365
366 function emoticonFromText(str) {
367 var words_in = str.split(' '),
368 words_out = [],
369 i,
370 pushEmoticon = function (alt, emote_name) {
371 words_out.push('<i class="emoticon ' + emote_name + '">' + alt + '</i>');
372 };
373
374 for (i = 0; i < words_in.length; i++) {
375 switch(words_in[i]) {
376 case ':)':
377 pushEmoticon(':)', 'smile');
378 break;
379 case ':(':
380 pushEmoticon(':(', 'sad');
381 break;
382 case ':3':
383 pushEmoticon(':3', 'lion');
384 break;
385 case ';3':
386 pushEmoticon(';3', 'winky_lion');
387 break;
388 case ':s':
389 case ':S':
390 pushEmoticon(':s', 'confused');
391 break;
392 case ';(':
393 case ';_;':
394 pushEmoticon(';(', 'cry');
395 break;
396 case ';)':
397 pushEmoticon(';)', 'wink');
398 break;
399 case ';D':
400 pushEmoticon(';D"', 'wink_happy');
401 break;
402 case ':P':
403 case ':p':
404 pushEmoticon(':P', 'tongue');
405 break;
406 case 'xP':
407 pushEmoticon('xP', 'cringe_tongue');
408 break;
409 case ':o':
410 case ':O':
411 case ':0':
412 pushEmoticon(':o', 'shocked');
413 break;
414 case ':D':
415 pushEmoticon(':D', 'happy');
416 break;
417 case '^^':
418 case '^.^':
419 pushEmoticon('^^,', 'eyebrows');
420 break;
421 case '&lt;3':
422 pushEmoticon('<3', 'heart');
423 break;
424 case '&gt;_&lt;':
425 case '&gt;.&lt;':
426 pushEmoticon('>_<', 'doh');
427 break;
428 case 'XD':
429 case 'xD':
430 pushEmoticon('xD', 'big_grin');
431 break;
432 case 'o.0':
433 case 'o.O':
434 pushEmoticon('o.0', 'wide_eye_right');
435 break;
436 case '0.o':
437 case 'O.o':
438 pushEmoticon('0.o', 'wide_eye_left');
439 break;
440 case ':\\':
441 case '=\\':
442 case ':/':
443 case '=/':
444 pushEmoticon(':\\', 'unsure');
445 break;
446 default:
447 words_out.push(words_in[i]);
448 }
449 }
450
451 return words_out.join(' ');
452 }
453
454 // Code based on http://anentropic.wordpress.com/2009/06/25/javascript-iso8601-parser-and-pretty-dates/#comment-154
455 function parseISO8601(str) {
456 if (Date.prototype.toISOString) {
457 return new Date(str);
458 } else {
459 var parts = str.split('T'),
460 dateParts = parts[0].split('-'),
461 timeParts = parts[1].split('Z'),
462 timeSubParts = timeParts[0].split(':'),
463 timeSecParts = timeSubParts[2].split('.'),
464 timeHours = Number(timeSubParts[0]),
465 _date = new Date();
466
467 _date.setUTCFullYear(Number(dateParts[0]));
468 _date.setUTCDate(1);
469 _date.setUTCMonth(Number(dateParts[1])-1);
470 _date.setUTCDate(Number(dateParts[2]));
471 _date.setUTCHours(Number(timeHours));
472 _date.setUTCMinutes(Number(timeSubParts[1]));
473 _date.setUTCSeconds(Number(timeSecParts[0]));
474 if (timeSecParts[1]) {
475 _date.setUTCMilliseconds(Number(timeSecParts[1]));
476 }
477
478 return _date;
479 }
480 }