]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/ui/baseui.js
Fixes issue #174.
[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 qwebirc.ui.GenericLoginBox(this.parentElement, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
201 },
202 focusChange: function(newValue) {
203 var window_ = this.getActiveWindow();
204 if($defined(window_))
205 window_.focusChange(newValue);
206 }
207 });
208
209 qwebirc.ui.StandardUI = new Class({
210 Extends: qwebirc.ui.BaseUI,
211 UICommands: qwebirc.ui.UI_COMMANDS,
212 initialize: function(parentElement, windowClass, uiName, options) {
213 this.parent(parentElement, windowClass, uiName, options);
214
215 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
216 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
217 this.customWindows = {};
218
219 this.__styleValues = {hue: this.uiOptions.STYLE_HUE, saturation: 0, lightness: 0};
220 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
221 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
222 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
223
224 if(this.options.thue !== null) this.__styleValues.textHue = this.options.thue;
225 if(this.options.tsaturation !== null) this.__styleValues.textSaturation = this.options.tsaturation;
226 if(this.options.tlightness !== null) this.__styleValues.textLightness = this.options.tlightness;
227
228 var ev;
229 if(Browser.Engine.trident) {
230 ev = "keydown";
231 } else {
232 ev = "keypress";
233 }
234 document.addEvent(ev, this.__handleHotkey.bind(this));
235 },
236 __handleHotkey: function(x) {
237 if(!x.alt || x.control) {
238 if(x.key == "backspace" || x.key == "/")
239 if(!this.getInputFocused(x))
240 new Event(x).stop();
241 return;
242 }
243 var success = false;
244 if(x.key == "a" || x.key == "A") {
245 var highestNum = 0;
246 var highestIndex = -1;
247 success = true;
248
249 new Event(x).stop();
250 for(var i=0;i<this.windowArray.length;i++) {
251 var h = this.windowArray[i].hilighted;
252 if(h > highestNum) {
253 highestIndex = i;
254 highestNum = h;
255 }
256 }
257 if(highestIndex > -1)
258 this.selectWindow(this.windowArray[highestIndex]);
259 } else if(x.key >= '0' && x.key <= '9') {
260 success = true;
261
262 number = x.key - '0';
263 if(number == 0)
264 number = 10
265
266 number = number - 1;
267
268 if(number >= this.windowArray.length)
269 return;
270
271 this.selectWindow(this.windowArray[number]);
272 } else if(x.key == "left") {
273 this.prevWindow();
274 success = true;
275 } else if(x.key == "right") {
276 this.nextWindow();
277 success = true;
278 }
279 if(success)
280 new Event(x).stop();
281 },
282 getInputFocused: function(x) {
283 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
284 return false;
285 return true;
286 },
287 newCustomWindow: function(name, select, type) {
288 if(!type)
289 type = qwebirc.ui.WINDOW_CUSTOM;
290
291 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
292 w.addEvent("close", function(w) {
293 delete this.windows[qwebirc.ui.CUSTOM_CLIENT][w.identifier];
294 }.bind(this));
295
296 if(select)
297 this.selectWindow(w);
298
299 return w;
300 },
301 addCustomWindow: function(windowName, class_, cssClass, options) {
302 if(!$defined(options))
303 options = {};
304
305 if(this.customWindows[windowName]) {
306 this.selectWindow(this.customWindows[windowName]);
307 return;
308 }
309
310 var d = this.newCustomWindow(windowName, true);
311 this.customWindows[windowName] = d;
312
313 d.addEvent("close", function() {
314 this.customWindows[windowName] = null;
315 }.bind(this));
316
317 if(cssClass)
318 d.lines.addClass("qwebirc-" + cssClass);
319
320 var ew = new class_(d.lines, options);
321 ew.addEvent("close", function() {
322 d.close();
323 }.bind(this));
324
325 d.setSubWindow(ew);
326 },
327 embeddedWindow: function() {
328 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
329 this.optionsWindow();
330 }.bind(this)});
331 },
332 optionsWindow: function() {
333 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
334 },
335 aboutWindow: function() {
336 this.addCustomWindow("About", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
337 },
338 privacyWindow: function() {
339 this.addCustomWindow("Privacy policy", qwebirc.ui.PrivacyPolicyPane, "privacypolicypane", this.uiOptions);
340 },
341 feedbackWindow: function() {
342 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
343 },
344 faqWindow: function() {
345 this.addCustomWindow("FAQ", qwebirc.ui.FAQPane, "faqpane", this.uiOptions);
346 },
347 urlDispatcher: function(name, window) {
348 if(name == "embedded")
349 return ["a", this.embeddedWindow.bind(this)];
350
351 if(name == "options")
352 return ["a", this.optionsWindow.bind(this)];
353
354 /* doesn't really belong here */
355 if(name == "whois") {
356 return ["span", function(nick) {
357 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
358 window.client.exec("/QUERY " + nick);
359 } else {
360 window.client.exec("/WHOIS " + nick);
361 }
362 }.bind(this)];
363 }
364
365 return null;
366 },
367 tabComplete: function(element) {
368 this.tabCompleter.tabComplete(element);
369 },
370 resetTabComplete: function() {
371 this.tabCompleter.reset();
372 },
373 setModifiableStylesheet: function(name) {
374 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + name + qwebirc.FILE_SUFFIX + ".mcss");
375 this.setModifiableStylesheetValues({});
376 },
377 setModifiableStylesheetValues: function(values) {
378 for(var k in values)
379 this.__styleValues[k] = values[k];
380
381 if(!$defined(this.__styleSheet))
382 return;
383
384 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
385 var front = {hue: this.__styleValues.textHue, lightness: this.__styleValues.textLightness, saturation: this.__styleValues.textSaturation};
386
387 if(!this.__styleValues.textHue && !this.__styleValues.textLightness && !this.__styleValues.textSaturation)
388 front = back;
389
390 var colours = {
391 back: back,
392 front: front
393 };
394
395 this.__styleSheet.set(function() {
396 var mode = arguments[0];
397 if(mode == "c") {
398 var t = colours[arguments[2]];
399 var x = new Color(arguments[1]);
400 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
401 if(c == "255,255,255") /* IE confuses white with transparent... */
402 c = "255,255,254";
403
404 return "rgb(" + c + ")";
405 } else if(mode == "o") {
406 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
407 }
408 }.bind(this));
409 }
410 });
411
412 qwebirc.ui.NotificationUI = new Class({
413 Extends: qwebirc.ui.StandardUI,
414 initialize: function(parentElement, windowClass, uiName, options) {
415 this.parent(parentElement, windowClass, uiName, options);
416
417 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
418 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
419
420 this.beep = this.__beeper.beep.bind(this.__beeper);
421
422 this.flash = this.__flasher.flash.bind(this.__flasher);
423 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
424 },
425 setBeepOnMention: function(value) {
426 if(value)
427 this.__beeper.soundInit();
428 },
429 updateTitle: function(text) {
430 if(this.__flasher.updateTitle(text))
431 this.parent(text);
432 },
433 focusChange: function(value) {
434 this.parent(value);
435 this.__flasher.focusChange(value);
436 }
437 });
438
439 qwebirc.ui.NewLoginUI = new Class({
440 Extends: qwebirc.ui.NotificationUI,
441 loginBox: function(callbackfn, initialNickname, initialChannels, autoConnect, autoNick) {
442 this.postInitialize();
443
444 /* I'd prefer something shorter and snappier! */
445 var w = this.newCustomWindow("Connection details", true, qwebirc.ui.WINDOW_CONNECT);
446 var callback = function(args) {
447 w.close();
448 callbackfn(args);
449 };
450
451 qwebirc.ui.GenericLoginBox(w.lines, callback, initialNickname, initialChannels, autoConnect, autoNick, this.options.networkName);
452 }
453 });
454
455 qwebirc.ui.QuakeNetUI = new Class({
456 Extends: qwebirc.ui.NewLoginUI,
457 urlDispatcher: function(name, window) {
458 if(name == "qwhois") {
459 return ["span", function(auth) {
460 this.client.exec("/MSG Q whois #" + auth);
461 }.bind(window)];
462 }
463 return this.parent(name, window);
464 },
465 logout: function() {
466 if(!qwebirc.auth.loggedin())
467 return;
468 if(confirm("Log out?")) {
469 for(var client in this.clients) {
470 this.clients[client].quit("Logged out");
471 };
472
473 /* HACK */
474 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
475 foo.delay(500);
476 }
477 }
478 });
479
480 qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
481
482 qwebirc.ui.RequestTransformHTML = function(options) {
483 var HREF_ELEMENTS = {
484 "IMG": 1
485 };
486
487 var update = options.update;
488 var onSuccess = options.onSuccess;
489
490 var fixUp = function(node) {
491 if(node.nodeType != 1)
492 return;
493
494 var tagName = node.nodeName.toUpperCase();
495 if(HREF_ELEMENTS[tagName]) {
496 var attr = node.getAttribute("transform_attr");
497 var value = node.getAttribute("transform_value");
498 if($defined(attr) && $defined(value)) {
499 node.removeAttribute("transform_attr");
500 node.removeAttribute("transform_value");
501 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
502 }
503 }
504
505 for(var i=0;i<node.childNodes.length;i++)
506 fixUp(node.childNodes[i]);
507 };
508
509 delete options["update"];
510 options.onSuccess = function(tree, elements, html, js) {
511 var container = new Element("div");
512 container.set("html", html);
513 fixUp(container);
514 update.empty();
515
516 while(container.childNodes.length > 0) {
517 var x = container.firstChild;
518 container.removeChild(x);
519 update.appendChild(x);
520 }
521 onSuccess();
522 };
523
524 return new Request.HTML(options);
525 };
526