EventComponent comments
[KiwiIRC.git] / client / src / app.js
1 // Holds anything kiwi client specific (ie. front, gateway, _kiwi.plugs..)
2 /**
3 * @namespace
4 */
5 var _kiwi = {};
6
7 _kiwi.model = {};
8 _kiwi.view = {};
9 _kiwi.applets = {};
10
11
12 /**
13 * A global container for third party access
14 * Will be used to access a limited subset of kiwi functionality
15 * and data (think: plugins)
16 */
17 _kiwi.global = {
18 build_version: '', // Kiwi IRC version this is built from (Set from index.html)
19 settings: undefined, // Instance of _kiwi.model.DataStore
20 plugins: undefined, // Instance of _kiwi.model.PluginManager
21 events: undefined, // Instance of PluginInterface
22 utils: {}, // TODO: Re-usable methods
23
24 addMediaMessageType: function(match, buildHtml) {
25 _kiwi.view.MediaMessage.addType(match, buildHtml);
26 },
27
28 // Event managers for plugins
29 components: {
30 EventComponent: function(event_source, proxy_event_name) {
31 /*
32 * proxyEvent() listens for events then re-triggers them on its own
33 * event emitter. Why? So we can .off() on this emitter without
34 * effecting the source of events. Handy for plugins that we don't
35 * trust meddling with the core events.
36 *
37 * If listening for 'all' events the arguments are as follows:
38 * 1. Name of the triggered event
39 * 2. The event data
40 * For all other events, we only have one argument:
41 * 1. The event data
42 *
43 * When this is used via `new kiwi.components.Network()`, this listens
44 * for 'all' events so the first argument is the event name which is
45 * the connection ID. We don't want to re-trigger this event name so
46 * we need to juggle the arguments to find the real event name we want
47 * to emit.
48 */
49 function proxyEvent(event_name, event_data) {
50 if (proxy_event_name == 'all') {
51 event_name = event_data.event_name;
52 event_data = event_data.event_data;
53 } else {
54 event_data = event_name.event_data;
55 event_name = event_name.event_name;
56 }
57
58 this.trigger(event_name, event_data);
59 }
60
61 // The event we are to proxy
62 proxy_event_name = proxy_event_name || 'all';
63
64
65 _.extend(this, Backbone.Events);
66 this._source = event_source;
67
68 // Proxy the events to this dispatcher
69 event_source.on(proxy_event_name, proxyEvent, this);
70
71 // Clean up this object
72 this.dispose = function () {
73 event_source.off(proxy_event_name, proxyEvent);
74 this.off();
75 delete this.event_source;
76 };
77 },
78
79 Network: function(connection_id) {
80 var connection_event;
81
82 if (typeof connection_id !== 'undefined') {
83 connection_event = 'connection:' + connection_id.toString();
84 }
85
86 var obj = new this.EventComponent(_kiwi.gateway, connection_event);
87 var funcs = {
88 kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',
89 part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',
90 ctcpRequest: 'ctcpRequest', ctcpResponse: 'ctcpResponse',
91 notice: 'notice', msg: 'privmsg', changeNick: 'changeNick',
92 channelInfo: 'channelInfo', mode: 'mode'
93 };
94
95 // Proxy each gateway method
96 _.each(funcs, function(gateway_fn, func_name) {
97 obj[func_name] = function() {
98 var fn_name = gateway_fn;
99
100 // Add connection_id to the argument list
101 var args = Array.prototype.slice.call(arguments, 0);
102 args.unshift(connection_id);
103
104 // Call the gateway function on behalf of this connection
105 return _kiwi.gateway[fn_name].apply(_kiwi.gateway, args);
106 };
107 });
108
109 return obj;
110 },
111
112 ControlInput: function() {
113 var obj = new this.EventComponent(_kiwi.app.controlbox);
114 var funcs = {
115 run: 'processInput', addPluginIcon: 'addPluginIcon'
116 };
117
118 _.each(funcs, function(controlbox_fn, func_name) {
119 obj[func_name] = function() {
120 var fn_name = controlbox_fn;
121 return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);
122 };
123 });
124
125 return obj;
126 }
127 },
128
129 // Entry point to start the kiwi application
130 init: function (opts, callback) {
131 var jobs, locale, localeLoaded, textThemeLoaded, text_theme;
132 opts = opts || {};
133
134 jobs = new JobManager();
135 jobs.onFinish(function(locale, s, xhr) {
136 _kiwi.app = new _kiwi.model.Application(opts);
137
138 // Start the client up
139 _kiwi.app.initializeInterfaces();
140
141 // Event emitter to let plugins interface with parts of kiwi
142 _kiwi.global.events = new PluginInterface();
143
144 // Now everything has started up, load the plugin manager for third party plugins
145 _kiwi.global.plugins = new _kiwi.model.PluginManager();
146
147 callback();
148 });
149
150 textThemeLoaded = function(text_theme, s, xhr) {
151 opts.text_theme = text_theme;
152
153 jobs.finishJob('load_text_theme');
154 };
155
156 localeLoaded = function(locale, s, xhr) {
157 if (locale) {
158 _kiwi.global.i18n = new Jed(locale);
159 } else {
160 _kiwi.global.i18n = new Jed();
161 }
162
163 jobs.finishJob('load_locale');
164 };
165
166 // Set up the settings datastore
167 _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');
168 _kiwi.global.settings.load();
169
170 // Set the window title
171 window.document.title = opts.server_settings.client.window_title || 'Kiwi IRC';
172
173 jobs.registerJob('load_locale');
174 locale = _kiwi.global.settings.get('locale');
175 if (!locale) {
176 $.getJSON(opts.base_path + '/assets/locales/magic.json', localeLoaded);
177 } else {
178 $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', localeLoaded);
179 }
180
181 jobs.registerJob('load_text_theme');
182 text_theme = opts.server_settings.client.settings.text_theme || 'default';
183 $.getJSON(opts.base_path + '/assets/text_themes/' + text_theme + '.json', textThemeLoaded);
184 },
185
186 start: function() {
187 _kiwi.app.showStartup();
188 },
189
190 // Allow plugins to change the startup applet
191 registerStartupApplet: function(startup_applet_name) {
192 _kiwi.app.startup_applet_name = startup_applet_name;
193 },
194
195 /**
196 * Open a new IRC connection
197 * @param {Object} connection_details {nick, host, port, ssl, password, options}
198 * @param {Function} callback function(err, network){}
199 */
200 newIrcConnection: function(connection_details, callback) {
201 _kiwi.gateway.newConnection(connection_details, callback);
202 },
203
204
205 /**
206 * Taking settings from the server and URL, extract the default server/channel/nick settings
207 */
208 defaultServerSettings: function () {
209 var parts;
210 var defaults = {
211 nick: '',
212 server: '',
213 port: 6667,
214 ssl: false,
215 channel: '',
216 channel_key: ''
217 };
218 var uricheck;
219
220
221 /**
222 * Get any settings set by the server
223 * These settings may be changed in the server selection dialog or via URL parameters
224 */
225 if (_kiwi.app.server_settings.client) {
226 if (_kiwi.app.server_settings.client.nick)
227 defaults.nick = _kiwi.app.server_settings.client.nick;
228
229 if (_kiwi.app.server_settings.client.server)
230 defaults.server = _kiwi.app.server_settings.client.server;
231
232 if (_kiwi.app.server_settings.client.port)
233 defaults.port = _kiwi.app.server_settings.client.port;
234
235 if (_kiwi.app.server_settings.client.ssl)
236 defaults.ssl = _kiwi.app.server_settings.client.ssl;
237
238 if (_kiwi.app.server_settings.client.channel)
239 defaults.channel = _kiwi.app.server_settings.client.channel;
240
241 if (_kiwi.app.server_settings.client.channel_key)
242 defaults.channel_key = _kiwi.app.server_settings.client.channel_key;
243 }
244
245
246
247 /**
248 * Get any settings passed in the URL
249 * These settings may be changed in the server selection dialog
250 */
251
252 // Any query parameters first
253 if (getQueryVariable('nick'))
254 defaults.nick = getQueryVariable('nick');
255
256 if (window.location.hash)
257 defaults.channel = window.location.hash;
258
259
260 // Process the URL part by part, extracting as we go
261 parts = window.location.pathname.toString().replace(_kiwi.app.get('base_path'), '').split('/');
262
263 if (parts.length > 0) {
264 parts.shift();
265
266 if (parts.length > 0 && parts[0]) {
267 // Check to see if we're dealing with an irc: uri, or whether we need to extract the server/channel info from the HTTP URL path.
268 uricheck = parts[0].substr(0, 7).toLowerCase();
269 if ((uricheck === 'ircs%3a') || (uricheck.substr(0,6) === 'irc%3a')) {
270 parts[0] = decodeURIComponent(parts[0]);
271 // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
272 uricheck = /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts[0]);
273 /*
274 uricheck[1] = ssl (optional)
275 uricheck[2] = host
276 uricheck[3] = port (optional)
277 uricheck[4] = channel (optional)
278 uricheck[5] = channel key (optional, channel must also be set)
279 */
280 if (uricheck) {
281 if (typeof uricheck[1] !== 'undefined') {
282 defaults.ssl = true;
283 if (defaults.port === 6667) {
284 defaults.port = 6697;
285 }
286 }
287 defaults.server = uricheck[2];
288 if (typeof uricheck[3] !== 'undefined') {
289 defaults.port = uricheck[3];
290 }
291 if (typeof uricheck[4] !== 'undefined') {
292 defaults.channel = '#' + uricheck[4];
293 if (typeof uricheck[5] !== 'undefined') {
294 defaults.channel_key = uricheck[5];
295 }
296 }
297 }
298 parts = [];
299 } else {
300 // Extract the port+ssl if we find one
301 if (parts[0].search(/:/) > 0) {
302 defaults.port = parts[0].substring(parts[0].search(/:/) + 1);
303 defaults.server = parts[0].substring(0, parts[0].search(/:/));
304 if (defaults.port[0] === '+') {
305 defaults.port = parseInt(defaults.port.substring(1), 10);
306 defaults.ssl = true;
307 } else {
308 defaults.ssl = false;
309 }
310
311 } else {
312 defaults.server = parts[0];
313 }
314
315 parts.shift();
316 }
317 }
318
319 if (parts.length > 0 && parts[0]) {
320 defaults.channel = '#' + parts[0];
321 parts.shift();
322 }
323 }
324
325 // If any settings have been given by the server.. override any auto detected settings
326 /**
327 * Get any server restrictions as set in the server config
328 * These settings can not be changed in the server selection dialog
329 */
330 if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {
331 if (_kiwi.app.server_settings.connection.server) {
332 defaults.server = _kiwi.app.server_settings.connection.server;
333 }
334
335 if (_kiwi.app.server_settings.connection.port) {
336 defaults.port = _kiwi.app.server_settings.connection.port;
337 }
338
339 if (_kiwi.app.server_settings.connection.ssl) {
340 defaults.ssl = _kiwi.app.server_settings.connection.ssl;
341 }
342
343 if (_kiwi.app.server_settings.connection.channel) {
344 defaults.channel = _kiwi.app.server_settings.connection.channel;
345 }
346
347 if (_kiwi.app.server_settings.connection.channel_key) {
348 defaults.channel_key = _kiwi.app.server_settings.connection.channel_key;
349 }
350
351 if (_kiwi.app.server_settings.connection.nick) {
352 defaults.nick = _kiwi.app.server_settings.connection.nick;
353 }
354 }
355
356 // Set any random numbers if needed
357 defaults.nick = defaults.nick.replace('?', Math.floor(Math.random() * 100000).toString());
358
359 if (getQueryVariable('encoding'))
360 defaults.encoding = getQueryVariable('encoding');
361
362 return defaults;
363 },
364 };
365
366
367
368 // If within a closure, expose the kiwi globals
369 if (typeof global !== 'undefined') {
370 global.kiwi = _kiwi.global;
371 } else {
372 // Not within a closure so set a var in the current scope
373 var kiwi = _kiwi.global;
374 }