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