]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/ui/baseui.js
Remove QuakeNet specifics
[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, callback: callback, autoNick: autoNick,
217 uiOptions: this.options
218 }, qwebirc.ui.WINDOW_CONNECT);
219 },
220 focusChange: function(newValue) {
221 var window_ = this.getActiveWindow();
222 if($defined(window_))
223 window_.focusChange(newValue);
224 },
225 oobMessage: function(message) {
226 var c = message.splitMax(" ", 2);
227 if(c.length != 2)
228 return;
229
230 var command = c[0];
231 if(command != "CMD")
232 return;
233
234 var d = c[1].splitMax(" ", 2);
235 if(d.length != 2)
236 return;
237
238 var command = d[0];
239 var args = d[1];
240 if(command == "SAY") {
241 var w = this.getActiveIRCWindow();
242 if($defined(w) && (w.type == qwebirc.ui.WINDOW_CHANNEL || w.type == qwebirc.ui.WINDOW_QUERY)) {
243 w.client.exec("/SAY " + args);
244 return;
245 }
246 }
247 }
248 });
249
250 qwebirc.ui.StandardUI = new Class({
251 Extends: qwebirc.ui.BaseUI,
252 UICommands: qwebirc.ui.UI_COMMANDS,
253 initialize: function(parentElement, windowClass, uiName, options) {
254 this.parent(parentElement, windowClass, uiName, options);
255
256 this.__styleValues = {hue: qwebirc.ui.DEFAULT_HUE, saturation: 0, lightness: 0, textHue: null, textSaturation: null, textLightness: null};
257 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
258 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
259 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
260 this.customWindows = new QHash();
261
262 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
263 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
264 if($defined(this.options.tsaturation)) this.__styleValues.textSaturation = this.options.tsaturation;
265 if($defined(this.options.tlightness)) this.__styleValues.textLightness = this.options.tlightness;
266
267 if($defined(this.options.hue)) { /* overridden in url */
268 /* ugh... this will go away when we add proper options for hue/sat/light for text and background */
269 this.uiOptions.setValueByPrefix("STYLE_HUE", this.__styleValues.hue);
270 } else {
271 this.__styleValues.hue = this.uiOptions.STYLE_HUE; /* otherwise copy from serialised store */
272 }
273 this.__styleValues.textHue = $defined(this.options.thue) ? this.options.thue : this.__styleValues.hue;
274
275 document.addEvent("keydown", this.__handleHotkey.bind(this));
276 },
277 __handleHotkey: function(x) {
278 var success = false;
279 if(!x.alt && !x.control && !x.shift && !x.meta) {
280 if((x.key == "backspace" || x.key == "/") && !this.getInputFocused(x)) {
281 success = true;
282 }
283 } else if(!x.alt || x.control || x.meta) {
284 /* do nothing */
285 } else if(x.key == "a" || x.key == "A") {
286 var highestNum = 0;
287 var highestIndex = -1;
288 success = true;
289
290 for(var i=0;i<this.windowArray.length;i++) {
291 var h = this.windowArray[i].hilighted;
292 if(h > highestNum) {
293 highestIndex = i;
294 highestNum = h;
295 }
296 }
297 if(highestIndex > -1)
298 this.selectWindow(this.windowArray[highestIndex]);
299 } else if((x.key >= '0' && x.key <= '9') && !x.shift) {
300 success = true;
301
302 number = x.key - '0';
303 if(number == 0)
304 number = 10
305
306 number = number - 1;
307
308 if(number >= this.windowArray.length)
309 return;
310
311 this.selectWindow(this.windowArray[number]);
312 } else if((x.key == "left" || x.key == "up") && !x.shift) {
313 this.prevWindow();
314 success = true;
315 } else if((x.key == "right" || x.key == "down") && !x.shift) {
316 this.nextWindow();
317 success = true;
318 }
319
320 if(success) {
321 new Event(x).stop();
322 x.preventDefault();
323 }
324 },
325 getInputFocused: function(x) {
326 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
327 return false;
328 return true;
329 },
330 newCustomWindow: function(name, select, type) {
331 if(!type)
332 type = qwebirc.ui.WINDOW_CUSTOM;
333
334 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
335 w.addEvent("close", function(w) {
336 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
337 }.bind(this));
338
339 if(select)
340 this.selectWindow(w);
341
342 return w;
343 },
344 addCustomWindow: function(windowName, class_, cssClass, options, type) {
345 if(!$defined(options))
346 options = {};
347
348 if(this.customWindows.contains(windowName)) {
349 this.selectWindow(this.customWindows.get(windowName));
350 return;
351 }
352
353 var d = this.newCustomWindow(windowName, true, type);
354 this.customWindows.put(windowName, d);
355
356 d.addEvent("close", function() {
357 this.customWindows.remove(windowName);
358 }.bind(this));
359
360 if(cssClass)
361 d.lines.addClass("qwebirc-" + cssClass);
362
363 var ew = new class_(d.lines, options);
364 ew.addEvent("close", function() {
365 d.close();
366 }.bind(this));
367
368 d.setSubWindow(ew);
369 },
370 embeddedWindow: function() {
371 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
372 this.optionsWindow();
373 }.bind(this)});
374 },
375 optionsWindow: function() {
376 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
377 },
378 aboutWindow: function() {
379 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
380 },
381 privacyWindow: function() {
382 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
383 },
384 feedbackWindow: function() {
385 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
386 },
387 helpWindow: function() {
388 this.addCustomWindow("Help!", qwebirc.ui.HelpPane, "helppane", this.uiOptions);
389 },
390 urlDispatcher: function(name, window) {
391 if(name == "embedded")
392 return ["a", this.embeddedWindow.bind(this)];
393
394 if(name == "options")
395 return ["a", this.optionsWindow.bind(this)];
396
397 /* doesn't really belong here */
398 if(name == "whois") {
399 return ["span", function(nick) {
400 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
401 window.client.exec("/QUERY " + nick);
402 } else {
403 window.client.exec("/WHOIS " + nick);
404 }
405 }.bind(this)];
406 }
407
408 return null;
409 },
410 tabComplete: function(element, backwards) {
411 this.tabCompleter.tabComplete(element, backwards);
412 },
413 resetTabComplete: function() {
414 this.tabCompleter.reset();
415 },
416 setModifiableStylesheet: function(name) {
417 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + (QWEBIRC_DEBUG ? "debug/" : "") + name + qwebirc.FILE_SUFFIX + ".mcss");
418 this.setModifiableStylesheetValues({});
419 },
420 setModifiableStylesheetValues: function(values) {
421 for (var k in values)
422 this.__styleValues[k] = values[k];
423
424 if (!$defined(this.__styleSheet))
425 return;
426
427 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
428 var front;
429 if (!$defined(this.__styleValues.textHue) && !$defined(this.__styleValues.textLightness) && !$defined(this.__styleValues.textSaturation)) {
430 front = back;
431 } else {
432 front = {hue: Number(this.__styleValues.textHue), lightness: Number(this.__styleValues.textLightness), saturation: Number(this.__styleValues.textSaturation)}
433 }
434 var colours = {
435 back: back,
436 front: front
437 };
438
439 this.__styleSheet.set(function() {
440 var mode = arguments[0];
441 if(mode == "c") {
442 var t = colours[arguments[2]];
443 var x = new Color(arguments[1]);
444 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
445 if(c == "255,255,255") /* IE confuses white with transparent... */
446 c = "255,255,254";
447
448 return "rgb(" + c + ")";
449 } else if(mode == "o") {
450 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
451 }
452 }.bind(this));
453 }
454 });
455
456 qwebirc.ui.NotificationUI = new Class({
457 Extends: qwebirc.ui.StandardUI,
458 initialize: function(parentElement, windowClass, uiName, options) {
459 this.parent(parentElement, windowClass, uiName, options);
460
461 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
462 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
463 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
464
465 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
466 },
467 beep: function() {
468 this.__beeper.beep();
469 },
470 notify: function(title, message, callback) {
471 this.__beeper.beep();
472 this.__flasher.flash();
473 this.__notifier.notify(title, message, callback);
474 },
475 setBeepOnMention: function(value) {
476 if(value)
477 this.__beeper.soundInit();
478 },
479 setNotifications: function(value) {
480 this.__notifier.setEnabled(value);
481 },
482 updateTitle: function(text) {
483 if(this.__flasher.updateTitle(text))
484 this.parent(text);
485 },
486 focusChange: function(value) {
487 this.parent(value);
488 this.__flasher.focusChange(value);
489 this.__notifier.focusChange(value);
490 }
491 });
492
493 qwebirc.ui.QuakeNetUI = new Class({
494 Extends: qwebirc.ui.NotificationUI,
495 urlDispatcher: function(name, window) {
496 if(name == "qwhois") {
497 return ["span", function(auth) {
498 if($defined(this.parentObject.options.accountWhoisCommand))
499 this.client.exec(this.parentObject.options.accountWhoisCommand + auth);
500 }.bind(window)];
501 }
502 return this.parent(name, window);
503 },
504 logout: function() {
505 if(!qwebirc.auth.loggedin())
506 return;
507 if(confirm("Log out?")) {
508 this.clients.each(function(k, v) {
509 v.quit("Logged out");
510 }, this);
511
512 /* HACK */
513 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
514 foo.delay(500);
515 }
516 }
517 });
518
519 qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
520
521 qwebirc.ui.RequestTransformHTML = function(options) {
522 var HREF_ELEMENTS = {
523 "IMG": 1
524 };
525
526 var update = options.update;
527 var onSuccess = options.onSuccess;
528
529 var fixUp = function(node) {
530 if(node.nodeType != 1)
531 return;
532
533 var tagName = node.nodeName.toUpperCase();
534 if(HREF_ELEMENTS[tagName]) {
535 var attr = node.getAttribute("transform_attr");
536 var value = node.getAttribute("transform_value");
537 if($defined(attr) && $defined(value)) {
538 node.removeAttribute("transform_attr");
539 node.removeAttribute("transform_value");
540 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
541 }
542 }
543
544 for(var i=0;i<node.childNodes.length;i++)
545 fixUp(node.childNodes[i]);
546 };
547
548 delete options["update"];
549 options.onSuccess = function(tree, elements, html, js) {
550 var container = new Element("div");
551 container.set("html", html);
552 fixUp(container);
553 update.empty();
554
555 while(container.childNodes.length > 0) {
556 var x = container.firstChild;
557 container.removeChild(x);
558 update.appendChild(x);
559 }
560 onSuccess();
561 };
562
563 return new Request.HTML(options);
564 };
565