]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/ui/baseui.js
Merge.
[irc/quakenet/qwebirc.git] / js / ui / baseui.js
1 qwebirc.ui.WINDOW_STATUS = 0x01;
2 qwebirc.ui.WINDOW_QUERY = 0x02;
3 qwebirc.ui.WINDOW_CHANNEL = 0x04;
4 qwebirc.ui.WINDOW_CUSTOM = 0x08;
5 qwebirc.ui.WINDOW_CONNECT = 0x10;
6 qwebirc.ui.WINDOW_MESSAGES = 0x20;
7
8 qwebirc.ui.CUSTOM_CLIENT = "custom";
9
10 qwebirc.ui.BaseUI = new Class({
11 Implements: [Events],
12 initialize: function(parentElement, windowClass, uiName, options) {
13 this.options = options;
14
15 this.windows = new QHash();
16 this.clients = new QHash();
17 this.windows.put(qwebirc.ui.CUSTOM_CLIENT, new QHash());
18 this.windowArray = [];
19 this.windowClass = windowClass;
20 this.parentElement = parentElement;
21 this.parentElement.addClass("qwebirc");
22 this.parentElement.addClass("qwebirc-" + uiName);
23 this.firstClient = false;
24 this.commandhistory = new qwebirc.irc.CommandHistory();
25 this.clientId = 0;
26
27 this.windowFocused = true;
28
29 if(Browser.Engine.trident) {
30 var checkFocus = function() {
31 var hasFocus = document.hasFocus();
32 if(hasFocus != this.windowFocused) {
33 this.windowFocused = hasFocus;
34 this.focusChange(hasFocus);
35 }
36 }
37
38 checkFocus.periodical(100, this);
39 } else {
40 var blur = function() { if(this.windowFocused) { this.windowFocused = false; this.focusChange(false); } }.bind(this);
41 var focus = function() { if(!this.windowFocused) { this.windowFocused = true; this.focusChange(true); } }.bind(this);
42
43 /* firefox requires both */
44
45 document.addEvent("blur", blur);
46 window.addEvent("blur", blur);
47 document.addEvent("focus", focus);
48 window.addEvent("focus", focus);
49 }
50
51 qwebirc.util.__log = function(x) {
52 if(QWEBIRC_DEBUG) {
53 if(typeof console != "undefined")
54 console.log(x);
55 this.getActiveWindow().addLine(null, x);
56 }
57 }.bind(this);
58 },
59 newClient: function(client) {
60 client.id = String(this.clientId++);
61 client.hilightController = new qwebirc.ui.HilightController(client);
62 client.addEvent("signedOn", function() {
63 this.poller = new qwebirc.xdomain.Poller(this.oobMessage.bind(this));
64 this.fireEvent("signedOn", client);
65 }.bind(this));
66 this.windows.put(client.id, new QHash());
67 this.clients.put(client.id, client);
68 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
69 this.selectWindow(w);
70 if(!this.firstClient) {
71 this.firstClient = true;
72 w.addLine("", "qwebirc v" + qwebirc.VERSION);
73 w.addLine("", "Copyright (C) 2008-2014 Chris Porter and the qwebirc project.");
74 w.addLine("", "http://www.qwebirc.org");
75 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
76 }
77 return w;
78 },
79 getClientId: function(client) {
80 if(client == qwebirc.ui.CUSTOM_CLIENT) {
81 return qwebirc.ui.CUSTOM_CLIENT;
82 } else {
83 return client.id;
84 }
85 },
86 getWindowIdentifier: function(client, type, name) {
87 if(type == qwebirc.ui.WINDOW_MESSAGES)
88 return "-M";
89 if(type == qwebirc.ui.WINDOW_STATUS)
90 return "";
91
92 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
93 return "_" + name;
94
95 return "_" + client.toIRCLower(name);
96 },
97 newWindow: function(client, type, name) {
98 var w = this.getWindow(client, type, name);
99 if($defined(w))
100 return w;
101
102 var wId = this.getWindowIdentifier(client, type, name);
103 var w = new this.windowClass(this, client, type, name, wId);
104 this.windows.get(this.getClientId(client)).put(wId, w);
105 this.windowArray.push(w);
106
107 return w;
108 },
109 getWindow: function(client, type, name) {
110 var c = this.windows.get(this.getClientId(client));
111 if(!$defined(c))
112 return null;
113
114 return c.get(this.getWindowIdentifier(client, type, name));
115 },
116 getActiveWindow: function() {
117 return this.active;
118 },
119 getActiveIRCWindow: function(client) {
120 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
121 return this.windows.get(this.getClientId(client)).get(this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS));
122 } else {
123 return this.active;
124 }
125 },
126 __setActiveWindow: function(window) {
127 this.active = window;
128 },
129 renameWindow: function(window, name) {
130 if(this.getWindow(window.client, window.type, name))
131 return null;
132
133 var clientId = this.getClientId(window.client);
134 var index = this.windowArray.indexOf(window);
135 if(index == -1)
136 return null;
137
138 this.windows.get(clientId).remove(window.identifier);
139
140 var window = this.windowArray[index];
141 window.name = name;
142 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
143
144 this.windows.get(clientId).put(window.identifier, this.windowArray[index]);
145
146 if(window.active)
147 this.updateTitle(window.name + " - " + this.options.appTitle);
148
149 window.rename(window.name);
150 return window;
151 },
152 selectWindow: function(window) {
153 if(this.active)
154 this.active.deselect();
155 window.select(); /* calls setActiveWindow */
156 this.updateTitle(window.name + " - " + this.options.appTitle);
157 },
158 updateTitle: function(text) {
159 document.title = text;
160 },
161 nextWindow: function(direction) {
162 if(this.windowArray.length == 0 || !this.active)
163 return;
164
165 if(!direction)
166 direction = 1;
167
168 var index = this.windowArray.indexOf(this.active);
169 if(index == -1)
170 return;
171
172 index = index + direction;
173 if(index < 0) {
174 index = this.windowArray.length - 1;
175 } else if(index >= this.windowArray.length) {
176 index = 0;
177 }
178
179 this.selectWindow(this.windowArray[index]);
180 },
181 prevWindow: function() {
182 this.nextWindow(-1);
183 },
184 __closed: function(window) {
185 if(window.active) {
186 this.active = undefined;
187 if(this.windowArray.length == 1) {
188 this.windowArray = [];
189 } else {
190 var index = this.windowArray.indexOf(window);
191 if(index == -1) {
192 return;
193 } else if(index == 0) {
194 this.selectWindow(this.windowArray[1]);
195 } else {
196 this.selectWindow(this.windowArray[index - 1]);
197 }
198 }
199 }
200
201 this.windowArray = this.windowArray.erase(window);
202 this.windows.get(this.getClientId(window.client)).remove(window.identifier);
203 },
204 /*
205 this shouldn't be called by overriding classes!
206 they should implement their own!
207 some form of user input MUST be received before an
208 IRC connection is made, else users are going to get
209 tricked into getting themselves glined
210 */
211 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
212 this.postInitialize();
213
214 this.addCustomWindow("Connect", qwebirc.ui.ConnectPane, "connectpane", {
215 initialNickname: initialNickname, initialChannels: initialChannels, autoConnect: autoConnect, networkName: this.options.networkName, callback: callback, autoNick: autoNick
216 }, qwebirc.ui.WINDOW_CONNECT);
217 },
218 focusChange: function(newValue) {
219 var window_ = this.getActiveWindow();
220 if($defined(window_))
221 window_.focusChange(newValue);
222 },
223 oobMessage: function(message) {
224 var c = message.splitMax(" ", 2);
225 if(c.length != 2)
226 return;
227
228 var command = c[0];
229 if(command != "CMD")
230 return;
231
232 var d = c[1].splitMax(" ", 2);
233 if(d.length != 2)
234 return;
235
236 var command = d[0];
237 var args = d[1];
238 if(command == "SAY") {
239 var w = this.getActiveIRCWindow();
240 if($defined(w) && (w.type == qwebirc.ui.WINDOW_CHANNEL || w.type == qwebirc.ui.WINDOW_QUERY)) {
241 w.client.exec("/SAY " + args);
242 return;
243 }
244 }
245 }
246 });
247
248 qwebirc.ui.StandardUI = new Class({
249 Extends: qwebirc.ui.BaseUI,
250 UICommands: qwebirc.ui.UI_COMMANDS,
251 initialize: function(parentElement, windowClass, uiName, options) {
252 this.parent(parentElement, windowClass, uiName, options);
253
254 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
255 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
256 this.customWindows = new QHash();
257
258 this.__styleValues = {hue: this.uiOptions.STYLE_HUE, saturation: 0, lightness: 0, textHue: this.uiOptions.STYLE_HUE, textSaturation: 0, textLightness: 0};
259 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
260 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
261 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
262 if($defined(this.options.thue)) this.__styleValues.textHue = this.options.thue;
263 if($defined(this.options.tsaturation)) this.__styleValues.textSaturation = this.options.tsaturation;
264 if($defined(this.options.tlightness)) this.__styleValues.textLightness = this.options.tlightness;
265
266 document.addEvent("keydown", this.__handleHotkey.bind(this));
267 },
268 __handleHotkey: function(x) {
269 var success = false;
270 if(!x.alt || x.control) {
271 if((x.key == "backspace" || x.key == "/") && !this.getInputFocused(x)) {
272 success = true;
273 }
274 } else if(x.key == "a" || x.key == "A") {
275 var highestNum = 0;
276 var highestIndex = -1;
277 success = true;
278
279 for(var i=0;i<this.windowArray.length;i++) {
280 var h = this.windowArray[i].hilighted;
281 if(h > highestNum) {
282 highestIndex = i;
283 highestNum = h;
284 }
285 }
286 if(highestIndex > -1)
287 this.selectWindow(this.windowArray[highestIndex]);
288 } else if(x.key >= '0' && x.key <= '9') {
289 success = true;
290
291 number = x.key - '0';
292 if(number == 0)
293 number = 10
294
295 number = number - 1;
296
297 if(number >= this.windowArray.length)
298 return;
299
300 this.selectWindow(this.windowArray[number]);
301 } else if(x.key == "left") {
302 this.prevWindow();
303 success = true;
304 } else if(x.key == "right") {
305 this.nextWindow();
306 success = true;
307 }
308 if(success) {
309 new Event(x).stop();
310 x.preventDefault();
311 }
312 },
313 getInputFocused: function(x) {
314 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
315 return false;
316 return true;
317 },
318 newCustomWindow: function(name, select, type) {
319 if(!type)
320 type = qwebirc.ui.WINDOW_CUSTOM;
321
322 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
323 w.addEvent("close", function(w) {
324 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
325 }.bind(this));
326
327 if(select)
328 this.selectWindow(w);
329
330 return w;
331 },
332 addCustomWindow: function(windowName, class_, cssClass, options, type) {
333 if(!$defined(options))
334 options = {};
335
336 if(this.customWindows.contains(windowName)) {
337 this.selectWindow(this.customWindows.get(windowName));
338 return;
339 }
340
341 var d = this.newCustomWindow(windowName, true, type);
342 this.customWindows.put(windowName, d);
343
344 d.addEvent("close", function() {
345 this.customWindows.remove(windowName);
346 }.bind(this));
347
348 if(cssClass)
349 d.lines.addClass("qwebirc-" + cssClass);
350
351 var ew = new class_(d.lines, options);
352 ew.addEvent("close", function() {
353 d.close();
354 }.bind(this));
355
356 d.setSubWindow(ew);
357 },
358 embeddedWindow: function() {
359 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
360 this.optionsWindow();
361 }.bind(this)});
362 },
363 optionsWindow: function() {
364 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
365 },
366 aboutWindow: function() {
367 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
368 },
369 privacyWindow: function() {
370 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
371 },
372 feedbackWindow: function() {
373 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
374 },
375 helpWindow: function() {
376 this.addCustomWindow("Help!", qwebirc.ui.HelpPane, "helppane", this.uiOptions);
377 },
378 urlDispatcher: function(name, window) {
379 if(name == "embedded")
380 return ["a", this.embeddedWindow.bind(this)];
381
382 if(name == "options")
383 return ["a", this.optionsWindow.bind(this)];
384
385 /* doesn't really belong here */
386 if(name == "whois") {
387 return ["span", function(nick) {
388 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
389 window.client.exec("/QUERY " + nick);
390 } else {
391 window.client.exec("/WHOIS " + nick);
392 }
393 }.bind(this)];
394 }
395
396 return null;
397 },
398 tabComplete: function(element) {
399 this.tabCompleter.tabComplete(element);
400 },
401 resetTabComplete: function() {
402 this.tabCompleter.reset();
403 },
404 setModifiableStylesheet: function(name) {
405 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + (QWEBIRC_DEBUG ? "debug/" : "") + name + qwebirc.FILE_SUFFIX + ".mcss");
406 this.setModifiableStylesheetValues({});
407 },
408 setModifiableStylesheetValues: function(values) {
409 for(var k in values)
410 this.__styleValues[k] = values[k];
411
412 if(!$defined(this.__styleSheet))
413 return;
414
415 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
416 var front = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
417
418 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
419 front = back;
420
421 var colours = {
422 back: back,
423 front: front
424 };
425
426 this.__styleSheet.set(function() {
427 var mode = arguments[0];
428 if(mode == "c") {
429 var t = colours[arguments[2]];
430 var x = new Color(arguments[1]);
431 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
432 if(c == "255,255,255") /* IE confuses white with transparent... */
433 c = "255,255,254";
434
435 return "rgb(" + c + ")";
436 } else if(mode == "o") {
437 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
438 }
439 }.bind(this));
440 }
441 });
442
443 qwebirc.ui.NotificationUI = new Class({
444 Extends: qwebirc.ui.StandardUI,
445 initialize: function(parentElement, windowClass, uiName, options) {
446 this.parent(parentElement, windowClass, uiName, options);
447
448 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
449 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
450 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
451
452 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
453 },
454 beep: function() {
455 this.__beeper.beep();
456 },
457 notify: function(title, message, callback) {
458 this.__beeper.beep();
459 this.__flasher.flash();
460 this.__notifier.notify(title, message, callback);
461 },
462 setBeepOnMention: function(value) {
463 if(value)
464 this.__beeper.soundInit();
465 },
466 setNotifications: function(value) {
467 this.__notifier.setEnabled(value);
468 },
469 updateTitle: function(text) {
470 if(this.__flasher.updateTitle(text))
471 this.parent(text);
472 },
473 focusChange: function(value) {
474 this.parent(value);
475 this.__flasher.focusChange(value);
476 this.__notifier.focusChange(value);
477 }
478 });
479
480 qwebirc.ui.QuakeNetUI = new Class({
481 Extends: qwebirc.ui.NotificationUI,
482 urlDispatcher: function(name, window) {
483 if(name == "qwhois") {
484 return ["span", function(auth) {
485 this.client.exec("/MSG Q whois #" + auth);
486 }.bind(window)];
487 }
488 return this.parent(name, window);
489 },
490 logout: function() {
491 if(!qwebirc.auth.loggedin())
492 return;
493 if(confirm("Log out?")) {
494 this.clients.each(function(k, v) {
495 v.quit("Logged out");
496 }, this);
497
498 /* HACK */
499 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
500 foo.delay(500);
501 }
502 }
503 });
504
505 qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
506
507 qwebirc.ui.RequestTransformHTML = function(options) {
508 var HREF_ELEMENTS = {
509 "IMG": 1
510 };
511
512 var update = options.update;
513 var onSuccess = options.onSuccess;
514
515 var fixUp = function(node) {
516 if(node.nodeType != 1)
517 return;
518
519 var tagName = node.nodeName.toUpperCase();
520 if(HREF_ELEMENTS[tagName]) {
521 var attr = node.getAttribute("transform_attr");
522 var value = node.getAttribute("transform_value");
523 if($defined(attr) && $defined(value)) {
524 node.removeAttribute("transform_attr");
525 node.removeAttribute("transform_value");
526 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
527 }
528 }
529
530 for(var i=0;i<node.childNodes.length;i++)
531 fixUp(node.childNodes[i]);
532 };
533
534 delete options["update"];
535 options.onSuccess = function(tree, elements, html, js) {
536 var container = new Element("div");
537 container.set("html", html);
538 fixUp(container);
539 update.empty();
540
541 while(container.childNodes.length > 0) {
542 var x = container.firstChild;
543 container.removeChild(x);
544 update.appendChild(x);
545 }
546 onSuccess();
547 };
548
549 return new Request.HTML(options);
550 };
551