Extracting client user commands from application.js
[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 event_name = event_data.event_name;
67 event_data = event_data.event_data;
68 } else {
69 event_data = event_name.event_data;
70 event_name = event_name.event_name;
71 }
72
73 this.trigger(event_name, event_data);
74 }
75
76 // The event we are to proxy
77 proxy_event_name = proxy_event_name || 'all';
78
79
80 _.extend(this, Backbone.Events);
81 this._source = event_source;
82
83 // Proxy the events to this dispatcher
84 event_source.on(proxy_event_name, proxyEvent, this);
85
86 // Clean up this object
87 this.dispose = function () {
88 event_source.off(proxy_event_name, proxyEvent);
89 this.off();
90 delete this.event_source;
91 };
92 },
93
94 Network: function(connection_id) {
95 var connection_event;
96
97 if (typeof connection_id !== 'undefined') {
98 connection_event = 'connection:' + connection_id.toString();
99 } else {
100 connection_event = 'connection';
101 }
102
103 var obj = new this.EventComponent(_kiwi.gateway, connection_event);
104 var funcs = {
105 kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',
106 part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',
107 ctcpRequest: 'ctcpRequest', ctcpResponse: 'ctcpResponse',
108 notice: 'notice', msg: 'privmsg', changeNick: 'changeNick',
109 channelInfo: 'channelInfo', mode: 'mode'
110 };
111
112 // Proxy each gateway method
113 _.each(funcs, function(gateway_fn, func_name) {
114 obj[func_name] = function() {
115 var fn_name = gateway_fn;
116
117 // Add connection_id to the argument list
118 var args = Array.prototype.slice.call(arguments, 0);
119 args.unshift(connection_id);
120
121 // Call the gateway function on behalf of this connection
122 return _kiwi.gateway[fn_name].apply(_kiwi.gateway, args);
123 };
124 });
125
126 return obj;
127 },
128
129 ControlInput: function() {
130 var obj = new this.EventComponent(_kiwi.app.controlbox);
131 var funcs = {
132 run: 'processInput', addPluginIcon: 'addPluginIcon'
133 };
134
135 _.each(funcs, function(controlbox_fn, func_name) {
136 obj[func_name] = function() {
137 var fn_name = controlbox_fn;
138 return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);
139 };
140 });
141
142 return obj;
143 }
144 },
145
146 // Entry point to start the kiwi application
147 init: function (opts, callback) {
148 var jobs, locale, localeLoaded, textThemeLoaded, text_theme;
149 opts = opts || {};
150
151 this.initUtils();
152
153 jobs = new JobManager();
154 jobs.onFinish(function(locale, s, xhr) {
155 _kiwi.app = new _kiwi.model.Application(opts);
156
157 // Start the client up
158 _kiwi.app.initializeInterfaces();
159
160 // Event emitter to let plugins interface with parts of kiwi
161 _kiwi.global.events = new PluginInterface();
162
163 // Now everything has started up, load the plugin manager for third party plugins
164 _kiwi.global.plugins = new _kiwi.model.PluginManager();
165
166 callback();
167 });
168
169 textThemeLoaded = function(text_theme, s, xhr) {
170 opts.text_theme = text_theme;
171
172 jobs.finishJob('load_text_theme');
173 };
174
175 localeLoaded = function(locale, s, xhr) {
176 if (locale) {
177 _kiwi.global.i18n = new Jed(locale);
178 } else {
179 _kiwi.global.i18n = new Jed();
180 }
181
182 jobs.finishJob('load_locale');
183 };
184
185 // Set up the settings datastore
186 _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');
187 _kiwi.global.settings.load();
188
189 // Set the window title
190 window.document.title = opts.server_settings.client.window_title || 'Kiwi IRC';
191
192 jobs.registerJob('load_locale');
193 locale = _kiwi.global.settings.get('locale');
194 if (!locale) {
195 $.getJSON(opts.base_path + '/assets/locales/magic.json', localeLoaded);
196 } else {
197 $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', localeLoaded);
198 }
199
200 jobs.registerJob('load_text_theme');
201 text_theme = opts.server_settings.client.settings.text_theme || 'default';
202 $.getJSON(opts.base_path + '/assets/text_themes/' + text_theme + '.json', textThemeLoaded);
203 },
204
205 start: function() {
206 _kiwi.app.showStartup();
207 },
208
209 // Allow plugins to change the startup applet
210 registerStartupApplet: function(startup_applet_name) {
211 _kiwi.app.startup_applet_name = startup_applet_name;
212 },
213
214 /**
215 * Open a new IRC connection
216 * @param {Object} connection_details {nick, host, port, ssl, password, options}
217 * @param {Function} callback function(err, network){}
218 */
219 newIrcConnection: function(connection_details, callback) {
220 _kiwi.gateway.newConnection(connection_details, callback);
221 },
222
223
224 /**
225 * Taking settings from the server and URL, extract the default server/channel/nick settings
226 */
227 defaultServerSettings: function () {
228 var parts;
229 var defaults = {
230 nick: '',
231 server: '',
232 port: 6667,
233 ssl: false,
234 channel: '',
235 channel_key: ''
236 };
237 var uricheck;
238
239
240 /**
241 * Get any settings set by the server
242 * These settings may be changed in the server selection dialog or via URL parameters
243 */
244 if (_kiwi.app.server_settings.client) {
245 if (_kiwi.app.server_settings.client.nick)
246 defaults.nick = _kiwi.app.server_settings.client.nick;
247
248 if (_kiwi.app.server_settings.client.server)
249 defaults.server = _kiwi.app.server_settings.client.server;
250
251 if (_kiwi.app.server_settings.client.port)
252 defaults.port = _kiwi.app.server_settings.client.port;
253
254 if (_kiwi.app.server_settings.client.ssl)
255 defaults.ssl = _kiwi.app.server_settings.client.ssl;
256
257 if (_kiwi.app.server_settings.client.channel)
258 defaults.channel = _kiwi.app.server_settings.client.channel;
259
260 if (_kiwi.app.server_settings.client.channel_key)
261 defaults.channel_key = _kiwi.app.server_settings.client.channel_key;
262 }
263
264
265
266 /**
267 * Get any settings passed in the URL
268 * These settings may be changed in the server selection dialog
269 */
270
271 // Any query parameters first
272 if (getQueryVariable('nick'))
273 defaults.nick = getQueryVariable('nick');
274
275 if (window.location.hash)
276 defaults.channel = window.location.hash;
277
278
279 // Process the URL part by part, extracting as we go
280 parts = window.location.pathname.toString().replace(_kiwi.app.get('base_path'), '').split('/');
281
282 if (parts.length > 0) {
283 parts.shift();
284
285 if (parts.length > 0 && parts[0]) {
286 // 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.
287 uricheck = parts[0].substr(0, 7).toLowerCase();
288 if ((uricheck === 'ircs%3a') || (uricheck.substr(0,6) === 'irc%3a')) {
289 parts[0] = decodeURIComponent(parts[0]);
290 // irc[s]://<host>[:<port>]/[<channel>[?<password>]]
291 uricheck = /^irc(s)?:(?:\/\/?)?([^:\/]+)(?::([0-9]+))?(?:(?:\/)([^\?]*)(?:(?:\?)(.*))?)?$/.exec(parts[0]);
292 /*
293 uricheck[1] = ssl (optional)
294 uricheck[2] = host
295 uricheck[3] = port (optional)
296 uricheck[4] = channel (optional)
297 uricheck[5] = channel key (optional, channel must also be set)
298 */
299 if (uricheck) {
300 if (typeof uricheck[1] !== 'undefined') {
301 defaults.ssl = true;
302 if (defaults.port === 6667) {
303 defaults.port = 6697;
304 }
305 }
306 defaults.server = uricheck[2];
307 if (typeof uricheck[3] !== 'undefined') {
308 defaults.port = uricheck[3];
309 }
310 if (typeof uricheck[4] !== 'undefined') {
311 defaults.channel = '#' + uricheck[4];
312 if (typeof uricheck[5] !== 'undefined') {
313 defaults.channel_key = uricheck[5];
314 }
315 }
316 }
317 parts = [];
318 } else {
319 // Extract the port+ssl if we find one
320 if (parts[0].search(/:/) > 0) {
321 defaults.port = parts[0].substring(parts[0].search(/:/) + 1);
322 defaults.server = parts[0].substring(0, parts[0].search(/:/));
323 if (defaults.port[0] === '+') {
324 defaults.port = parseInt(defaults.port.substring(1), 10);
325 defaults.ssl = true;
326 } else {
327 defaults.ssl = false;
328 }
329
330 } else {
331 defaults.server = parts[0];
332 }
333
334 parts.shift();
335 }
336 }
337
338 if (parts.length > 0 && parts[0]) {
339 defaults.channel = '#' + parts[0];
340 parts.shift();
341 }
342 }
343
344 // If any settings have been given by the server.. override any auto detected settings
345 /**
346 * Get any server restrictions as set in the server config
347 * These settings can not be changed in the server selection dialog
348 */
349 if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {
350 if (_kiwi.app.server_settings.connection.server) {
351 defaults.server = _kiwi.app.server_settings.connection.server;
352 }
353
354 if (_kiwi.app.server_settings.connection.port) {
355 defaults.port = _kiwi.app.server_settings.connection.port;
356 }
357
358 if (_kiwi.app.server_settings.connection.ssl) {
359 defaults.ssl = _kiwi.app.server_settings.connection.ssl;
360 }
361
362 if (_kiwi.app.server_settings.connection.channel) {
363 defaults.channel = _kiwi.app.server_settings.connection.channel;
364 }
365
366 if (_kiwi.app.server_settings.connection.channel_key) {
367 defaults.channel_key = _kiwi.app.server_settings.connection.channel_key;
368 }
369
370 if (_kiwi.app.server_settings.connection.nick) {
371 defaults.nick = _kiwi.app.server_settings.connection.nick;
372 }
373 }
374
375 // Set any random numbers if needed
376 defaults.nick = defaults.nick.replace('?', Math.floor(Math.random() * 100000).toString());
377
378 if (getQueryVariable('encoding'))
379 defaults.encoding = getQueryVariable('encoding');
380
381 return defaults;
382 },
383 };
384
385
386
387 // If within a closure, expose the kiwi globals
388 if (typeof global !== 'undefined') {
389 global.kiwi = _kiwi.global;
390 } else {
391 // Not within a closure so set a var in the current scope
392 var kiwi = _kiwi.global;
393 }