]> jfr.im git - irc/quakenet/qwebirc.git/blame_incremental - js/ui/baseui.js
tab complete by channel priority
[irc/quakenet/qwebirc.git] / js / ui / baseui.js
... / ...
CommitLineData
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;
6qwebirc.ui.WINDOW_MESSAGES = 0x20;
7
8qwebirc.ui.CUSTOM_CLIENT = "custom";
9
10qwebirc.ui.BaseUI = new Class({
11 Implements: [Events],
12 initialize: function(parentElement, windowClass, uiName, options) {
13 this.options = options;
14
15 this.windows = new QHash();
16 this.clients = new QHash();
17 this.windows.put(qwebirc.ui.CUSTOM_CLIENT, new QHash());
18 this.windowArray = [];
19 this.windowClass = windowClass;
20 this.parentElement = parentElement;
21 this.parentElement.addClass("qwebirc");
22 this.parentElement.addClass("qwebirc-" + uiName);
23 this.firstClient = false;
24 this.commandhistory = new qwebirc.irc.CommandHistory();
25 this.clientId = 0;
26
27 this.windowFocused = true;
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 }
50 },
51 newClient: function(client) {
52 client.id = String(this.clientId++);
53 client.hilightController = new qwebirc.ui.HilightController(client);
54 client.addEvent("signedOn", function() {
55 this.fireEvent("signedOn", client);
56 }.bind(this));
57 this.windows.put(client.id, new QHash());
58 this.clients.put(client.id, client);
59 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
60 this.selectWindow(w);
61 if(!this.firstClient) {
62 this.firstClient = true;
63 w.addLine("", "qwebirc v" + qwebirc.VERSION);
64 w.addLine("", "Copyright (C) 2008-2014 Chris Porter and the qwebirc project.");
65 w.addLine("", "http://www.qwebirc.org");
66 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
67 }
68 return w;
69 },
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 },
77 getWindowIdentifier: function(client, type, name) {
78 if(type == qwebirc.ui.WINDOW_MESSAGES)
79 return "-M";
80 if(type == qwebirc.ui.WINDOW_STATUS)
81 return "";
82
83 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
84 return "_" + name;
85
86 return "_" + client.toIRCLower(name);
87 },
88 newWindow: function(client, type, name) {
89 var w = this.getWindow(client, type, name);
90 if($defined(w))
91 return w;
92
93 var wId = this.getWindowIdentifier(client, type, name);
94 var w = new this.windowClass(this, client, type, name, wId);
95 this.windows.get(this.getClientId(client)).put(wId, w);
96 this.windowArray.push(w);
97
98 return w;
99 },
100 getWindow: function(client, type, name) {
101 var c = this.windows.get(this.getClientId(client));
102 if(!$defined(c))
103 return null;
104
105 return c[this.getWindowIdentifier(client, type, name)];
106 },
107 getActiveWindow: function() {
108 return this.active;
109 },
110 getActiveIRCWindow: function(client) {
111 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
112 return this.windows.get(this.getClientId(client)).get(this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS));
113 } else {
114 return this.active;
115 }
116 },
117 __setActiveWindow: function(window) {
118 this.active = window;
119 },
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
129 this.windows.get(clientId).remove(window.identifier);
130
131 var window = this.windowArray[index];
132 window.name = name;
133 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
134
135 this.windows.get(clientId).put(window.identifier, this.windowArray[index]);
136
137 if(window.active)
138 this.updateTitle(window.name + " - " + this.options.appTitle);
139
140 window.rename(window.name);
141 return window;
142 },
143 selectWindow: function(window) {
144 if(this.active)
145 this.active.deselect();
146 window.select(); /* calls setActiveWindow */
147 this.updateTitle(window.name + " - " + this.options.appTitle);
148 },
149 updateTitle: function(text) {
150 document.title = text;
151 },
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 },
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);
182 if(index == -1) {
183 return;
184 } else if(index == 0) {
185 this.selectWindow(this.windowArray[1]);
186 } else {
187 this.selectWindow(this.windowArray[index - 1]);
188 }
189 }
190 }
191
192 this.windowArray = this.windowArray.erase(window);
193 this.windows.get(this.getClientId(window.client)).remove(window.identifier);
194 },
195 /*
196 this shouldn't be called by overriding classes!
197 they should implement their own!
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 */
202 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
203 qwebirc.ui.GenericLoginBox(this.parentElement, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
204 },
205 focusChange: function(newValue) {
206 var window_ = this.getActiveWindow();
207 if($defined(window_))
208 window_.focusChange(newValue);
209 }
210});
211
212qwebirc.ui.StandardUI = new Class({
213 Extends: qwebirc.ui.BaseUI,
214 UICommands: qwebirc.ui.UI_COMMANDS,
215 initialize: function(parentElement, windowClass, uiName, options) {
216 this.parent(parentElement, windowClass, uiName, options);
217
218 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
219 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
220 this.customWindows = new QHash();
221
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;
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;
230
231 document.addEvent("keydown", this.__handleHotkey.bind(this));
232 },
233 __handleHotkey: function(x) {
234 var success = false;
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") {
240 var highestNum = 0;
241 var highestIndex = -1;
242 success = true;
243
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;
249 }
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
259
260 number = number - 1;
261
262 if(number >= this.windowArray.length)
263 return;
264
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 }
273 if(success) {
274 new Event(x).stop();
275 x.preventDefault();
276 }
277 },
278 getInputFocused: function(x) {
279 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
280 return false;
281 return true;
282 },
283 newCustomWindow: function(name, select, type) {
284 if(!type)
285 type = qwebirc.ui.WINDOW_CUSTOM;
286
287 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
288 w.addEvent("close", function(w) {
289 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
290 }.bind(this));
291
292 if(select)
293 this.selectWindow(w);
294
295 return w;
296 },
297 addCustomWindow: function(windowName, class_, cssClass, options) {
298 if(!$defined(options))
299 options = {};
300
301 if(this.customWindows.contains(windowName)) {
302 this.selectWindow(this.customWindows.get(windowName));
303 return;
304 }
305
306 var d = this.newCustomWindow(windowName, true);
307 this.customWindows.put(windowName, d);
308
309 d.addEvent("close", function() {
310 this.customWindows.remove(windowName);
311 }.bind(this));
312
313 if(cssClass)
314 d.lines.addClass("qwebirc-" + cssClass);
315
316 var ew = new class_(d.lines, options);
317 ew.addEvent("close", function() {
318 d.close();
319 }.bind(this));
320
321 d.setSubWindow(ew);
322 },
323 embeddedWindow: function() {
324 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
325 this.optionsWindow();
326 }.bind(this)});
327 },
328 optionsWindow: function() {
329 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
330 },
331 aboutWindow: function() {
332 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
333 },
334 privacyWindow: function() {
335 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
336 },
337 feedbackWindow: function() {
338 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
339 },
340 helpWindow: function() {
341 this.addCustomWindow("Help!", qwebirc.ui.HelpPane, "helppane", this.uiOptions);
342 },
343 urlDispatcher: function(name, window) {
344 if(name == "embedded")
345 return ["a", this.embeddedWindow.bind(this)];
346
347 if(name == "options")
348 return ["a", this.optionsWindow.bind(this)];
349
350 /* doesn't really belong here */
351 if(name == "whois") {
352 return ["span", function(nick) {
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)];
359 }
360
361 return null;
362 },
363 tabComplete: function(element) {
364 this.tabCompleter.tabComplete(element);
365 },
366 resetTabComplete: function() {
367 this.tabCompleter.reset();
368 },
369 setModifiableStylesheet: function(name) {
370 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + name + qwebirc.FILE_SUFFIX + ".mcss");
371 this.setModifiableStylesheetValues({});
372 },
373 setModifiableStylesheetValues: function(values) {
374 for(var k in values)
375 this.__styleValues[k] = values[k];
376
377 if(!$defined(this.__styleSheet))
378 return;
379
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
391 this.__styleSheet.set(function() {
392 var mode = arguments[0];
393 if(mode == "c") {
394 var t = colours[arguments[2]];
395 var x = new Color(arguments[1]);
396 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
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));
405 }
406});
407
408qwebirc.ui.NotificationUI = new Class({
409 Extends: qwebirc.ui.StandardUI,
410 initialize: function(parentElement, windowClass, uiName, options) {
411 this.parent(parentElement, windowClass, uiName, options);
412
413 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
414 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
415 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
416
417 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
418 },
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 },
427 setBeepOnMention: function(value) {
428 if(value)
429 this.__beeper.soundInit();
430 },
431 setNotifications: function(value) {
432 this.__notifier.setEnabled(value);
433 },
434 updateTitle: function(text) {
435 if(this.__flasher.updateTitle(text))
436 this.parent(text);
437 },
438 focusChange: function(value) {
439 this.parent(value);
440 this.__flasher.focusChange(value);
441 this.__notifier.focusChange(value);
442 }
443});
444
445qwebirc.ui.NewLoginUI = new Class({
446 Extends: qwebirc.ui.NotificationUI,
447 loginBox: function(callbackfn, initialNickname, initialChannels, autoConnect, autoNick) {
448 this.postInitialize();
449
450 /* I'd prefer something shorter and snappier! */
451 var w = this.newCustomWindow("Connect", true, qwebirc.ui.WINDOW_CONNECT);
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,
463 urlDispatcher: function(name, window) {
464 if(name == "qwhois") {
465 return ["span", function(auth) {
466 this.client.exec("/MSG Q whois #" + auth);
467 }.bind(window)];
468 }
469 return this.parent(name, window);
470 },
471 logout: function() {
472 if(!qwebirc.auth.loggedin())
473 return;
474 if(confirm("Log out?")) {
475 this.clients.each(function(k, v) {
476 v.quit("Logged out");
477 }, this);
478
479 /* HACK */
480 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
481 foo.delay(500);
482 }
483 }
484});
485
486qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
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