]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/ui/baseui.js
Merge.
[irc/quakenet/qwebirc.git] / js / ui / baseui.js
1 qwebirc.ui.WINDOW_STATUS = 0x01;
2 qwebirc.ui.WINDOW_QUERY = 0x02;
3 qwebirc.ui.WINDOW_CHANNEL = 0x04;
4 qwebirc.ui.WINDOW_CUSTOM = 0x08;
5 qwebirc.ui.WINDOW_CONNECT = 0x10;
6 qwebirc.ui.WINDOW_MESSAGES = 0x20;
7
8 qwebirc.ui.CUSTOM_CLIENT = "custom";
9
10 qwebirc.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 qwebirc.util.__log = function(x) {
52 if(QWEBIRC_DEBUG) {
53 if(typeof console != "undefined")
54 console.log(x);
55 this.getActiveWindow().addLine(null, x);
56 }
57 }.bind(this);
58 },
59 newClient: function(client) {
60 client.id = String(this.clientId++);
61 client.hilightController = new qwebirc.ui.HilightController(client);
62 client.addEvent("signedOn", function() {
63 this.fireEvent("signedOn", client);
64 }.bind(this));
65 this.windows.put(client.id, new QHash());
66 this.clients.put(client.id, client);
67 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
68 this.selectWindow(w);
69 if(!this.firstClient) {
70 this.firstClient = true;
71 w.addLine("", "qwebirc v" + qwebirc.VERSION);
72 w.addLine("", "Copyright (C) 2008-2014 Chris Porter and the qwebirc project.");
73 w.addLine("", "http://www.qwebirc.org");
74 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
75 }
76 return w;
77 },
78 getClientId: function(client) {
79 if(client == qwebirc.ui.CUSTOM_CLIENT) {
80 return qwebirc.ui.CUSTOM_CLIENT;
81 } else {
82 return client.id;
83 }
84 },
85 getWindowIdentifier: function(client, type, name) {
86 if(type == qwebirc.ui.WINDOW_MESSAGES)
87 return "-M";
88 if(type == qwebirc.ui.WINDOW_STATUS)
89 return "";
90
91 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
92 return "_" + name;
93
94 return "_" + client.toIRCLower(name);
95 },
96 newWindow: function(client, type, name) {
97 var w = this.getWindow(client, type, name);
98 if($defined(w))
99 return w;
100
101 var wId = this.getWindowIdentifier(client, type, name);
102 var w = new this.windowClass(this, client, type, name, wId);
103 this.windows.get(this.getClientId(client)).put(wId, w);
104 this.windowArray.push(w);
105
106 return w;
107 },
108 getWindow: function(client, type, name) {
109 var c = this.windows.get(this.getClientId(client));
110 if(!$defined(c))
111 return null;
112
113 return c[this.getWindowIdentifier(client, type, name)];
114 },
115 getActiveWindow: function() {
116 return this.active;
117 },
118 getActiveIRCWindow: function(client) {
119 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
120 return this.windows.get(this.getClientId(client)).get(this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS));
121 } else {
122 return this.active;
123 }
124 },
125 __setActiveWindow: function(window) {
126 this.active = window;
127 },
128 renameWindow: function(window, name) {
129 if(this.getWindow(window.client, window.type, name))
130 return null;
131
132 var clientId = this.getClientId(window.client);
133 var index = this.windowArray.indexOf(window);
134 if(index == -1)
135 return null;
136
137 this.windows.get(clientId).remove(window.identifier);
138
139 var window = this.windowArray[index];
140 window.name = name;
141 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
142
143 this.windows.get(clientId).put(window.identifier, this.windowArray[index]);
144
145 if(window.active)
146 this.updateTitle(window.name + " - " + this.options.appTitle);
147
148 window.rename(window.name);
149 return window;
150 },
151 selectWindow: function(window) {
152 if(this.active)
153 this.active.deselect();
154 window.select(); /* calls setActiveWindow */
155 this.updateTitle(window.name + " - " + this.options.appTitle);
156 },
157 updateTitle: function(text) {
158 document.title = text;
159 },
160 nextWindow: function(direction) {
161 if(this.windowArray.length == 0 || !this.active)
162 return;
163
164 if(!direction)
165 direction = 1;
166
167 var index = this.windowArray.indexOf(this.active);
168 if(index == -1)
169 return;
170
171 index = index + direction;
172 if(index < 0) {
173 index = this.windowArray.length - 1;
174 } else if(index >= this.windowArray.length) {
175 index = 0;
176 }
177
178 this.selectWindow(this.windowArray[index]);
179 },
180 prevWindow: function() {
181 this.nextWindow(-1);
182 },
183 __closed: function(window) {
184 if(window.active) {
185 this.active = undefined;
186 if(this.windowArray.length == 1) {
187 this.windowArray = [];
188 } else {
189 var index = this.windowArray.indexOf(window);
190 if(index == -1) {
191 return;
192 } else if(index == 0) {
193 this.selectWindow(this.windowArray[1]);
194 } else {
195 this.selectWindow(this.windowArray[index - 1]);
196 }
197 }
198 }
199
200 this.windowArray = this.windowArray.erase(window);
201 this.windows.get(this.getClientId(window.client)).remove(window.identifier);
202 },
203 /*
204 this shouldn't be called by overriding classes!
205 they should implement their own!
206 some form of user input MUST be received before an
207 IRC connection is made, else users are going to get
208 tricked into getting themselves glined
209 */
210 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
211 qwebirc.ui.GenericLoginBox(this.parentElement, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
212 },
213 focusChange: function(newValue) {
214 var window_ = this.getActiveWindow();
215 if($defined(window_))
216 window_.focusChange(newValue);
217 }
218 });
219
220 qwebirc.ui.StandardUI = new Class({
221 Extends: qwebirc.ui.BaseUI,
222 UICommands: qwebirc.ui.UI_COMMANDS,
223 initialize: function(parentElement, windowClass, uiName, options) {
224 this.parent(parentElement, windowClass, uiName, options);
225
226 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
227 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
228 this.customWindows = new QHash();
229
230 this.__styleValues = {hue: this.uiOptions.STYLE_HUE, saturation: 0, lightness: 0};
231 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
232 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
233 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
234
235 if(this.options.thue !== null) this.__styleValues.textHue = this.options.thue;
236 if(this.options.tsaturation !== null) this.__styleValues.textSaturation = this.options.tsaturation;
237 if(this.options.tlightness !== null) this.__styleValues.textLightness = this.options.tlightness;
238
239 document.addEvent("keydown", this.__handleHotkey.bind(this));
240 },
241 __handleHotkey: function(x) {
242 var success = false;
243 if(!x.alt || x.control) {
244 if((x.key == "backspace" || x.key == "/") && !this.getInputFocused(x)) {
245 success = true;
246 }
247 } else if(x.key == "a" || x.key == "A") {
248 var highestNum = 0;
249 var highestIndex = -1;
250 success = true;
251
252 for(var i=0;i<this.windowArray.length;i++) {
253 var h = this.windowArray[i].hilighted;
254 if(h > highestNum) {
255 highestIndex = i;
256 highestNum = h;
257 }
258 }
259 if(highestIndex > -1)
260 this.selectWindow(this.windowArray[highestIndex]);
261 } else if(x.key >= '0' && x.key <= '9') {
262 success = true;
263
264 number = x.key - '0';
265 if(number == 0)
266 number = 10
267
268 number = number - 1;
269
270 if(number >= this.windowArray.length)
271 return;
272
273 this.selectWindow(this.windowArray[number]);
274 } else if(x.key == "left") {
275 this.prevWindow();
276 success = true;
277 } else if(x.key == "right") {
278 this.nextWindow();
279 success = true;
280 }
281 if(success) {
282 new Event(x).stop();
283 x.preventDefault();
284 }
285 },
286 getInputFocused: function(x) {
287 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
288 return false;
289 return true;
290 },
291 newCustomWindow: function(name, select, type) {
292 if(!type)
293 type = qwebirc.ui.WINDOW_CUSTOM;
294
295 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
296 w.addEvent("close", function(w) {
297 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
298 }.bind(this));
299
300 if(select)
301 this.selectWindow(w);
302
303 return w;
304 },
305 addCustomWindow: function(windowName, class_, cssClass, options) {
306 if(!$defined(options))
307 options = {};
308
309 if(this.customWindows.contains(windowName)) {
310 this.selectWindow(this.customWindows.get(windowName));
311 return;
312 }
313
314 var d = this.newCustomWindow(windowName, true);
315 this.customWindows.put(windowName, d);
316
317 d.addEvent("close", function() {
318 this.customWindows.remove(windowName);
319 }.bind(this));
320
321 if(cssClass)
322 d.lines.addClass("qwebirc-" + cssClass);
323
324 var ew = new class_(d.lines, options);
325 ew.addEvent("close", function() {
326 d.close();
327 }.bind(this));
328
329 d.setSubWindow(ew);
330 },
331 embeddedWindow: function() {
332 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
333 this.optionsWindow();
334 }.bind(this)});
335 },
336 optionsWindow: function() {
337 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
338 },
339 aboutWindow: function() {
340 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
341 },
342 privacyWindow: function() {
343 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
344 },
345 feedbackWindow: function() {
346 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
347 },
348 helpWindow: function() {
349 this.addCustomWindow("Help!", qwebirc.ui.HelpPane, "helppane", this.uiOptions);
350 },
351 urlDispatcher: function(name, window) {
352 if(name == "embedded")
353 return ["a", this.embeddedWindow.bind(this)];
354
355 if(name == "options")
356 return ["a", this.optionsWindow.bind(this)];
357
358 /* doesn't really belong here */
359 if(name == "whois") {
360 return ["span", function(nick) {
361 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
362 window.client.exec("/QUERY " + nick);
363 } else {
364 window.client.exec("/WHOIS " + nick);
365 }
366 }.bind(this)];
367 }
368
369 return null;
370 },
371 tabComplete: function(element) {
372 this.tabCompleter.tabComplete(element);
373 },
374 resetTabComplete: function() {
375 this.tabCompleter.reset();
376 },
377 setModifiableStylesheet: function(name) {
378 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + (QWEBIRC_DEBUG ? "debug/" : "") + name + qwebirc.FILE_SUFFIX + ".mcss");
379 this.setModifiableStylesheetValues({});
380 },
381 setModifiableStylesheetValues: function(values) {
382 for(var k in values)
383 this.__styleValues[k] = values[k];
384
385 if(!$defined(this.__styleSheet))
386 return;
387
388 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
389 var front = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
390
391 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
392 front = back;
393
394 var colours = {
395 back: back,
396 front: front
397 };
398
399 this.__styleSheet.set(function() {
400 var mode = arguments[0];
401 if(mode == "c") {
402 var t = colours[arguments[2]];
403 var x = new Color(arguments[1]);
404 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
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));
413 }
414 });
415
416 qwebirc.ui.NotificationUI = new Class({
417 Extends: qwebirc.ui.StandardUI,
418 initialize: function(parentElement, windowClass, uiName, options) {
419 this.parent(parentElement, windowClass, uiName, options);
420
421 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
422 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
423 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
424
425 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
426 },
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 },
435 setBeepOnMention: function(value) {
436 if(value)
437 this.__beeper.soundInit();
438 },
439 setNotifications: function(value) {
440 this.__notifier.setEnabled(value);
441 },
442 updateTitle: function(text) {
443 if(this.__flasher.updateTitle(text))
444 this.parent(text);
445 },
446 focusChange: function(value) {
447 this.parent(value);
448 this.__flasher.focusChange(value);
449 this.__notifier.focusChange(value);
450 }
451 });
452
453 qwebirc.ui.NewLoginUI = new Class({
454 Extends: qwebirc.ui.NotificationUI,
455 loginBox: function(callbackfn, initialNickname, initialChannels, autoConnect, autoNick) {
456 this.postInitialize();
457
458 /* I'd prefer something shorter and snappier! */
459 var w = this.newCustomWindow("Connect", true, qwebirc.ui.WINDOW_CONNECT);
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
469 qwebirc.ui.QuakeNetUI = new Class({
470 Extends: qwebirc.ui.NewLoginUI,
471 urlDispatcher: function(name, window) {
472 if(name == "qwhois") {
473 return ["span", function(auth) {
474 this.client.exec("/MSG Q whois #" + auth);
475 }.bind(window)];
476 }
477 return this.parent(name, window);
478 },
479 logout: function() {
480 if(!qwebirc.auth.loggedin())
481 return;
482 if(confirm("Log out?")) {
483 this.clients.each(function(k, v) {
484 v.quit("Logged out");
485 }, this);
486
487 /* HACK */
488 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
489 foo.delay(500);
490 }
491 }
492 });
493
494 qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
495
496 qwebirc.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