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