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