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