Use case-insensitive comparisons against own nick.
[KiwiIRC.git] / client / assets / src / models / gateway.js
CommitLineData
eaaf73b0 1_kiwi.model.Gateway = function () {\r
696a66f8
D
2\r
3 // Set to a reference to this object within initialize()\r
4 var that = null;\r
5\r
6 this.defaults = {\r
7 /**\r
8 * The name of the network\r
9 * @type String\r
10 */\r
11 name: 'Server',\r
12\r
13 /**\r
14 * The address (URL) of the network\r
15 * @type String\r
16 */\r
17 address: '',\r
18\r
19 /**\r
20 * The current nickname\r
21 * @type String\r
22 */\r
23 nick: '',\r
24\r
25 /**\r
26 * The channel prefix for this network\r
27 * @type String\r
28 */\r
29 channel_prefix: '#',\r
30\r
31 /**\r
32 * The user prefixes for channel owner/admin/op/voice etc. on this network\r
33 * @type Array\r
34 */\r
35 user_prefixes: ['~', '&', '@', '+'],\r
36\r
37 /**\r
38 * The URL to the Kiwi server\r
39 * @type String\r
40 */\r
f3091277
D
41 kiwi_server: '//kiwi',\r
42\r
43 /**\r
44 * List of nicks we are ignoring\r
45 * @type Array\r
46 */\r
47 ignore_list: []\r
696a66f8
D
48 };\r
49\r
50\r
51 this.initialize = function () {\r
52 that = this;\r
09c26937 53\r
696a66f8
D
54 // For ease of access. The socket.io object\r
55 this.socket = this.get('socket');\r
6228b635
D
56\r
57 this.applyEventHandlers();\r
09c26937
D
58\r
59 // Used to check if a disconnection was unplanned\r
60 this.disconnect_requested = false;\r
6228b635
D
61 };\r
62\r
63\r
64 this.applyEventHandlers = function () {\r
65 /*\r
a1629e09
D
66 kiwi.gateway.on('message:#channel', my_function);\r
67 kiwi.gateway.on('message:somenick', my_function);\r
6228b635
D
68\r
69 kiwi.gateway.on('notice:#channel', my_function);\r
70 kiwi.gateway.on('action:somenick', my_function);\r
71\r
72 kiwi.gateway.on('join:#channel', my_function);\r
73 kiwi.gateway.on('part:#channel', my_function);\r
74 kiwi.gateway.on('quit', my_function);\r
75 */\r
76 var that = this;\r
09c26937 77\r
6228b635
D
78 // Some easier handler events\r
79 this.on('onmsg', function (event) {\r
80 var source,\r
fc9b83de 81 connection = _kiwi.app.connections.getByConnectionId(event.server),\r
aef6e2a7 82 is_pm = (event.channel.toLowerCase() == connection.get('nick').toLowerCase());\r
6228b635
D
83\r
84 source = is_pm ? event.nick : event.channel;\r
09c26937 85\r
8913f3ea
D
86 that.trigger('message:' + source, event);\r
87 that.trigger('message', event);\r
6228b635
D
88\r
89 if (is_pm) {\r
6228b635 90 that.trigger('pm:' + source, event);\r
8913f3ea 91 that.trigger('pm', event);\r
6228b635
D
92 }\r
93 }, this);\r
94\r
95\r
96 this.on('onnotice', function (event) {\r
97 // The notice towards a channel or a query window?\r
98 var source = event.target || event.nick;\r
99\r
6228b635 100 this.trigger('notice:' + source, event);\r
8913f3ea 101 this.trigger('notice', event);\r
6228b635
D
102 }, this);\r
103\r
104\r
105 this.on('onaction', function (event) {\r
106 var source,\r
fc9b83de 107 connection = _kiwi.app.connections.getByConnectionId(event.server),\r
aef6e2a7 108 is_pm = (event.channel.toLowerCase() == connection.get('nick').toLowerCase());\r
6228b635
D
109\r
110 source = is_pm ? event.nick : event.channel;\r
09c26937 111\r
6228b635
D
112 that.trigger('action:' + source, event);\r
113\r
114 if (is_pm) {\r
6228b635 115 that.trigger('action:' + source, event);\r
8913f3ea 116 that.trigger('action', event);\r
6228b635
D
117 }\r
118 }, this);\r
119\r
120\r
121 this.on('ontopic', function (event) {\r
6228b635 122 that.trigger('topic:' + event.channel, event);\r
8913f3ea 123 that.trigger('topic', event);\r
6228b635 124 });\r
22373da6
D
125\r
126\r
127 this.on('onjoin', function (event) {\r
128 that.trigger('join:' + event.channel, event);\r
129 that.trigger('join', event);\r
130 });\r
131\r
696a66f8
D
132 };\r
133\r
134\r
6228b635 135\r
09c26937 136 this.reconnect = function (callback) {\r
d6eec6ed
D
137 var that = this,\r
138 transport_path;\r
139\r
09c26937
D
140 this.disconnect_requested = true;\r
141 this.socket.disconnect();\r
d6eec6ed
D
142\r
143 // To get around the allow-origin issues for requests, completely reload the\r
144 // transport source from the new server\r
145 window.io = null;\r
146\r
147 // Path to get the socket.io transport code\r
148 transport_path = _kiwi.app.kiwi_server + _kiwi.app.get('base_path') + '/transport/socket.io.js?ts='+(new Date().getTime());\r
149 $script(transport_path, function() {\r
150 if (!window.io) {\r
151 return callback('err_kiwi_server_not_found');\r
152 }\r
153\r
154 that.set('kiwi_server', _kiwi.app.kiwi_server + '/kiwi');\r
155 that.connect(callback);\r
156 });\r
09c26937
D
157 };\r
158\r
159\r
160\r
696a66f8
D
161 /**\r
162 * Connects to the server\r
696a66f8
D
163 * @param {Function} callback A callback function to be invoked once Kiwi's server has connected to the IRC server\r
164 */\r
f2bb5380 165 this.connect = function (callback) {\r
8b0eb787
D
166 var resource;\r
167\r
168 // Work out the resource URL for socket.io\r
eaaf73b0
D
169 if (_kiwi.app.get('base_path').substr(0, 1) === '/') {\r
170 resource = _kiwi.app.get('base_path');\r
8b0eb787
D
171 resource = resource.substr(1, resource.length-1);\r
172 resource += '/transport';\r
173 } else {\r
eaaf73b0 174 resource = _kiwi.app.get('base_path') + '/transport';\r
8b0eb787
D
175 }\r
176\r
696a66f8 177 this.socket = io.connect(this.get('kiwi_server'), {\r
8b0eb787 178 'resource': resource,\r
f2bb5380 179\r
696a66f8
D
180 'try multiple transports': true,\r
181 'connect timeout': 3000,\r
182 'max reconnection attempts': 7,\r
183 'reconnection delay': 2000,\r
184 'sync disconnect on unload': false\r
185 });\r
186 this.socket.on('connect_failed', function (reason) {\r
696a66f8
D
187 this.socket.disconnect();\r
188 this.trigger("connect_fail", {reason: reason});\r
189 });\r
190\r
191 this.socket.on('error', function (e) {\r
eaaf73b0 192 console.log("_kiwi.gateway.socket.on('error')", {reason: e});\r
696a66f8
D
193 that.trigger("connect_fail", {reason: e});\r
194 });\r
195\r
196 this.socket.on('connecting', function (transport_type) {\r
eaaf73b0 197 console.log("_kiwi.gateway.socket.on('connecting')");\r
696a66f8
D
198 that.trigger("connecting");\r
199 });\r
200\r
201 /**\r
202 * Once connected to the kiwi server send the IRC connect command along\r
203 * with the IRC server details.\r
204 * A `connect` event is sent from the kiwi server once connected to the\r
205 * IRCD and the nick has been accepted.\r
206 */\r
207 this.socket.on('connect', function () {\r
09c26937
D
208 // Reset the disconnect_requested flag\r
209 that.disconnect_requested = false;\r
210\r
f2bb5380 211 callback && callback();\r
696a66f8
D
212 });\r
213\r
214 this.socket.on('too_many_connections', function () {\r
215 that.trigger("connect_fail", {reason: 'too_many_connections'});\r
216 });\r
217\r
218 this.socket.on('irc', function (data, callback) {\r
219 that.parse(data.command, data.data);\r
220 });\r
221\r
d2d91c10
D
222 this.socket.on('kiwi', function (data, callback) {\r
223 that.parseKiwi(data.command, data.data);\r
224 });\r
225\r
696a66f8
D
226 this.socket.on('disconnect', function () {\r
227 that.trigger("disconnect", {});\r
eaaf73b0 228 console.log("_kiwi.gateway.socket.on('disconnect')");\r
696a66f8
D
229 });\r
230\r
231 this.socket.on('close', function () {\r
eaaf73b0 232 console.log("_kiwi.gateway.socket.on('close')");\r
696a66f8
D
233 });\r
234\r
235 this.socket.on('reconnecting', function (reconnectionDelay, reconnectionAttempts) {\r
eaaf73b0 236 console.log("_kiwi.gateway.socket.on('reconnecting')");\r
696a66f8
D
237 that.trigger("reconnecting", {delay: reconnectionDelay, attempts: reconnectionAttempts});\r
238 });\r
239\r
240 this.socket.on('reconnect_failed', function () {\r
eaaf73b0 241 console.log("_kiwi.gateway.socket.on('reconnect_failed')");\r
696a66f8
D
242 });\r
243 };\r
244\r
245\r
d6eec6ed
D
246 /**\r
247 * Return a new network object with the new connection details\r
248 */\r
2a27c998 249 this.newConnection = function(connection_info, callback_fn) {\r
d6eec6ed 250 var that = this;\r
2a27c998 251\r
d6eec6ed 252 this.makeIrcConnection(connection_info, function(err, server_num) {\r
c966123a
D
253 var connection;\r
254\r
2a27c998 255 if (!err) {\r
f2bb5380 256 if (!_kiwi.app.connections.getByConnectionId(server_num)){\r
d6eec6ed
D
257 var inf = {\r
258 connection_id: server_num,\r
259 nick: connection_info.nick,\r
260 address: connection_info.host,\r
261 port: connection_info.port,\r
262 ssl: connection_info.ssl,\r
263 password: connection_info.password\r
264 };\r
265 connection = new _kiwi.model.Network(inf);\r
c966123a 266 _kiwi.app.connections.add(connection);\r
2a27c998
D
267 }\r
268\r
d6eec6ed 269 console.log("_kiwi.gateway.socket.on('connect')", connection);\r
c966123a 270 callback_fn && callback_fn(err, connection);\r
09c26937 271\r
2a27c998
D
272 } else {\r
273 console.log("_kiwi.gateway.socket.on('error')", {reason: err});\r
274 callback_fn && callback_fn(err);\r
275 }\r
276 });\r
277 };\r
278\r
d6eec6ed
D
279\r
280 /**\r
281 * Make a new IRC connection and return its connection ID\r
282 */\r
283 this.makeIrcConnection = function(connection_info, callback_fn) {\r
284 var server_info = {\r
285 command: 'connect',\r
286 nick: connection_info.nick,\r
287 hostname: connection_info.host,\r
288 port: connection_info.port,\r
289 ssl: connection_info.ssl,\r
290 password: connection_info.password\r
291 };\r
292\r
cc54c0a1
D
293 // A few optional parameters\r
294 if (connection_info.options.encoding)\r
295 server_info.encoding = connection_info.options.encoding;\r
296\r
d6eec6ed
D
297 this.socket.emit('kiwi', server_info, function (err, server_num) {\r
298 if (!err) {\r
299 callback_fn && callback_fn(err, server_num);\r
300\r
301 } else {\r
302 callback_fn && callback_fn(err);\r
303 }\r
304 });\r
305 };\r
306\r
307\r
696a66f8
D
308 this.isConnected = function () {\r
309 return this.socket.socket.connected;\r
310 };\r
311\r
312\r
d2d91c10
D
313\r
314 this.parseKiwi = function (command, data) {\r
a1629e09
D
315 this.trigger('kiwi:' + command, data);\r
316 this.trigger('kiwi', data);\r
d2d91c10 317 };\r
696a66f8
D
318 /*\r
319 Events:\r
320 msg\r
321 action\r
322 server_connect\r
323 options\r
324 motd\r
325 notice\r
326 userlist\r
327 nick\r
328 join\r
329 topic\r
330 part\r
331 kick\r
332 quit\r
333 whois\r
334 syncchannel_redirect\r
335 debug\r
336 */\r
337 /**\r
338 * Parses the response from the server\r
339 */\r
340 this.parse = function (command, data) {\r
d2d91c10 341 //console.log('gateway event', command, data);\r
ce13508b 342\r
696a66f8 343 if (command !== undefined) {\r
696a66f8
D
344 switch (command) {\r
345 case 'options':\r
346 $.each(data.options, function (name, value) {\r
347 switch (name) {\r
348 case 'CHANTYPES':\r
d9e06c6e 349 that.set('channel_prefix', value.join(''));\r
696a66f8
D
350 break;\r
351 case 'NETWORK':\r
352 that.set('name', value);\r
353 break;\r
354 case 'PREFIX':\r
355 that.set('user_prefixes', value);\r
356 break;\r
357 }\r
358 });\r
dff8edba 359 that.set('cap', data.cap);\r
696a66f8
D
360 break;\r
361\r
696a66f8
D
362 /*\r
363 case 'sync':\r
eaaf73b0
D
364 if (_kiwi.gateway.onSync && _kiwi.gateway.syncing) {\r
365 _kiwi.gateway.syncing = false;\r
366 _kiwi.gateway.onSync(item);\r
696a66f8
D
367 }\r
368 break;\r
369 */\r
370\r
371 case 'kiwi':\r
eaaf73b0 372 this.emit('_kiwi.' + data.namespace, data.data);\r
696a66f8
D
373 break;\r
374 }\r
375 }\r
ce13508b
D
376\r
377\r
378 if (typeof data.server !== 'undefined') {\r
379 that.trigger('connection:' + data.server.toString(), {\r
380 event_name: command,\r
381 event_data: data\r
382 });\r
383 }\r
fc9b83de
D
384\r
385 // Trigger the global events (Mainly legacy now)\r
386 that.trigger('on' + command, data);\r
696a66f8
D
387 };\r
388\r
389 /**\r
390 * Sends data to the server\r
391 * @private\r
392 * @param {Object} data The data to send\r
393 * @param {Function} callback A callback function\r
394 */\r
e7d65587
D
395 this.sendData = function (connection_id, data, callback) {\r
396 if (typeof connection_id === 'undefined' || connection_id === null)\r
397 connection_id = _kiwi.app.connections.active_connection.get('connection_id');\r
398\r
2ffd1291 399 var data_buffer = {\r
e7d65587 400 server: connection_id,\r
2ffd1291
D
401 data: JSON.stringify(data)\r
402 };\r
2ffd1291 403 this.socket.emit('irc', data_buffer, callback);\r
696a66f8
D
404 };\r
405\r
406 /**\r
407 * Sends a PRIVMSG message\r
408 * @param {String} target The target of the message (e.g. a channel or nick)\r
409 * @param {String} msg The message to send\r
410 * @param {Function} callback A callback function\r
411 */\r
e7d65587 412 this.privmsg = function (connection_id, target, msg, callback) {\r
696a66f8
D
413 var data = {\r
414 method: 'privmsg',\r
415 args: {\r
416 target: target,\r
417 msg: msg\r
418 }\r
419 };\r
420\r
e7d65587 421 this.sendData(connection_id, data, callback);\r
696a66f8
D
422 };\r
423\r
424 /**\r
425 * Sends a NOTICE message\r
426 * @param {String} target The target of the message (e.g. a channel or nick)\r
427 * @param {String} msg The message to send\r
428 * @param {Function} callback A callback function\r
429 */\r
e7d65587 430 this.notice = function (connection_id, target, msg, callback) {\r
696a66f8
D
431 var data = {\r
432 method: 'notice',\r
433 args: {\r
434 target: target,\r
435 msg: msg\r
436 }\r
437 };\r
438\r
e7d65587 439 this.sendData(connection_id, data, callback);\r
696a66f8
D
440 };\r
441\r
442 /**\r
443 * Sends a CTCP message\r
444 * @param {Boolean} request Indicates whether this is a CTCP request (true) or reply (false)\r
445 * @param {String} type The type of CTCP message, e.g. 'VERSION', 'TIME', 'PING' etc.\r
446 * @param {String} target The target of the message, e.g a channel or nick\r
447 * @param {String} params Additional paramaters\r
448 * @param {Function} callback A callback function\r
449 */\r
e7d65587 450 this.ctcp = function (connection_id, request, type, target, params, callback) {\r
696a66f8
D
451 var data = {\r
452 method: 'ctcp',\r
453 args: {\r
454 request: request,\r
455 type: type,\r
456 target: target,\r
457 params: params\r
458 }\r
459 };\r
460\r
e7d65587 461 this.sendData(connection_id, data, callback);\r
696a66f8
D
462 };\r
463\r
464 /**\r
465 * @param {String} target The target of the message (e.g. a channel or nick)\r
466 * @param {String} msg The message to send\r
467 * @param {Function} callback A callback function\r
468 */\r
e7d65587 469 this.action = function (connection_id, target, msg, callback) {\r
11177392 470 this.ctcp(connection_id, true, 'ACTION', target, msg, callback);\r
696a66f8
D
471 };\r
472\r
473 /**\r
474 * Joins a channel\r
475 * @param {String} channel The channel to join\r
476 * @param {String} key The key to the channel\r
477 * @param {Function} callback A callback function\r
478 */\r
e7d65587 479 this.join = function (connection_id, channel, key, callback) {\r
696a66f8
D
480 var data = {\r
481 method: 'join',\r
482 args: {\r
483 channel: channel,\r
484 key: key\r
485 }\r
486 };\r
487\r
e7d65587 488 this.sendData(connection_id, data, callback);\r
696a66f8
D
489 };\r
490\r
491 /**\r
492 * Leaves a channel\r
493 * @param {String} channel The channel to part\r
494 * @param {Function} callback A callback function\r
495 */\r
e7d65587 496 this.part = function (connection_id, channel, callback) {\r
696a66f8
D
497 var data = {\r
498 method: 'part',\r
499 args: {\r
500 channel: channel\r
501 }\r
502 };\r
503\r
e7d65587 504 this.sendData(connection_id, data, callback);\r
696a66f8
D
505 };\r
506\r
507 /**\r
508 * Queries or modifies a channell topic\r
509 * @param {String} channel The channel to query or modify\r
510 * @param {String} new_topic The new topic to set\r
511 * @param {Function} callback A callback function\r
512 */\r
e7d65587 513 this.topic = function (connection_id, channel, new_topic, callback) {\r
696a66f8
D
514 var data = {\r
515 method: 'topic',\r
516 args: {\r
517 channel: channel,\r
518 topic: new_topic\r
519 }\r
520 };\r
521\r
e7d65587 522 this.sendData(connection_id, data, callback);\r
696a66f8
D
523 };\r
524\r
525 /**\r
526 * Kicks a user from a channel\r
527 * @param {String} channel The channel to kick the user from\r
528 * @param {String} nick The nick of the user to kick\r
529 * @param {String} reason The reason for kicking the user\r
530 * @param {Function} callback A callback function\r
531 */\r
e7d65587 532 this.kick = function (connection_id, channel, nick, reason, callback) {\r
696a66f8
D
533 var data = {\r
534 method: 'kick',\r
535 args: {\r
536 channel: channel,\r
537 nick: nick,\r
538 reason: reason\r
539 }\r
540 };\r
541\r
e7d65587 542 this.sendData(connection_id, data, callback);\r
696a66f8
D
543 };\r
544\r
545 /**\r
546 * Disconnects us from the server\r
547 * @param {String} msg The quit message to send to the IRC server\r
548 * @param {Function} callback A callback function\r
549 */\r
e7d65587 550 this.quit = function (connection_id, msg, callback) {\r
696a66f8
D
551 msg = msg || "";\r
552 var data = {\r
553 method: 'quit',\r
554 args: {\r
555 message: msg\r
556 }\r
557 };\r
558\r
e7d65587 559 this.sendData(connection_id, data, callback);\r
696a66f8
D
560 };\r
561\r
562 /**\r
563 * Sends a string unmodified to the IRC server\r
564 * @param {String} data The data to send to the IRC server\r
565 * @param {Function} callback A callback function\r
566 */\r
e7d65587 567 this.raw = function (connection_id, data, callback) {\r
696a66f8
D
568 data = {\r
569 method: 'raw',\r
570 args: {\r
571 data: data\r
572 }\r
573 };\r
574\r
e7d65587 575 this.sendData(connection_id, data, callback);\r
696a66f8
D
576 };\r
577\r
578 /**\r
579 * Changes our nickname\r
580 * @param {String} new_nick Our new nickname\r
581 * @param {Function} callback A callback function\r
582 */\r
e7d65587 583 this.changeNick = function (connection_id, new_nick, callback) {\r
696a66f8
D
584 var data = {\r
585 method: 'nick',\r
586 args: {\r
587 nick: new_nick\r
588 }\r
589 };\r
590\r
e7d65587 591 this.sendData(connection_id, data, callback);\r
696a66f8
D
592 };\r
593\r
d1b3e8b3
VDF
594 /**\r
595 * Sends ENCODING change request to server.\r
596 * @param {String} new_encoding The new proposed encode\r
597 * @param {Fucntion} callback A callback function\r
598 */\r
599 this.setEncoding = function (connection_id, new_encoding, callback) {\r
65c5a477
VDF
600 var data = {\r
601 method: 'encoding',\r
602 args: {\r
603 encoding: new_encoding\r
604 }\r
605 };\r
606 this.sendData(connection_id, data, callback);\r
d1b3e8b3
VDF
607 };\r
608\r
696a66f8
D
609 /**\r
610 * Sends data to a fellow Kiwi IRC user\r
611 * @param {String} target The nick of the Kiwi IRC user to send to\r
612 * @param {String} data The data to send\r
613 * @param {Function} callback A callback function\r
614 */\r
615 this.kiwi = function (target, data, callback) {\r
616 data = {\r
617 method: 'kiwi',\r
618 args: {\r
619 target: target,\r
620 data: data\r
621 }\r
622 };\r
623\r
624 this.sendData(data, callback);\r
625 };\r
626\r
f3091277
D
627 // Check a nick alongside our ignore list\r
628 this.isNickIgnored = function (nick) {\r
629 var idx, list = this.get('ignore_list');\r
630 var pattern, regex;\r
631\r
632 for (idx = 0; idx < list.length; idx++) {\r
633 pattern = list[idx].replace(/([.+^$[\]\\(){}|-])/g, "\\$1")\r
634 .replace('*', '.*')\r
635 .replace('?', '.');\r
636\r
637 regex = new RegExp(pattern, 'i');\r
638 if (regex.test(nick)) return true;\r
639 }\r
640\r
641 return false;\r
4047ee2b 642 };\r
f3091277
D
643\r
644\r
696a66f8 645 return new (Backbone.Model.extend(this))(arguments);\r
d1b3e8b3 646};\r