]> 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("Connection details", 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 var ev;
268 if(Browser.Engine.trident) {
269 ev = "keydown";
270 } else {
271 ev = "keypress";
272 }
273 document.addEvent(ev, this.__handleHotkey.bind(this));
274 },
275 __handleHotkey: function(x) {
276 if(!x.alt || x.control) {
277 if(x.key == "backspace" || x.key == "/")
278 if(!this.getInputFocused(x))
279 new Event(x).stop();
280 return;
281 }
282 var success = false;
283 if(x.key == "a" || x.key == "A") {
284 var highestNum = 0;
285 var highestIndex = -1;
286 success = true;
287
288 new Event(x).stop();
289 for(var i=0;i<this.windowArray.length;i++) {
290 var h = this.windowArray[i].hilighted;
291 if(h > highestNum) {
292 highestIndex = i;
293 highestNum = h;
294 }
295 }
296 if(highestIndex > -1)
297 this.selectWindow(this.windowArray[highestIndex]);
298 } else if(x.key >= '0' && x.key <= '9') {
299 success = true;
300
301 number = x.key - '0';
302 if(number == 0)
303 number = 10
304
305 number = number - 1;
306
307 if(number >= this.windowArray.length)
308 return;
309
310 this.selectWindow(this.windowArray[number]);
311 } else if(x.key == "left") {
312 this.prevWindow();
313 success = true;
314 } else if(x.key == "right") {
315 this.nextWindow();
316 success = true;
317 }
318 if(success)
319 new Event(x).stop();
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 = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
425
426 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
427 front = back;
428
429 var colours = {
430 back: back,
431 front: front
432 };
433
434 this.__styleSheet.set(function() {
435 var mode = arguments[0];
436 if(mode == "c") {
437 var t = colours[arguments[2]];
438 var x = new Color(arguments[1]);
439 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
440 if(c == "255,255,255") /* IE confuses white with transparent... */
441 c = "255,255,254";
442
443 return "rgb(" + c + ")";
444 } else if(mode == "o") {
445 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
446 }
447 }.bind(this));
448 }
449 });
450
451 qwebirc.ui.NotificationUI = new Class({
452 Extends: qwebirc.ui.StandardUI,
453 initialize: function(parentElement, windowClass, uiName, options) {
454 this.parent(parentElement, windowClass, uiName, options);
455
456 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
457 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
458
459 this.beep = this.__beeper.beep.bind(this.__beeper);
460
461 this.flash = this.__flasher.flash.bind(this.__flasher);
462 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
463 },
464 setBeepOnMention: function(value) {
465 if(value)
466 this.__beeper.soundInit();
467 },
468 updateTitle: function(text) {
469 if(this.__flasher.updateTitle(text))
470 this.parent(text);
471 },
472 focusChange: function(value) {
473 this.parent(value);
474 this.__flasher.focusChange(value);
475 }
476 });
477
478 qwebirc.ui.QuakeNetUI = new Class({
479 Extends: qwebirc.ui.NotificationUI,
480 urlDispatcher: function(name, window) {
481 if(name == "qwhois") {
482 return ["span", function(auth) {
483 this.client.exec("/MSG Q whois #" + auth);
484 }.bind(window)];
485 }
486 return this.parent(name, window);
487 },
488 logout: function() {
489 if(!qwebirc.auth.loggedin())
490 return;
491 if(confirm("Log out?")) {
492 this.clients.each(function(k, v) {
493 v.quit("Logged out");
494 }, this);
495
496 /* HACK */
497 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
498 foo.delay(500);
499 }
500 }
501 });
502
503 qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
504
505 qwebirc.ui.RequestTransformHTML = function(options) {
506 var HREF_ELEMENTS = {
507 "IMG": 1
508 };
509
510 var update = options.update;
511 var onSuccess = options.onSuccess;
512
513 var fixUp = function(node) {
514 if(node.nodeType != 1)
515 return;
516
517 var tagName = node.nodeName.toUpperCase();
518 if(HREF_ELEMENTS[tagName]) {
519 var attr = node.getAttribute("transform_attr");
520 var value = node.getAttribute("transform_value");
521 if($defined(attr) && $defined(value)) {
522 node.removeAttribute("transform_attr");
523 node.removeAttribute("transform_value");
524 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
525 }
526 }
527
528 for(var i=0;i<node.childNodes.length;i++)
529 fixUp(node.childNodes[i]);
530 };
531
532 delete options["update"];
533 options.onSuccess = function(tree, elements, html, js) {
534 var container = new Element("div");
535 container.set("html", html);
536 fixUp(container);
537 update.empty();
538
539 while(container.childNodes.length > 0) {
540 var x = container.firstChild;
541 container.removeChild(x);
542 update.appendChild(x);
543 }
544 onSuccess();
545 };
546
547 return new Request.HTML(options);
548 };
549