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