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