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