]> 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 = {};
16 this.clients = {};
17 this.windows[qwebirc.ui.CUSTOM_CLIENT] = {};
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 = this.clientId++;
53 client.hilightController = new qwebirc.ui.HilightController(client);
54
55 this.windows[client.id] = {}
56 this.clients[client.id] = client;
57 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
58 this.selectWindow(w);
59 if(!this.firstClient) {
60 this.firstClient = true;
61 w.addLine("", "qwebirc v" + qwebirc.VERSION);
62 w.addLine("", "Copyright (C) 2008-2012 Chris Porter and the qwebirc project.");
63 w.addLine("", "http://www.qwebirc.org");
64 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
65 }
66 return w;
67 },
68 getClientId: function(client) {
69 if(client == qwebirc.ui.CUSTOM_CLIENT) {
70 return qwebirc.ui.CUSTOM_CLIENT;
71 } else {
72 return client.id;
73 }
74 },
75 getWindowIdentifier: function(client, type, name) {
76 if(type == qwebirc.ui.WINDOW_MESSAGES)
77 return "-M";
78 if(type == qwebirc.ui.WINDOW_STATUS)
79 return "";
80
81 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
82 return "_" + name;
83
84 return "_" + client.toIRCLower(name);
85 },
86 newWindow: function(client, type, name) {
87 var w = this.getWindow(client, type, name);
88 if($defined(w))
89 return w;
90
91 var wId = this.getWindowIdentifier(client, type, name);
92 var w = this.windows[this.getClientId(client)][wId] = new this.windowClass(this, client, type, name, wId);
93 this.windowArray.push(w);
94
95 return w;
96 },
97 getWindow: function(client, type, name) {
98 var c = this.windows[this.getClientId(client)];
99 if(!$defined(c))
100 return null;
101
102 return c[this.getWindowIdentifier(client, type, name)];
103 },
104 getActiveWindow: function() {
105 return this.active;
106 },
107 getActiveIRCWindow: function(client) {
108 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
109 return this.windows[this.getClientId(client)][this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS)];
110 } else {
111 return this.active;
112 }
113 },
114 __setActiveWindow: function(window) {
115 this.active = window;
116 },
117 renameWindow: function(window, name) {
118 if(this.getWindow(window.client, window.type, name))
119 return null;
120
121 var clientId = this.getClientId(window.client);
122 var index = this.windowArray.indexOf(window);
123 if(index == -1)
124 return null;
125
126 delete this.windows[clientId][window.identifier];
127
128 var window = this.windowArray[index];
129 window.name = name;
130 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
131
132 this.windows[clientId][window.identifier] = this.windowArray[index];
133
134 if(window.active)
135 this.updateTitle(window.name + " - " + this.options.appTitle);
136
137 window.rename(window.name);
138 return window;
139 },
140 selectWindow: function(window) {
141 if(this.active)
142 this.active.deselect();
143 window.select(); /* calls setActiveWindow */
144 this.updateTitle(window.name + " - " + this.options.appTitle);
145 },
146 updateTitle: function(text) {
147 document.title = text;
148 },
149 nextWindow: function(direction) {
150 if(this.windowArray.length == 0 || !this.active)
151 return;
152
153 if(!direction)
154 direction = 1;
155
156 var index = this.windowArray.indexOf(this.active);
157 if(index == -1)
158 return;
159
160 index = index + direction;
161 if(index < 0) {
162 index = this.windowArray.length - 1;
163 } else if(index >= this.windowArray.length) {
164 index = 0;
165 }
166
167 this.selectWindow(this.windowArray[index]);
168 },
169 prevWindow: function() {
170 this.nextWindow(-1);
171 },
172 __closed: function(window) {
173 if(window.active) {
174 this.active = undefined;
175 if(this.windowArray.length == 1) {
176 this.windowArray = [];
177 } else {
178 var index = this.windowArray.indexOf(window);
179 if(index == -1) {
180 return;
181 } else if(index == 0) {
182 this.selectWindow(this.windowArray[1]);
183 } else {
184 this.selectWindow(this.windowArray[index - 1]);
185 }
186 }
187 }
188
189 this.windowArray = this.windowArray.erase(window);
190 delete this.windows[this.getClientId(window.client)][window.identifier];
191 },
192 /*
193 this shouldn't be called by overriding classes!
194 they should implement their own!
195 some form of user input MUST be received before an
196 IRC connection is made, else users are going to get
197 tricked into getting themselves glined
198 */
199 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
200 this.postInitialize();
201
202 this.addCustomWindow("Connection details", qwebirc.ui.ConnectPane, "connectpane", {
203 initialNickname: initialNickname, initialChannels: initialChannels, autoConnect: autoConnect, networkName: this.options.networkName, callback: callback, autoNick: autoNick
204 }, qwebirc.ui.WINDOW_CONNECT);
205 },
206 focusChange: function(newValue) {
207 var window_ = this.getActiveWindow();
208 if($defined(window_))
209 window_.focusChange(newValue);
210 },
211 signedOn: function() {
212 this.poller = new qwebirc.xdomain.Poller(this.oobMessage.bind(this));
213 },
214 oobMessage: function(message) {
215 var c = message.splitMax(" ", 2);
216 if(c.length != 2)
217 return;
218
219 var command = c[0];
220 if(command != "CMD")
221 return;
222
223 var d = c[1].splitMax(" ", 2);
224 if(d.length != 2)
225 return;
226
227 var command = d[0];
228 var args = d[1];
229 if(command == "SAY") {
230 var w = this.getActiveIRCWindow();
231 if($defined(w) && (w.type == qwebirc.ui.WINDOW_CHANNEL || w.type == qwebirc.ui.WINDOW_QUERY)) {
232 w.client.exec("/SAY " + args);
233 return;
234 }
235 }
236 }
237 });
238
239 qwebirc.ui.StandardUI = new Class({
240 Extends: qwebirc.ui.BaseUI,
241 UICommands: qwebirc.ui.UI_COMMANDS,
242 initialize: function(parentElement, windowClass, uiName, options) {
243 this.parent(parentElement, windowClass, uiName, options);
244
245 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
246 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
247 this.customWindows = {};
248
249 this.__styleValues = {hue: this.uiOptions.STYLE_HUE, saturation: 0, lightness: 0};
250 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
251 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
252 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
253
254 if(this.options.thue !== null) this.__styleValues.textHue = this.options.thue;
255 if(this.options.tsaturation !== null) this.__styleValues.textSaturation = this.options.tsaturation;
256 if(this.options.tlightness !== null) this.__styleValues.textLightness = this.options.tlightness;
257
258 var ev;
259 if(Browser.Engine.trident) {
260 ev = "keydown";
261 } else {
262 ev = "keypress";
263 }
264 document.addEvent(ev, this.__handleHotkey.bind(this));
265 },
266 __handleHotkey: function(x) {
267 if(!x.alt || x.control) {
268 if(x.key == "backspace" || x.key == "/")
269 if(!this.getInputFocused(x))
270 new Event(x).stop();
271 return;
272 }
273 var success = false;
274 if(x.key == "a" || x.key == "A") {
275 var highestNum = 0;
276 var highestIndex = -1;
277 success = true;
278
279 new Event(x).stop();
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 },
312 getInputFocused: function(x) {
313 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
314 return false;
315 return true;
316 },
317 newCustomWindow: function(name, select, type) {
318 if(!type)
319 type = qwebirc.ui.WINDOW_CUSTOM;
320
321 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
322 w.addEvent("close", function(w) {
323 delete this.windows[qwebirc.ui.CUSTOM_CLIENT][w.identifier];
324 }.bind(this));
325
326 if(select)
327 this.selectWindow(w);
328
329 return w;
330 },
331 addCustomWindow: function(windowName, class_, cssClass, options, type) {
332 if(!$defined(options))
333 options = {};
334
335 if(this.customWindows[windowName]) {
336 this.selectWindow(this.customWindows[windowName]);
337 return;
338 }
339
340 var d = this.newCustomWindow(windowName, true, type);
341 this.customWindows[windowName] = d;
342
343 d.addEvent("close", function() {
344 this.customWindows[windowName] = null;
345 }.bind(this));
346
347 if(cssClass)
348 d.lines.addClass("qwebirc-" + cssClass);
349
350 var ew = new class_(d.lines, options);
351 ew.addEvent("close", function() {
352 d.close();
353 }.bind(this));
354
355 d.setSubWindow(ew);
356 },
357 embeddedWindow: function() {
358 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
359 this.optionsWindow();
360 }.bind(this)});
361 },
362 optionsWindow: function() {
363 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
364 },
365 aboutWindow: function() {
366 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
367 },
368 privacyWindow: function() {
369 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
370 },
371 feedbackWindow: function() {
372 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
373 },
374 faqWindow: function() {
375 this.addCustomWindow("FAQ", qwebirc.ui.FAQPane, "faqpane", this.uiOptions);
376 },
377 urlDispatcher: function(name, window) {
378 if(name == "embedded")
379 return ["a", this.embeddedWindow.bind(this)];
380
381 if(name == "options")
382 return ["a", this.optionsWindow.bind(this)];
383
384 /* doesn't really belong here */
385 if(name == "whois") {
386 return ["span", function(nick) {
387 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
388 window.client.exec("/QUERY " + nick);
389 } else {
390 window.client.exec("/WHOIS " + nick);
391 }
392 }.bind(this)];
393 }
394
395 return null;
396 },
397 tabComplete: function(element) {
398 this.tabCompleter.tabComplete(element);
399 },
400 resetTabComplete: function() {
401 this.tabCompleter.reset();
402 },
403 setModifiableStylesheet: function(name) {
404 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + name + qwebirc.FILE_SUFFIX + ".mcss");
405 this.setModifiableStylesheetValues({});
406 },
407 setModifiableStylesheetValues: function(values) {
408 for(var k in values)
409 this.__styleValues[k] = values[k];
410
411 if(!$defined(this.__styleSheet))
412 return;
413
414 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
415 var front = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
416
417 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
418 front = back;
419
420 var colours = {
421 back: back,
422 front: front
423 };
424
425 this.__styleSheet.set(function() {
426 var mode = arguments[0];
427 if(mode == "c") {
428 var t = colours[arguments[2]];
429 var x = new Color(arguments[1]);
430 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
431 if(c == "255,255,255") /* IE confuses white with transparent... */
432 c = "255,255,254";
433
434 return "rgb(" + c + ")";
435 } else if(mode == "o") {
436 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
437 }
438 }.bind(this));
439 }
440 });
441
442 qwebirc.ui.NotificationUI = new Class({
443 Extends: qwebirc.ui.StandardUI,
444 initialize: function(parentElement, windowClass, uiName, options) {
445 this.parent(parentElement, windowClass, uiName, options);
446
447 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
448 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
449
450 this.beep = this.__beeper.beep.bind(this.__beeper);
451
452 this.flash = this.__flasher.flash.bind(this.__flasher);
453 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
454 },
455 setBeepOnMention: function(value) {
456 if(value)
457 this.__beeper.soundInit();
458 },
459 updateTitle: function(text) {
460 if(this.__flasher.updateTitle(text))
461 this.parent(text);
462 },
463 focusChange: function(value) {
464 this.parent(value);
465 this.__flasher.focusChange(value);
466 }
467 });
468
469 qwebirc.ui.QuakeNetUI = new Class({
470 Extends: qwebirc.ui.NotificationUI,
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 for(var client in this.clients) {
484 this.clients[client].quit("Logged out");
485 };
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