]> jfr.im git - irc/quakenet/qwebirc.git/blame_incremental - js/ui/baseui.js
add spinner to connect dialog
[irc/quakenet/qwebirc.git] / js / ui / baseui.js
... / ...
CommitLineData
1qwebirc.ui.WINDOW_STATUS = 0x01;
2qwebirc.ui.WINDOW_QUERY = 0x02;
3qwebirc.ui.WINDOW_CHANNEL = 0x04;
4qwebirc.ui.WINDOW_CUSTOM = 0x08;
5qwebirc.ui.WINDOW_CONNECT = 0x10;
6qwebirc.ui.WINDOW_MESSAGES = 0x20;
7
8qwebirc.ui.CUSTOM_CLIENT = "custom";
9qwebirc.ui.DEFAULT_HUE = 210; /* nice blue */
10
11qwebirc.ui.BaseUI = new Class({
12 Implements: [Events],
13 initialize: function(parentElement, windowClass, uiName, options) {
14 this.options = options;
15
16 this.windows = new QHash();
17 this.clients = new QHash();
18 this.windows.put(qwebirc.ui.CUSTOM_CLIENT, new QHash());
19 this.windowArray = [];
20 this.windowClass = windowClass;
21 this.parentElement = parentElement;
22 this.parentElement.addClass("qwebirc");
23 this.parentElement.addClass("qwebirc-" + uiName);
24 this.firstClient = false;
25 this.commandhistory = new qwebirc.irc.CommandHistory();
26 this.clientId = 0;
27
28 this.windowFocused = true;
29
30 if(Browser.Engine.trident) {
31 var checkFocus = function() {
32 var hasFocus = document.hasFocus();
33 if(hasFocus != this.windowFocused) {
34 this.windowFocused = hasFocus;
35 this.focusChange(hasFocus);
36 }
37 }
38
39 checkFocus.periodical(100, this);
40 } else {
41 var blur = function() { if(this.windowFocused) { this.windowFocused = false; this.focusChange(false); } }.bind(this);
42 var focus = function() { if(!this.windowFocused) { this.windowFocused = true; this.focusChange(true); } }.bind(this);
43
44 /* firefox requires both */
45
46 document.addEvent("blur", blur);
47 window.addEvent("blur", blur);
48 document.addEvent("focus", focus);
49 window.addEvent("focus", focus);
50 }
51
52 qwebirc.util.__log = function(x) {
53 if(QWEBIRC_DEBUG) {
54 if(typeof console != "undefined")
55 console.log(x);
56 this.getActiveWindow().addLine(null, x);
57 }
58 }.bind(this);
59 },
60 newClient: function(client) {
61 client.id = String(this.clientId++);
62 client.hilightController = new qwebirc.ui.HilightController(client);
63 client.addEvent("signedOn", function() {
64 this.poller = new qwebirc.xdomain.Poller(this.oobMessage.bind(this));
65 this.fireEvent("signedOn", client);
66 }.bind(this));
67 this.windows.put(client.id, new QHash());
68 this.clients.put(client.id, client);
69 var w = this.newWindow(client, qwebirc.ui.WINDOW_STATUS, "Status");
70 this.selectWindow(w);
71 if(!this.firstClient) {
72 this.firstClient = true;
73 w.addLine("", "qwebirc v" + qwebirc.VERSION);
74 w.addLine("", "Copyright (C) 2008-2017 Chris Porter and the qwebirc project.");
75 w.addLine("", "http://www.qwebirc.org");
76 w.addLine("", "Licensed under the GNU General Public License, Version 2.");
77 }
78 return w;
79 },
80 getClientId: function(client) {
81 if(client == qwebirc.ui.CUSTOM_CLIENT) {
82 return qwebirc.ui.CUSTOM_CLIENT;
83 } else {
84 return client.id;
85 }
86 },
87 getWindowIdentifier: function(client, type, name) {
88 if(type == qwebirc.ui.WINDOW_MESSAGES)
89 return "-M";
90 if(type == qwebirc.ui.WINDOW_STATUS)
91 return "";
92
93 if(client == qwebirc.ui.CUSTOM_CLIENT) /* HACK */
94 return "_" + name;
95
96 return "_" + client.toIRCLower(name);
97 },
98 newWindow: function(client, type, name) {
99 var w = this.getWindow(client, type, name);
100 if($defined(w))
101 return w;
102
103 var wId = this.getWindowIdentifier(client, type, name);
104 var w = new this.windowClass(this, client, type, name, wId);
105 this.windows.get(this.getClientId(client)).put(wId, w);
106 this.windowArray.push(w);
107
108 return w;
109 },
110 getWindow: function(client, type, name) {
111 var c = this.windows.get(this.getClientId(client));
112 if(!$defined(c))
113 return null;
114
115 return c.get(this.getWindowIdentifier(client, type, name));
116 },
117 getActiveWindow: function() {
118 return this.active;
119 },
120 getStatusWindow: function(client) {
121 return this.windows.get(this.getClientId(client)).get(this.getWindowIdentifier(client, qwebirc.ui.WINDOW_STATUS));
122 },
123 getActiveIRCWindow: function(client) {
124 if(!this.active || this.active.type == qwebirc.ui.WINDOW_CUSTOM) {
125 return this.getStatusWindow(client);
126 } else {
127 return this.active;
128 }
129 },
130 __setActiveWindow: function(window) {
131 this.active = window;
132 },
133 renameWindow: function(window, name) {
134 if(this.getWindow(window.client, window.type, name))
135 return null;
136
137 var clientId = this.getClientId(window.client);
138 var index = this.windowArray.indexOf(window);
139 if(index == -1)
140 return null;
141
142 this.windows.get(clientId).remove(window.identifier);
143
144 var window = this.windowArray[index];
145 window.name = name;
146 window.identifier = this.getWindowIdentifier(window.client, window.type, window.name);
147
148 this.windows.get(clientId).put(window.identifier, this.windowArray[index]);
149
150 if(window.active)
151 this.updateTitle(window.name + " - " + this.options.appTitle);
152
153 window.rename(window.name);
154 return window;
155 },
156 selectWindow: function(window) {
157 if(this.active)
158 this.active.deselect();
159 window.select(); /* calls setActiveWindow */
160 this.updateTitle(window.name + " - " + this.options.appTitle);
161 },
162 updateTitle: function(text) {
163 document.title = text;
164 },
165 nextWindow: function(direction) {
166 if(this.windowArray.length == 0 || !this.active)
167 return;
168
169 if(!direction)
170 direction = 1;
171
172 var index = this.windowArray.indexOf(this.active);
173 if(index == -1)
174 return;
175
176 index = index + direction;
177 if(index < 0) {
178 index = this.windowArray.length - 1;
179 } else if(index >= this.windowArray.length) {
180 index = 0;
181 }
182
183 this.selectWindow(this.windowArray[index]);
184 },
185 prevWindow: function() {
186 this.nextWindow(-1);
187 },
188 __closed: function(window) {
189 if(window.active) {
190 this.active = undefined;
191 if(this.windowArray.length == 1) {
192 this.windowArray = [];
193 } else {
194 var index = this.windowArray.indexOf(window);
195 if(index == -1) {
196 return;
197 } else if(index == 0) {
198 this.selectWindow(this.windowArray[1]);
199 } else {
200 this.selectWindow(this.windowArray[index - 1]);
201 }
202 }
203 }
204
205 this.windowArray = this.windowArray.erase(window);
206 this.windows.get(this.getClientId(window.client)).remove(window.identifier);
207 },
208 /*
209 this shouldn't be called by overriding classes!
210 they should implement their own!
211 some form of user input MUST be received before an
212 IRC connection is made, else users are going to get
213 tricked into getting themselves glined
214 */
215 loginBox: function(callback, initialNickname, initialChannels, autoConnect, autoNick) {
216 this.postInitialize();
217
218 this.addCustomWindow("Connect", qwebirc.ui.ConnectPane, "connectpane", {
219 initialNickname: initialNickname, initialChannels: initialChannels, autoConnect: autoConnect, callback: callback, autoNick: autoNick,
220 uiOptions: this.options
221 }, qwebirc.ui.WINDOW_CONNECT);
222 },
223 focusChange: function(newValue) {
224 var window_ = this.getActiveWindow();
225 if($defined(window_))
226 window_.focusChange(newValue);
227 },
228 oobMessage: function(message) {
229 var c = message.splitMax(" ", 2);
230 if(c.length != 2)
231 return;
232
233 var command = c[0];
234 if(command != "CMD")
235 return;
236
237 var d = c[1].splitMax(" ", 2);
238 if(d.length != 2)
239 return;
240
241 var command = d[0];
242 var args = d[1];
243 if(command == "SAY") {
244 var w = this.getActiveIRCWindow();
245 if($defined(w) && (w.type == qwebirc.ui.WINDOW_CHANNEL || w.type == qwebirc.ui.WINDOW_QUERY)) {
246 w.client.exec("/SAY " + args);
247 return;
248 }
249 }
250 }
251});
252
253qwebirc.ui.StandardUI = new Class({
254 Extends: qwebirc.ui.BaseUI,
255 initialize: function(parentElement, windowClass, uiName, options) {
256 this.parent(parentElement, windowClass, uiName, options);
257
258 this.UICommands = this.__build_menu_items(options);
259
260 this.__styleValues = {hue: qwebirc.ui.DEFAULT_HUE, saturation: 0, lightness: 0, textHue: null, textSaturation: null, textLightness: null};
261 if($defined(this.options.hue)) this.__styleValues.hue = this.options.hue;
262 this.tabCompleter = new qwebirc.ui.TabCompleterFactory(this);
263 this.uiOptions = new qwebirc.ui.DefaultOptionsClass(this, options.uiOptionsArg);
264 this.customWindows = new QHash();
265
266 if($defined(this.options.saturation)) this.__styleValues.saturation = this.options.saturation;
267 if($defined(this.options.lightness)) this.__styleValues.lightness = this.options.lightness;
268 if($defined(this.options.tsaturation)) this.__styleValues.textSaturation = this.options.tsaturation;
269 if($defined(this.options.tlightness)) this.__styleValues.textLightness = this.options.tlightness;
270
271 if($defined(this.options.hue)) { /* overridden in url */
272 /* ugh... this will go away when we add proper options for hue/sat/light for text and background */
273 this.uiOptions.setValueByPrefix("STYLE_HUE", this.__styleValues.hue);
274 } else {
275 this.__styleValues.hue = this.uiOptions.STYLE_HUE; /* otherwise copy from serialised store */
276 }
277 this.__styleValues.textHue = $defined(this.options.thue) ? this.options.thue : this.__styleValues.hue;
278
279 document.addEvent("keydown", this.__handleHotkey.bind(this));
280 },
281 __build_menu_items: function(options) {
282 var r = [];
283 var seenAbout = null;
284
285 for(var i=0;i<qwebirc.ui.UI_COMMANDS_P1.length;i++)
286 r.push([true, qwebirc.ui.UI_COMMANDS_P1[i]]);
287 for(var i=0;i<options.customMenuItems.length;i++)
288 r.push([false, options.customMenuItems[i]]);
289 for(var i=0;i<qwebirc.ui.UI_COMMANDS_P2.length;i++)
290 r.push([true, qwebirc.ui.UI_COMMANDS_P2[i]]);
291
292 var r2 = []
293 for(var i=0;i<r.length;i++) {
294 var preset = r[i][0], c = r[i][1];
295
296 if(c[0] == "About qwebirc") { /* HACK */
297 if(!preset) {
298 seenAbout = c;
299 continue;
300 } else if(seenAbout) {
301 c = seenAbout;
302 preset = false;
303 }
304 }
305
306 if(preset) {
307 r2.push([c[0], this[c[1] + "Window"].bind(this)]);
308 } else {
309 r2.push([c[0], (function(c) { return function() {
310 this.addCustomWindow(c[0], qwebirc.ui.URLPane, "urlpane", {url: c[1]});
311 }.bind(this); }).call(this, c)]);
312 }
313 }
314
315 return r2;
316 },
317 __handleHotkey: function(x) {
318 var success = false;
319 if(!x.alt && !x.control && !x.shift && !x.meta) {
320 if((x.key == "backspace" || x.key == "/") && !this.getInputFocused(x)) {
321 success = true;
322 }
323 } else if(!x.alt || x.control || x.meta) {
324 /* do nothing */
325 } else if(x.key == "a" || x.key == "A") {
326 var highestNum = 0;
327 var highestIndex = -1;
328 success = true;
329
330 for(var i=0;i<this.windowArray.length;i++) {
331 var h = this.windowArray[i].hilighted;
332 if(h > highestNum) {
333 highestIndex = i;
334 highestNum = h;
335 }
336 }
337 if(highestIndex > -1)
338 this.selectWindow(this.windowArray[highestIndex]);
339 } else if((x.key >= '0' && x.key <= '9') && !x.shift) {
340 success = true;
341
342 number = x.key - '0';
343 if(number == 0)
344 number = 10
345
346 number = number - 1;
347
348 if(number >= this.windowArray.length)
349 return;
350
351 this.selectWindow(this.windowArray[number]);
352 } else if((x.key == "left" || x.key == "up") && !x.shift) {
353 this.prevWindow();
354 success = true;
355 } else if((x.key == "right" || x.key == "down") && !x.shift) {
356 this.nextWindow();
357 success = true;
358 }
359
360 if(success) {
361 new Event(x).stop();
362 x.preventDefault();
363 }
364 },
365 getInputFocused: function(x) {
366 if($$("input").indexOf(x.target) == -1 && $$("textarea").indexOf(x.target) == -1)
367 return false;
368 return true;
369 },
370 newCustomWindow: function(name, select, type) {
371 if(!type)
372 type = qwebirc.ui.WINDOW_CUSTOM;
373
374 var w = this.newWindow(qwebirc.ui.CUSTOM_CLIENT, type, name);
375 w.addEvent("close", function(w) {
376 this.windows.get(qwebirc.ui.CUSTOM_CLIENT).remove(w.identifier);
377 }.bind(this));
378
379 if(select)
380 this.selectWindow(w);
381
382 return w;
383 },
384 addCustomWindow: function(windowName, class_, cssClass, options, type) {
385 if(!$defined(options))
386 options = {};
387
388 if(this.customWindows.contains(windowName)) {
389 this.selectWindow(this.customWindows.get(windowName));
390 return;
391 }
392
393 var d = this.newCustomWindow(windowName, true, type);
394 this.customWindows.put(windowName, d);
395
396 d.addEvent("close", function() {
397 this.customWindows.remove(windowName);
398 }.bind(this));
399
400 if(cssClass)
401 d.lines.addClass("qwebirc-" + cssClass);
402
403 var ew = new class_(d.lines, options);
404 ew.addEvent("close", function() {
405 d.close();
406 }.bind(this));
407
408 d.setSubWindow(ew);
409 },
410 embeddedWindow: function() {
411 this.addCustomWindow("Add webchat to your site", qwebirc.ui.EmbedWizard, "embeddedwizard", {baseURL: this.options.baseURL, uiOptions: this.uiOptions, optionsCallback: function() {
412 this.optionsWindow();
413 }.bind(this)});
414 },
415 optionsWindow: function() {
416 this.addCustomWindow("Options", qwebirc.ui.OptionsPane, "optionspane", this.uiOptions);
417 },
418 aboutWindow: function() {
419 this.addCustomWindow("About qwebirc", qwebirc.ui.AboutPane, "aboutpane", this.uiOptions);
420 },
421 feedbackWindow: function() {
422 this.addCustomWindow("Feedback", qwebirc.ui.FeedbackPane, "feedbackpane", this.uiOptions);
423 },
424 urlDispatcher: function(name, window) {
425 if(name == "embedded")
426 return ["a", this.embeddedWindow.bind(this)];
427
428 if(name == "options")
429 return ["a", this.optionsWindow.bind(this)];
430
431 /* doesn't really belong here */
432 if(name == "whois") {
433 return ["span", function(nick) {
434 if(this.uiOptions.QUERY_ON_NICK_CLICK) {
435 window.client.exec("/QUERY " + nick);
436 } else {
437 window.client.exec("/WHOIS " + nick);
438 }
439 }.bind(this)];
440 }
441
442 return null;
443 },
444 tabComplete: function(element, backwards) {
445 this.tabCompleter.tabComplete(element, backwards);
446 },
447 resetTabComplete: function() {
448 this.tabCompleter.reset();
449 },
450 setModifiableStylesheet: function(name) {
451 this.__styleSheet = new qwebirc.ui.style.ModifiableStylesheet(qwebirc.global.staticBaseURL + "css/" + (QWEBIRC_DEBUG ? "debug/" : "") + name + qwebirc.FILE_SUFFIX + ".mcss");
452 this.setModifiableStylesheetValues({});
453 },
454 setModifiableStylesheetValues: function(values) {
455 for (var k in values)
456 this.__styleValues[k] = values[k];
457
458 if (!$defined(this.__styleSheet))
459 return;
460
461 var back = {hue: this.__styleValues.hue, lightness: this.__styleValues.lightness, saturation: this.__styleValues.saturation};
462 var front;
463 if (!$defined(this.__styleValues.textHue) && !$defined(this.__styleValues.textLightness) && !$defined(this.__styleValues.textSaturation)) {
464 front = back;
465 } else {
466 front = {hue: Number(this.__styleValues.textHue), lightness: Number(this.__styleValues.textLightness), saturation: Number(this.__styleValues.textSaturation)}
467 }
468 var colours = {
469 back: back,
470 front: front
471 };
472
473 this.__styleSheet.set(function() {
474 var mode = arguments[0];
475 if(mode == "c") {
476 var t = colours[arguments[2]];
477 var x = new Color(arguments[1]);
478 var c = x.setHue(t.hue).setSaturation(x.hsb[1] + t.saturation).setBrightness(x.hsb[2] + t.lightness);
479 if(c == "255,255,255") /* IE confuses white with transparent... */
480 c = "255,255,254";
481
482 return "rgb(" + c + ")";
483 } else if(mode == "o") {
484 return this.uiOptions[arguments[1]] ? arguments[2] : arguments[3];
485 }
486 }.bind(this));
487 }
488});
489
490qwebirc.ui.NotificationUI = new Class({
491 Extends: qwebirc.ui.StandardUI,
492 initialize: function(parentElement, windowClass, uiName, options) {
493 this.parent(parentElement, windowClass, uiName, options);
494
495 this.__beeper = new qwebirc.ui.Beeper(this.uiOptions);
496 this.__flasher = new qwebirc.ui.Flasher(this.uiOptions);
497 this.__notifier = new qwebirc.ui.Notifier(this.uiOptions);
498
499 this.cancelFlash = this.__flasher.cancelFlash.bind(this.__flasher);
500 },
501 beep: function() {
502 this.__beeper.beep();
503 },
504 notify: function(title, message, callback) {
505 this.__beeper.beep();
506 this.__flasher.flash();
507 this.__notifier.notify(title, message, callback);
508 },
509 setBeepOnMention: function(value) {
510 if(value)
511 this.__beeper.soundInit();
512 },
513 setNotifications: function(value) {
514 this.__notifier.setEnabled(value);
515 },
516 updateTitle: function(text) {
517 if(this.__flasher.updateTitle(text))
518 this.parent(text);
519 },
520 focusChange: function(value) {
521 this.parent(value);
522 this.__flasher.focusChange(value);
523 this.__notifier.focusChange(value);
524 }
525});
526
527qwebirc.ui.QuakeNetUI = new Class({
528 Extends: qwebirc.ui.NotificationUI,
529 urlDispatcher: function(name, window) {
530 if(name == "qwhois") {
531 return ["span", function(auth) {
532 if($defined(this.parentObject.options.accountWhoisCommand))
533 this.client.exec(this.parentObject.options.accountWhoisCommand + auth);
534 }.bind(window)];
535 }
536 return this.parent(name, window);
537 },
538 logout: function() {
539 if(!qwebirc.auth.loggedin())
540 return;
541 if(confirm("Log out?")) {
542 this.clients.each(function(k, v) {
543 v.quit("Logged out");
544 }, this);
545
546 /* HACK */
547 var foo = function() { document.location = qwebirc.global.dynamicBaseURL + "auth?logout=1"; };
548 foo.delay(500);
549 }
550 }
551});
552
553qwebirc.ui.RootUI = qwebirc.ui.QuakeNetUI;
554
555qwebirc.ui.RequestTransformHTML = function(options) {
556 var HREF_ELEMENTS = {
557 "IMG": 1
558 };
559
560 var update = options.update;
561 var onSuccess = options.onSuccess;
562
563 var fixUp = function(node) {
564 if(node.nodeType != 1)
565 return;
566
567 var tagName = node.nodeName.toUpperCase();
568 if(HREF_ELEMENTS[tagName]) {
569 var attr = node.getAttribute("transform_attr");
570 var value = node.getAttribute("transform_value");
571 if($defined(attr) && $defined(value)) {
572 node.removeAttribute("transform_attr");
573 node.removeAttribute("transform_value");
574 node.setAttribute(attr, qwebirc.global.staticBaseURL + value);
575 }
576 }
577
578 for(var i=0;i<node.childNodes.length;i++)
579 fixUp(node.childNodes[i]);
580 };
581
582 delete options["update"];
583 options.onSuccess = function(tree, elements, html, js) {
584 var container = new Element("div");
585 container.set("html", html);
586 fixUp(container);
587 update.empty();
588
589 while(container.childNodes.length > 0) {
590 var x = container.firstChild;
591 container.removeChild(x);
592 update.appendChild(x);
593 }
594 onSuccess();
595 };
596
597 return new Request.HTML(options);
598};
599