]> jfr.im git - irc/quakenet/qwebirc.git/blame - js/ui/baseui.js
tab complete by channel priority
[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
83d21243 231 document.addEvent("keydown", this.__handleHotkey.bind(this));
20157c51
CP
232 },
233 __handleHotkey: function(x) {
20157c51 234 var success = false;
83d21243
CP
235 if(!x.alt || x.control) {
236 if((x.key == "backspace" || x.key == "/") && !this.getInputFocused(x)) {
237 success = true;
238 }
239 } else if(x.key == "a" || x.key == "A") {
20157c51
CP
240 var highestNum = 0;
241 var highestIndex = -1;
242 success = true;
83d21243 243
20157c51
CP
244 for(var i=0;i<this.windowArray.length;i++) {
245 var h = this.windowArray[i].hilighted;
246 if(h > highestNum) {
247 highestIndex = i;
248 highestNum = h;
381fddfd 249 }
20157c51
CP
250 }
251 if(highestIndex > -1)
252 this.selectWindow(this.windowArray[highestIndex]);
253 } else if(x.key >= '0' && x.key <= '9') {
254 success = true;
255
256 number = x.key - '0';
257 if(number == 0)
258 number = 10
424608ac 259
20157c51
CP
260 number = number - 1;
261
262 if(number >= this.windowArray.length)
263 return;
381fddfd 264
20157c51
CP
265 this.selectWindow(this.windowArray[number]);
266 } else if(x.key == "left") {
267 this.prevWindow();
268 success = true;
269 } else if(x.key == "right") {
270 this.nextWindow();
271 success = true;
272 }
83d21243 273 if(success) {
20157c51 274 new Event(x).stop();
83d21243
CP
275 x.preventDefault();
276 }
20157c51
CP
277 },
278 getInputFocused: function(x) {
deebe19a
CP
279 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
280 return false;
281 return true;
841a451d 282 },
8af49135
CP
283 newCustomWindow: function(name, select, type) {
284 if(!type)
e20e5a6b 285 type = qwebirc.ui.WINDOW_CUSTOM;
8af49135 286
e20e5a6b 287 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
8af49135 288 w.addEvent("close", function(w) {
ea29e3d7 289 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
8af49135
CP
290 }.bind(this));
291
292 if(select)
293 this.selectWindow(w);
6c19eb8f 294
8af49135
CP
295 return w;
296 },
ebb21d2e
CP
297 addCustomWindow: function(windowName, class_, cssClass, options) {
298 if(!$defined(options))
299 options = {};
300
ea29e3d7
CP
301 if(this.customWindows.contains(windowName)) {
302 this.selectWindow(this.customWindows.get(windowName));
8af49135 303 return;
841a451d 304 }
8af49135 305
ebb21d2e 306 var d = this.newCustomWindow(windowName, true);
ea29e3d7 307 this.customWindows.put(windowName, d);
ebb21d2e
CP
308
309 d.addEvent("close", function() {
ea29e3d7 310 this.customWindows.remove(windowName);
8af49135
CP
311 }.bind(this));
312
ebb21d2e 313 if(cssClass)
e1a91a8a 314 d.lines.addClass("qwebirc-" + cssClass);
ebb21d2e
CP
315
316 var ew = new class_(d.lines, options);
8af49135 317 ew.addEvent("close", function() {
ebb21d2e 318 d.close();
8af49135 319 }.bind(this));
17f40fd9
CP
320
321 d.setSubWindow(ew);
841a451d 322 },
ebb21d2e 323 embeddedWindow: function() {
bcd2d24f 324 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
c0f2f367
CP
325 this.optionsWindow();
326 }.bind(this)});
ebb21d2e
CP
327 },
328 optionsWindow: function() {
329 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
330 },
e1a91a8a
CP
331 aboutWindow: function() {
332 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
333 },
b35116e2
CP
334 privacyWindow: function() {
335 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
336 },
391f51ff
CP
337 feedbackWindow: function() {
338 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
339 },
355dbcb7
CP
340 helpWindow: function() {
341 this.addCustomWindow("Help!", qwebirc.ui.HelpPane, "helppane", this.uiOptions);
f3d0c9f5 342 },
144ee52f 343 urlDispatcher: function(name, window) {
8af49135 344 if(name == "embedded")
925fc357 345 return ["a", this.embeddedWindow.bind(this)];
ebb21d2e
CP
346
347 if(name == "options")
348 return ["a", this.optionsWindow.bind(this)];
8af49135 349
5f2808af
CP
350 /* doesn't really belong here */
351 if(name == "whois") {
352 return ["span", function(nick) {
cbef082a
CP
353 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
354 window.client.exec("/QUERY " + nick);
355 } else {
356 window.client.exec("/WHOIS " + nick);
357 }
358 }.bind(this)];
5f2808af
CP
359 }
360
8af49135 361 return null;
3184781b
CP
362 },
363 tabComplete: function(element) {
364 this.tabCompleter.tabComplete(element);
365 },
366 resetTabComplete: function() {
367 this.tabCompleter.reset();
4dd199c3
CP
368 },
369 setModifiableStylesheet: function(name) {
fbe5af77 370 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + name + qwebirc.FILE_SUFFIX + ".mcss");
6f8a20df 371 this.setModifiableStylesheetValues({});
4dd199c3 372 },
6f8a20df
CP
373 setModifiableStylesheetValues: function(values) {
374 for(var k in values)
375 this.__styleValues[k] = values[k];
376
4dd199c3
CP
377 if(!$defined(this.__styleSheet))
378 return;
6f8a20df 379
656385a2
CP
380 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
381 var front = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
382
383 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
384 front = back;
385
386 var colours = {
387 back: back,
388 front: front
389 };
390
6f8a20df
CP
391 this.__styleSheet.set(function() {
392 var mode = arguments[0];
393 if(mode == "c") {
656385a2 394 var t = colours[arguments[2]];
6f8a20df 395 var x = new Color(arguments[1]);
656385a2 396 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
6f8a20df
CP
397 if(c == "255,255,255") /* IE confuses white with transparent... */
398 c = "255,255,254";
399
400 return "rgb(" + c + ")";
401 } else if(mode == "o") {
402 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
403 }
404 }.bind(this));
8af49135 405 }
381fddfd 406});
6f2e4a37 407
326478c2 408qwebirc.ui.NotificationUI = new Class({
e20e5a6b 409 Extends: qwebirc.ui.StandardUI,
fb71087a
CP
410 initialize: function(parentElement, windowClass, uiName, options) {
411 this.parent(parentElement, windowClass, uiName, options);
412
326478c2
CP
413 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
414 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
f63006ab
CP
415 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
416
326478c2 417 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
fb71087a 418 },
f63006ab
CP
419 beep: function() {
420 this.__beeper.beep();
421 },
422 notify: function(title, message, callback) {
423 this.__beeper.beep();
424 this.__flasher.flash();
425 this.__notifier.notify(title, message, callback);
426 },
127631e0
CP
427 setBeepOnMention: function(value) {
428 if(value)
326478c2
CP
429 this.__beeper.soundInit();
430 },
f63006ab
CP
431 setNotifications: function(value) {
432 this.__notifier.setEnabled(value);
433 },
326478c2
CP
434 updateTitle: function(text) {
435 if(this.__flasher.updateTitle(text))
436 this.parent(text);
eb271d86
CP
437 },
438 focusChange: function(value) {
439 this.parent(value);
440 this.__flasher.focusChange(value);
f63006ab 441 this.__notifier.focusChange(value);
1211ddcd 442 }
fb71087a
CP
443});
444
5f2808af 445qwebirc.ui.NewLoginUI = new Class({
326478c2 446 Extends: qwebirc.ui.NotificationUI,
5f2808af
CP
447 loginBox: function(callbackfn, initialNickname, initialChannels, autoConnect, autoNick) {
448 this.postInitialize();
449
c837b844 450 /* I'd prefer something shorter and snappier! */
a4a71818 451 var w = this.newCustomWindow("Connect", true, qwebirc.ui.WINDOW_CONNECT);
5f2808af
CP
452 var callback = function(args) {
453 w.close();
454 callbackfn(args);
455 };
456
457 qwebirc.ui.GenericLoginBox(w.lines, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
458 }
459});
460
461qwebirc.ui.QuakeNetUI = new Class({
462 Extends: qwebirc.ui.NewLoginUI,
2cd9e32d
CP
463 urlDispatcher: function(name, window) {
464 if(name == "qwhois") {
7cb09779 465 return ["span", function(auth) {
2cd9e32d 466 this.client.exec("/MSG Q whois #" + auth);
925fc357
CP
467 }.bind(window)];
468 }
144ee52f 469 return this.parent(name, window);
ffbb638d
CP
470 },
471 logout: function() {
472 if(!qwebirc.auth.loggedin())
473 return;
474 if(confirm("Log out?")) {
ea29e3d7
CP
475 this.clients.each(function(k, v) {
476 v.quit("Logged out");
477 }, this);
4b9f894d
CP
478
479 /* HACK */
fbe5af77 480 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
4b9f894d 481 foo.delay(500);
ffbb638d 482 }
2cd9e32d
CP
483 }
484});
144ee52f
CP
485
486qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
fbe5af77
CP
487
488qwebirc.ui.RequestTransformHTML = function(options) {
489 var HREF_ELEMENTS = {
490 "IMG": 1
491 };
492
493 var update = options.update;
494 var onSuccess = options.onSuccess;
495
496 var fixUp = function(node) {
497 if(node.nodeType != 1)
498 return;
499
500 var tagName = node.nodeName.toUpperCase();
501 if(HREF_ELEMENTS[tagName]) {
502 var attr = node.getAttribute("transform_attr");
503 var value = node.getAttribute("transform_value");
504 if($defined(attr) && $defined(value)) {
505 node.removeAttribute("transform_attr");
506 node.removeAttribute("transform_value");
507 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
508 }
509 }
510
511 for(var i=0;i<node.childNodes.length;i++)
512 fixUp(node.childNodes[i]);
513 };
514
515 delete options["update"];
516 options.onSuccess = function(tree, elements, html, js) {
517 var container = new Element("div");
518 container.set("html", html);
519 fixUp(container);
520 update.empty();
521
522 while(container.childNodes.length > 0) {
523 var x = container.firstChild;
524 container.removeChild(x);
525 update.appendChild(x);
526 }
527 onSuccess();
528 };
529
530 return new Request.HTML(options);
531};
532