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