]> jfr.im git - irc/quakenet/qwebirc.git/blame - js/ui/baseui.js
make topics bigger
[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";
9e769c12 9
e20e5a6b 10qwebirc.ui.BaseUI = new Class({
2cad083e 11 Implements: [Events],
a59dc700 12 initialize: function(parentElement, windowClass, uiName, options) {
2cad083e 13 this.options = options;
a59dc700 14
ea29e3d7
CP
15 this.windows = new QHash();
16 this.clients = new QHash();
17 this.windows.put(qwebirc.ui.CUSTOM_CLIENT, new QHash());
9e769c12
CP
18 this.windowArray = [];
19 this.windowClass = windowClass;
20 this.parentElement = parentElement;
21 this.parentElement.addClass("qwebirc");
22 this.parentElement.addClass("qwebirc-" + uiName);
e8db8558 23 this.firstClient = false;
e20e5a6b 24 this.commandhistory = new qwebirc.irc.CommandHistory();
ffbb638d 25 this.clientId = 0;
eb271d86
CP
26
27 this.windowFocused = true;
85449eee
CP
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 }
9e769c12
CP
50 },
51 newClient: function(client) {
ea29e3d7 52 client.id = String(this.clientId++);
96f28062 53 client.hilightController = new qwebirc.ui.HilightController(client);
fc38a626
CP
54 client.addEvent("signedOn", function() {
55 this.fireEvent("signedOn", client);
56 }.bind(this));
ea29e3d7
CP
57 this.windows.put(client.id, new QHash());
58 this.clients.put(client.id, client);
e20e5a6b 59 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
9e769c12 60 this.selectWindow(w);
e8db8558
CP
61 if(!this.firstClient) {
62 this.firstClient = true;
e20e5a6b 63 w.addLine("", "qwebirc v" + qwebirc.VERSION);
13828a8d 64 w.addLine("", "Copyright (C) 2008-2014 Chris Porter and the qwebirc project.");
2dfab0e1
CP
65 w.addLine("", "http://www.qwebirc.org");
66 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
e8db8558 67 }
9e769c12
CP
68 return w;
69 },
ffbb638d
CP
70 getClientId: function(client) {
71 if(client == qwebirc.ui.CUSTOM_CLIENT) {
72 return qwebirc.ui.CUSTOM_CLIENT;
73 } else {
74 return client.id;
75 }
76 },
fd3734d4 77 getWindowIdentifier: function(client, type, name) {
f74802c5
CP
78 if(type == qwebirc.ui.WINDOW_MESSAGES)
79 return "-M";
e20e5a6b 80 if(type == qwebirc.ui.WINDOW_STATUS)
f74802c5 81 return "";
fd3734d4
CP
82
83 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
84 return "_" + name;
85
86 return "_" + client.toIRCLower(name);
f74802c5
CP
87 },
88 newWindow: function(client, type, name) {
89 var w = this.getWindow(client, type, name);
90 if($defined(w))
91 return w;
9e769c12 92
fd3734d4 93 var wId = this.getWindowIdentifier(client, type, name);
ea29e3d7
CP
94 var w = new this.windowClass(this, client, type, name, wId);
95 this.windows.get(this.getClientId(client)).put(wId, w);
9e769c12
CP
96 this.windowArray.push(w);
97
98 return w;
99 },
f74802c5 100 getWindow: function(client, type, name) {
ea29e3d7 101 var c = this.windows.get(this.getClientId(client));
f74802c5
CP
102 if(!$defined(c))
103 return null;
104
fd3734d4 105 return c[this.getWindowIdentifier(client, type, name)];
f74802c5 106 },
9e769c12
CP
107 getActiveWindow: function() {
108 return this.active;
109 },
1d42a76f
CP
110 getActiveIRCWindow: function(client) {
111 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
ea29e3d7 112 return this.windows.get(this.getClientId(client)).get(this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS));
1d42a76f
CP
113 } else {
114 return this.active;
115 }
116 },
9e769c12
CP
117 __setActiveWindow: function(window) {
118 this.active = window;
119 },
5aa173fb
CP
120 renameWindow: function(window, name) {
121 if(this.getWindow(window.client, window.type, name))
122 return null;
123
124 var clientId = this.getClientId(window.client);
125 var index = this.windowArray.indexOf(window);
126 if(index == -1)
127 return null;
128
ea29e3d7 129 this.windows.get(clientId).remove(window.identifier);
5aa173fb
CP
130
131 var window = this.windowArray[index];
132 window.name = name;
133 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
134
ea29e3d7 135 this.windows.get(clientId).put(window.identifier, this.windowArray[index]);
5aa173fb
CP
136
137 if(window.active)
138 this.updateTitle(window.name + " - " + this.options.appTitle);
139
140 window.rename(window.name);
141 return window;
142 },
9e769c12
CP
143 selectWindow: function(window) {
144 if(this.active)
145 this.active.deselect();
146 window.select(); /* calls setActiveWindow */
326478c2
CP
147 this.updateTitle(window.name + " - " + this.options.appTitle);
148 },
149 updateTitle: function(text) {
150 document.title = text;
9e769c12 151 },
ff4befd8
CP
152 nextWindow: function(direction) {
153 if(this.windowArray.length == 0 || !this.active)
154 return;
155
156 if(!direction)
157 direction = 1;
158
159 var index = this.windowArray.indexOf(this.active);
160 if(index == -1)
161 return;
162
163 index = index + direction;
164 if(index < 0) {
165 index = this.windowArray.length - 1;
166 } else if(index >= this.windowArray.length) {
167 index = 0;
168 }
169
170 this.selectWindow(this.windowArray[index]);
171 },
172 prevWindow: function() {
173 this.nextWindow(-1);
174 },
9e769c12
CP
175 __closed: function(window) {
176 if(window.active) {
177 this.active = undefined;
178 if(this.windowArray.length == 1) {
179 this.windowArray = [];
180 } else {
181 var index = this.windowArray.indexOf(window);
6f2e4a37
CP
182 if(index == -1) {
183 return;
184 } else if(index == 0) {
9e769c12
CP
185 this.selectWindow(this.windowArray[1]);
186 } else {
187 this.selectWindow(this.windowArray[index - 1]);
188 }
9e769c12
CP
189 }
190 }
191
404cfb58 192 this.windowArray = this.windowArray.erase(window);
ea29e3d7 193 this.windows.get(this.getClientId(window.client)).remove(window.identifier);
eb9b087b 194 },
eb9b087b
CP
195 /*
196 this shouldn't be called by overriding classes!
66de775f 197 they should implement their own!
eb9b087b
CP
198 some form of user input MUST be received before an
199 IRC connection is made, else users are going to get
200 tricked into getting themselves glined
201 */
66de775f 202 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
2cad083e 203 qwebirc.ui.GenericLoginBox(this.parentElement, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
eb271d86
CP
204 },
205 focusChange: function(newValue) {
206 var window_ = this.getActiveWindow();
207 if($defined(window_))
208 window_.focusChange(newValue);
9e769c12
CP
209 }
210});
381fddfd 211
e20e5a6b
CP
212qwebirc.ui.StandardUI = new Class({
213 Extends: qwebirc.ui.BaseUI,
f3d0c9f5 214 UICommands: qwebirc.ui.UI_COMMANDS,
381fddfd
CP
215 initialize: function(parentElement, windowClass, uiName, options) {
216 this.parent(parentElement, windowClass, uiName, options);
3184781b
CP
217
218 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
c0f2f367 219 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
ea29e3d7 220 this.customWindows = new QHash();
ebb21d2e 221
6f8a20df
CP
222 this.__styleValues = {hue: this.uiOptions.STYLE_HUE, saturation: 0, lightness: 0};
223 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
224 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
225 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
656385a2
CP
226
227 if(this.options.thue !== null) this.__styleValues.textHue = this.options.thue;
228 if(this.options.tsaturation !== null) this.__styleValues.textSaturation = this.options.tsaturation;
229 if(this.options.tlightness !== null) this.__styleValues.textLightness = this.options.tlightness;
6f8a20df 230
2a802692 231 var ev;
20157c51
CP
232 if(Browser.Engine.trident) {
233 ev = "keydown";
234 } else {
235 ev = "keypress";
236 }
237 document.addEvent(ev, this.__handleHotkey.bind(this));
238 },
239 __handleHotkey: function(x) {
240 if(!x.alt || x.control) {
241 if(x.key == "backspace" || x.key == "/")
242 if(!this.getInputFocused(x))
243 new Event(x).stop();
244 return;
245 }
246 var success = false;
247 if(x.key == "a" || x.key == "A") {
248 var highestNum = 0;
249 var highestIndex = -1;
250 success = true;
251
252 new Event(x).stop();
253 for(var i=0;i<this.windowArray.length;i++) {
254 var h = this.windowArray[i].hilighted;
255 if(h > highestNum) {
256 highestIndex = i;
257 highestNum = h;
381fddfd 258 }
20157c51
CP
259 }
260 if(highestIndex > -1)
261 this.selectWindow(this.windowArray[highestIndex]);
262 } else if(x.key >= '0' && x.key <= '9') {
263 success = true;
264
265 number = x.key - '0';
266 if(number == 0)
267 number = 10
424608ac 268
20157c51
CP
269 number = number - 1;
270
271 if(number >= this.windowArray.length)
272 return;
381fddfd 273
20157c51
CP
274 this.selectWindow(this.windowArray[number]);
275 } else if(x.key == "left") {
276 this.prevWindow();
277 success = true;
278 } else if(x.key == "right") {
279 this.nextWindow();
280 success = true;
281 }
282 if(success)
283 new Event(x).stop();
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
CP
380 setModifiableStylesheetValues: function(values) {
381 for(var k in values)
382 this.__styleValues[k] = values[k];
383
4dd199c3
CP
384 if(!$defined(this.__styleSheet))
385 return;
6f8a20df 386
656385a2
CP
387 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
388 var front = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
389
390 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
391 front = back;
392
393 var colours = {
394 back: back,
395 front: front
396 };
397
6f8a20df
CP
398 this.__styleSheet.set(function() {
399 var mode = arguments[0];
400 if(mode == "c") {
656385a2 401 var t = colours[arguments[2]];
6f8a20df 402 var x = new Color(arguments[1]);
656385a2 403 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
6f8a20df
CP
404 if(c == "255,255,255") /* IE confuses white with transparent... */
405 c = "255,255,254";
406
407 return "rgb(" + c + ")";
408 } else if(mode == "o") {
409 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
410 }
411 }.bind(this));
8af49135 412 }
381fddfd 413});
6f2e4a37 414
326478c2 415qwebirc.ui.NotificationUI = new Class({
e20e5a6b 416 Extends: qwebirc.ui.StandardUI,
fb71087a
CP
417 initialize: function(parentElement, windowClass, uiName, options) {
418 this.parent(parentElement, windowClass, uiName, options);
419
326478c2
CP
420 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
421 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
fb71087a 422
326478c2 423 this.beep = this.__beeper.beep.bind(this.__beeper);
127631e0 424
326478c2
CP
425 this.flash = this.__flasher.flash.bind(this.__flasher);
426 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
fb71087a 427 },
127631e0
CP
428 setBeepOnMention: function(value) {
429 if(value)
326478c2
CP
430 this.__beeper.soundInit();
431 },
432 updateTitle: function(text) {
433 if(this.__flasher.updateTitle(text))
434 this.parent(text);
eb271d86
CP
435 },
436 focusChange: function(value) {
437 this.parent(value);
438 this.__flasher.focusChange(value);
1211ddcd 439 }
fb71087a
CP
440});
441
5f2808af 442qwebirc.ui.NewLoginUI = new Class({
326478c2 443 Extends: qwebirc.ui.NotificationUI,
5f2808af
CP
444 loginBox: function(callbackfn, initialNickname, initialChannels, autoConnect, autoNick) {
445 this.postInitialize();
446
c837b844 447 /* I'd prefer something shorter and snappier! */
a4a71818 448 var w = this.newCustomWindow("Connect", true, qwebirc.ui.WINDOW_CONNECT);
5f2808af
CP
449 var callback = function(args) {
450 w.close();
451 callbackfn(args);
452 };
453
454 qwebirc.ui.GenericLoginBox(w.lines, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
455 }
456});
457
458qwebirc.ui.QuakeNetUI = new Class({
459 Extends: qwebirc.ui.NewLoginUI,
2cd9e32d
CP
460 urlDispatcher: function(name, window) {
461 if(name == "qwhois") {
7cb09779 462 return ["span", function(auth) {
2cd9e32d 463 this.client.exec("/MSG Q whois #" + auth);
925fc357
CP
464 }.bind(window)];
465 }
144ee52f 466 return this.parent(name, window);
ffbb638d
CP
467 },
468 logout: function() {
469 if(!qwebirc.auth.loggedin())
470 return;
471 if(confirm("Log out?")) {
ea29e3d7
CP
472 this.clients.each(function(k, v) {
473 v.quit("Logged out");
474 }, this);
4b9f894d
CP
475
476 /* HACK */
fbe5af77 477 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
4b9f894d 478 foo.delay(500);
ffbb638d 479 }
2cd9e32d
CP
480 }
481});
144ee52f
CP
482
483qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
fbe5af77
CP
484
485qwebirc.ui.RequestTransformHTML = function(options) {
486 var HREF_ELEMENTS = {
487 "IMG": 1
488 };
489
490 var update = options.update;
491 var onSuccess = options.onSuccess;
492
493 var fixUp = function(node) {
494 if(node.nodeType != 1)
495 return;
496
497 var tagName = node.nodeName.toUpperCase();
498 if(HREF_ELEMENTS[tagName]) {
499 var attr = node.getAttribute("transform_attr");
500 var value = node.getAttribute("transform_value");
501 if($defined(attr) && $defined(value)) {
502 node.removeAttribute("transform_attr");
503 node.removeAttribute("transform_value");
504 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
505 }
506 }
507
508 for(var i=0;i<node.childNodes.length;i++)
509 fixUp(node.childNodes[i]);
510 };
511
512 delete options["update"];
513 options.onSuccess = function(tree, elements, html, js) {
514 var container = new Element("div");
515 container.set("html", html);
516 fixUp(container);
517 update.empty();
518
519 while(container.childNodes.length > 0) {
520 var x = container.firstChild;
521 container.removeChild(x);
522 update.appendChild(x);
523 }
524 onSuccess();
525 };
526
527 return new Request.HTML(options);
528};
529