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