]> 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 qwebirc.ui.DEFAULT_HUE = 210; /* nice blue */
10
11 qwebirc.ui.BaseUI = new Class({
12 Implements: [Events],
13 initialize: function(parentElement, windowClass, uiName, options) {
14 this.options = options;
15
16 this.windows = new QHash();
17 this.clients = new QHash();
18 this.windows.put(qwebirc.ui.CUSTOM_CLIENT, new QHash());
19 this.windowArray = [];
20 this.windowClass = windowClass;
21 this.parentElement = parentElement;
22 this.parentElement.addClass("qwebirc");
23 this.parentElement.addClass("qwebirc-" + uiName);
24 this.firstClient = false;
25 this.commandhistory = new qwebirc.irc.CommandHistory();
26 this.clientId = 0;
27
28 this.windowFocused = true;
29
30 if(Browser.Engine.trident) {
31 var checkFocus = function() {
32 var hasFocus = document.hasFocus();
33 if(hasFocus != this.windowFocused) {
34 this.windowFocused = hasFocus;
35 this.focusChange(hasFocus);
36 }
37 }
38
39 checkFocus.periodical(100, this);
40 } else {
41 var blur = function() { if(this.windowFocused) { this.windowFocused = false; this.focusChange(false); } }.bind(this);
42 var focus = function() { if(!this.windowFocused) { this.windowFocused = true; this.focusChange(true); } }.bind(this);
43
44 /* firefox requires both */
45
46 document.addEvent("blur", blur);
47 window.addEvent("blur", blur);
48 document.addEvent("focus", focus);
49 window.addEvent("focus", focus);
50 }
51
52 qwebirc.util.__log = function(x) {
53 if(QWEBIRC_DEBUG) {
54 if(typeof console != "undefined")
55 console.log(x);
56 this.getActiveWindow().addLine(null, x);
57 }
58 }.bind(this);
59 },
60 newClient: function(client) {
61 client.id = String(this.clientId++);
62 client.hilightController = new qwebirc.ui.HilightController(client);
63 client.addEvent("signedOn", function() {
64 this.poller = new qwebirc.xdomain.Poller(this.oobMessage.bind(this));
65 this.fireEvent("signedOn", client);
66 }.bind(this));
67 this.windows.put(client.id, new QHash());
68 this.clients.put(client.id, client);
69 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
70 this.selectWindow(w);
71 if(!this.firstClient) {
72 this.firstClient = true;
73 w.addLine("", "qwebirc v" + qwebirc.VERSION);
74 w.addLine("", "Copyright (C) 2008-2014 Chris Porter and the qwebirc project.");
75 w.addLine("", "http://www.qwebirc.org");
76 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
77 }
78 return w;
79 },
80 getClientId: function(client) {
81 if(client == qwebirc.ui.CUSTOM_CLIENT) {
82 return qwebirc.ui.CUSTOM_CLIENT;
83 } else {
84 return client.id;
85 }
86 },
87 getWindowIdentifier: function(client, type, name) {
88 if(type == qwebirc.ui.WINDOW_MESSAGES)
89 return "-M";
90 if(type == qwebirc.ui.WINDOW_STATUS)
91 return "";
92
93 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
94 return "_" + name;
95
96 return "_" + client.toIRCLower(name);
97 },
98 newWindow: function(client, type, name) {
99 var w = this.getWindow(client, type, name);
100 if($defined(w))
101 return w;
102
103 var wId = this.getWindowIdentifier(client, type, name);
104 var w = new this.windowClass(this, client, type, name, wId);
105 this.windows.get(this.getClientId(client)).put(wId, w);
106 this.windowArray.push(w);
107
108 return w;
109 },
110 getWindow: function(client, type, name) {
111 var c = this.windows.get(this.getClientId(client));
112 if(!$defined(c))
113 return null;
114
115 return c.get(this.getWindowIdentifier(client, type, name));
116 },
117 getActiveWindow: function() {
118 return this.active;
119 },
120 getActiveIRCWindow: function(client) {
121 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
122 return this.windows.get(this.getClientId(client)).get(this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS));
123 } else {
124 return this.active;
125 }
126 },
127 __setActiveWindow: function(window) {
128 this.active = window;
129 },
130 renameWindow: function(window, name) {
131 if(this.getWindow(window.client, window.type, name))
132 return null;
133
134 var clientId = this.getClientId(window.client);
135 var index = this.windowArray.indexOf(window);
136 if(index == -1)
137 return null;
138
139 this.windows.get(clientId).remove(window.identifier);
140
141 var window = this.windowArray[index];
142 window.name = name;
143 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
144
145 this.windows.get(clientId).put(window.identifier, this.windowArray[index]);
146
147 if(window.active)
148 this.updateTitle(window.name + " - " + this.options.appTitle);
149
150 window.rename(window.name);
151 return window;
152 },
153 selectWindow: function(window) {
154 if(this.active)
155 this.active.deselect();
156 window.select(); /* calls setActiveWindow */
157 this.updateTitle(window.name + " - " + this.options.appTitle);
158 },
159 updateTitle: function(text) {
160 document.title = text;
161 },
162 nextWindow: function(direction) {
163 if(this.windowArray.length == 0 || !this.active)
164 return;
165
166 if(!direction)
167 direction = 1;
168
169 var index = this.windowArray.indexOf(this.active);
170 if(index == -1)
171 return;
172
173 index = index + direction;
174 if(index < 0) {
175 index = this.windowArray.length - 1;
176 } else if(index >= this.windowArray.length) {
177 index = 0;
178 }
179
180 this.selectWindow(this.windowArray[index]);
181 },
182 prevWindow: function() {
183 this.nextWindow(-1);
184 },
185 __closed: function(window) {
186 if(window.active) {
187 this.active = undefined;
188 if(this.windowArray.length == 1) {
189 this.windowArray = [];
190 } else {
191 var index = this.windowArray.indexOf(window);
192 if(index == -1) {
193 return;
194 } else if(index == 0) {
195 this.selectWindow(this.windowArray[1]);
196 } else {
197 this.selectWindow(this.windowArray[index - 1]);
198 }
199 }
200 }
201
202 this.windowArray = this.windowArray.erase(window);
203 this.windows.get(this.getClientId(window.client)).remove(window.identifier);
204 },
205 /*
206 this shouldn't be called by overriding classes!
207 they should implement their own!
208 some form of user input MUST be received before an
209 IRC connection is made, else users are going to get
210 tricked into getting themselves glined
211 */
212 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
213 this.postInitialize();
214
215 this.addCustomWindow("Connect", qwebirc.ui.ConnectPane, "connectpane", {
216 initialNickname: initialNickname, initialChannels: initialChannels, autoConnect: autoConnect, networkName: this.options.networkName, callback: callback, autoNick: autoNick
217 }, qwebirc.ui.WINDOW_CONNECT);
218 },
219 focusChange: function(newValue) {
220 var window_ = this.getActiveWindow();
221 if($defined(window_))
222 window_.focusChange(newValue);
223 },
224 oobMessage: function(message) {
225 var c = message.splitMax(" ", 2);
226 if(c.length != 2)
227 return;
228
229 var command = c[0];
230 if(command != "CMD")
231 return;
232
233 var d = c[1].splitMax(" ", 2);
234 if(d.length != 2)
235 return;
236
237 var command = d[0];
238 var args = d[1];
239 if(command == "SAY") {
240 var w = this.getActiveIRCWindow();
241 if($defined(w) && (w.type == qwebirc.ui.WINDOW_CHANNEL || w.type == qwebirc.ui.WINDOW_QUERY)) {
242 w.client.exec("/SAY " + args);
243 return;
244 }
245 }
246 }
247 });
248
249 qwebirc.ui.StandardUI = new Class({
250 Extends: qwebirc.ui.BaseUI,
251 UICommands: qwebirc.ui.UI_COMMANDS,
252 initialize: function(parentElement, windowClass, uiName, options) {
253 this.parent(parentElement, windowClass, uiName, options);
254
255 this.__styleValues = {hue: qwebirc.ui.DEFAULT_HUE, saturation: 0, lightness: 0, textHue: null, textSaturation: null, textLightness: null};
256 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
257 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
258 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
259 this.customWindows = new QHash();
260
261 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
262 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
263 if($defined(this.options.tsaturation)) this.__styleValues.textSaturation = this.options.tsaturation;
264 if($defined(this.options.tlightness)) this.__styleValues.textLightness = this.options.tlightness;
265
266 if($defined(this.options.hue)) { /* overridden in url */
267 /* ugh... this will go away when we add proper options for hue/sat/light for text and background */
268 this.uiOptions.setValueByPrefix("STYLE_HUE", this.__styleValues.hue);
269 } else {
270 this.__styleValues.hue = this.uiOptions.STYLE_HUE; /* otherwise copy from serialised store */
271 }
272 this.__styleValues.textHue = $defined(this.options.thue) ? this.options.thue : this.__styleValues.hue;
273
274 document.addEvent("keydown", this.__handleHotkey.bind(this));
275 },
276 __handleHotkey: function(x) {
277 var success = false;
278 if(!x.alt || x.control) {
279 if((x.key == "backspace" || x.key == "/") && !this.getInputFocused(x)) {
280 success = true;
281 }
282 } else if(x.key == "a" || x.key == "A") {
283 var highestNum = 0;
284 var highestIndex = -1;
285 success = true;
286
287 for(var i=0;i<this.windowArray.length;i++) {
288 var h = this.windowArray[i].hilighted;
289 if(h > highestNum) {
290 highestIndex = i;
291 highestNum = h;
292 }
293 }
294 if(highestIndex > -1)
295 this.selectWindow(this.windowArray[highestIndex]);
296 } else if(x.key >= '0' && x.key <= '9') {
297 success = true;
298
299 number = x.key - '0';
300 if(number == 0)
301 number = 10
302
303 number = number - 1;
304
305 if(number >= this.windowArray.length)
306 return;
307
308 this.selectWindow(this.windowArray[number]);
309 } else if(x.key == "left") {
310 this.prevWindow();
311 success = true;
312 } else if(x.key == "right") {
313 this.nextWindow();
314 success = true;
315 }
316 if(success) {
317 new Event(x).stop();
318 x.preventDefault();
319 }
320 },
321 getInputFocused: function(x) {
322 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
323 return false;
324 return true;
325 },
326 newCustomWindow: function(name, select, type) {
327 if(!type)
328 type = qwebirc.ui.WINDOW_CUSTOM;
329
330 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
331 w.addEvent("close", function(w) {
332 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
333 }.bind(this));
334
335 if(select)
336 this.selectWindow(w);
337
338 return w;
339 },
340 addCustomWindow: function(windowName, class_, cssClass, options, type) {
341 if(!$defined(options))
342 options = {};
343
344 if(this.customWindows.contains(windowName)) {
345 this.selectWindow(this.customWindows.get(windowName));
346 return;
347 }
348
349 var d = this.newCustomWindow(windowName, true, type);
350 this.customWindows.put(windowName, d);
351
352 d.addEvent("close", function() {
353 this.customWindows.remove(windowName);
354 }.bind(this));
355
356 if(cssClass)
357 d.lines.addClass("qwebirc-" + cssClass);
358
359 var ew = new class_(d.lines, options);
360 ew.addEvent("close", function() {
361 d.close();
362 }.bind(this));
363
364 d.setSubWindow(ew);
365 },
366 embeddedWindow: function() {
367 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
368 this.optionsWindow();
369 }.bind(this)});
370 },
371 optionsWindow: function() {
372 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
373 },
374 aboutWindow: function() {
375 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
376 },
377 privacyWindow: function() {
378 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
379 },
380 feedbackWindow: function() {
381 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
382 },
383 helpWindow: function() {
384 this.addCustomWindow("Help!", qwebirc.ui.HelpPane, "helppane", this.uiOptions);
385 },
386 urlDispatcher: function(name, window) {
387 if(name == "embedded")
388 return ["a", this.embeddedWindow.bind(this)];
389
390 if(name == "options")
391 return ["a", this.optionsWindow.bind(this)];
392
393 /* doesn't really belong here */
394 if(name == "whois") {
395 return ["span", function(nick) {
396 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
397 window.client.exec("/QUERY " + nick);
398 } else {
399 window.client.exec("/WHOIS " + nick);
400 }
401 }.bind(this)];
402 }
403
404 return null;
405 },
406 tabComplete: function(element) {
407 this.tabCompleter.tabComplete(element);
408 },
409 resetTabComplete: function() {
410 this.tabCompleter.reset();
411 },
412 setModifiableStylesheet: function(name) {
413 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + (QWEBIRC_DEBUG ? "debug/" : "") + name + qwebirc.FILE_SUFFIX + ".mcss");
414 this.setModifiableStylesheetValues({});
415 },
416 setModifiableStylesheetValues: function(values) {
417 for (var k in values)
418 this.__styleValues[k] = values[k];
419
420 if (!$defined(this.__styleSheet))
421 return;
422
423 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
424 var front;
425 if (!$defined(this.__styleValues.textHue) && !$defined(this.__styleValues.textLightness) && !$defined(this.__styleValues.textSaturation)) {
426 front = back;
427 } else {
428 front = {hue: Number(this.__styleValues.textHue), lightness: Number(this.__styleValues.textLightness), saturation: Number(this.__styleValues.textSaturation)}
429 }
430 var colours = {
431 back: back,
432 front: front
433 };
434
435 this.__styleSheet.set(function() {
436 var mode = arguments[0];
437 if(mode == "c") {
438 var t = colours[arguments[2]];
439 var x = new Color(arguments[1]);
440 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
441 if(c == "255,255,255") /* IE confuses white with transparent... */
442 c = "255,255,254";
443
444 return "rgb(" + c + ")";
445 } else if(mode == "o") {
446 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
447 }
448 }.bind(this));
449 }
450 });
451
452 qwebirc.ui.NotificationUI = new Class({
453 Extends: qwebirc.ui.StandardUI,
454 initialize: function(parentElement, windowClass, uiName, options) {
455 this.parent(parentElement, windowClass, uiName, options);
456
457 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
458 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
459 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
460
461 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
462 },
463 beep: function() {
464 this.__beeper.beep();
465 },
466 notify: function(title, message, callback) {
467 this.__beeper.beep();
468 this.__flasher.flash();
469 this.__notifier.notify(title, message, callback);
470 },
471 setBeepOnMention: function(value) {
472 if(value)
473 this.__beeper.soundInit();
474 },
475 setNotifications: function(value) {
476 this.__notifier.setEnabled(value);
477 },
478 updateTitle: function(text) {
479 if(this.__flasher.updateTitle(text))
480 this.parent(text);
481 },
482 focusChange: function(value) {
483 this.parent(value);
484 this.__flasher.focusChange(value);
485 this.__notifier.focusChange(value);
486 }
487 });
488
489 qwebirc.ui.QuakeNetUI = new Class({
490 Extends: qwebirc.ui.NotificationUI,
491 urlDispatcher: function(name, window) {
492 if(name == "qwhois") {
493 return ["span", function(auth) {
494 this.client.exec("/MSG Q whois #" + auth);
495 }.bind(window)];
496 }
497 return this.parent(name, window);
498 },
499 logout: function() {
500 if(!qwebirc.auth.loggedin())
501 return;
502 if(confirm("Log out?")) {
503 this.clients.each(function(k, v) {
504 v.quit("Logged out");
505 }, this);
506
507 /* HACK */
508 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
509 foo.delay(500);
510 }
511 }
512 });
513
514 qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
515
516 qwebirc.ui.RequestTransformHTML = function(options) {
517 var HREF_ELEMENTS = {
518 "IMG": 1
519 };
520
521 var update = options.update;
522 var onSuccess = options.onSuccess;
523
524 var fixUp = function(node) {
525 if(node.nodeType != 1)
526 return;
527
528 var tagName = node.nodeName.toUpperCase();
529 if(HREF_ELEMENTS[tagName]) {
530 var attr = node.getAttribute("transform_attr");
531 var value = node.getAttribute("transform_value");
532 if($defined(attr) && $defined(value)) {
533 node.removeAttribute("transform_attr");
534 node.removeAttribute("transform_value");
535 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
536 }
537 }
538
539 for(var i=0;i<node.childNodes.length;i++)
540 fixUp(node.childNodes[i]);
541 };
542
543 delete options["update"];
544 options.onSuccess = function(tree, elements, html, js) {
545 var container = new Element("div");
546 container.set("html", html);
547 fixUp(container);
548 update.empty();
549
550 while(container.childNodes.length > 0) {
551 var x = container.firstChild;
552 container.removeChild(x);
553 update.appendChild(x);
554 }
555 onSuccess();
556 };
557
558 return new Request.HTML(options);
559 };
560