]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/ui/frontends/qui.js
make hue overridding more logical
[irc/quakenet/qwebirc.git] / js / ui / frontends / qui.js
1 qwebirc.ui.QUI = new Class({
2 Extends: qwebirc.ui.RootUI,
3 initialize: function(parentElement, theme, options) {
4 this.parent(parentElement, qwebirc.ui.QUI.Window, "qui", options);
5 this.theme = theme;
6 this.parentElement = parentElement;
7 this.setModifiableStylesheet("qui");
8 },
9 postInitialize: function() {
10 this.qjsui = new qwebirc.ui.QUI.JSUI("qwebirc-qui", this.parentElement);
11 this.qjsui.addEvent("reflow", function() {
12 var w = this.getActiveWindow();
13 if($defined(w))
14 w.onResize();
15 }.bind(this));
16 this.qjsui.top.addClass("outertabbar");
17 this.qjsui.left.addClass("outertabbar");
18
19 this.qjsui.top.addClass("outertabbar_top");
20 this.qjsui.left.addClass("outertabbar_left");
21
22 this.qjsui.bottom.addClass("input");
23 this.qjsui.right.addClass("nicklist");
24 this.qjsui.topic.addClass("topic");
25 this.qjsui.middle.addClass("lines");
26
27 this.outerTabs = new Element("div");
28 this.sideTabs = null;
29
30 this.tabs = new Element("div");
31 this.tabs.addClass("tabbar");
32
33 this.__createDropdownMenu();
34
35 this.outerTabs.appendChild(this.tabs);
36 this.origtopic = this.topic = this.qjsui.topic;
37 this.lines = this.qjsui.middle;
38 this.orignicklist = this.nicklist = this.qjsui.right;
39
40 this.input = this.qjsui.bottom;
41 this.reflow = this.qjsui.reflow.bind(this.qjsui);
42
43 var scrollHandler = function(x) {
44 var event = new Event(x);
45 var up, down;
46 if(this.sideTabs) {
47 var p = this.qjsui.left;
48
49 /* don't scroll if we're scrollable */
50 if(p.getScrollSize().y > p.clientHeight)
51 return;
52
53 up = event.wheel < 0;
54 down = event.wheel > 0;
55 } else {
56 up = event.wheel > 0;
57 down = event.wheel < 0;
58 }
59
60 if(up) {
61 this.nextWindow();
62 } else if(down) {
63 this.prevWindow();
64 }
65 event.stop();
66 }.bind(this);
67 this.qjsui.left.addEvent("mousewheel", scrollHandler);
68 this.qjsui.top.addEvent("mousewheel", scrollHandler);
69
70 this.createInput();
71 this.reflow();
72 this.reflow.delay(100); /* Konqueror fix */
73 this.setSideTabs(this.uiOptions.SIDE_TABS);
74
75 },
76 newWindow: function(client, type, name) {
77 var w = this.parent(client, type, name);
78 w.setSideTabs(this.sideTabs);
79 return w;
80 },
81 __createDropdownMenu: function() {
82 var dropdownMenu = new Element("span");
83 dropdownMenu.addClass("dropdownmenu");
84
85 dropdownMenu.hide = function() {
86 dropdownMenu.setStyle("display", "none");
87 dropdownMenu.visible = false;
88 document.removeEvent("mousedown", hideEvent);
89 }.bind(this);
90 var hideEvent = function() { dropdownMenu.hide(); };
91
92 dropdownMenu.hide();
93 this.parentElement.appendChild(dropdownMenu);
94
95 this.UICommands.forEach(function(x) {
96 var text = x[0];
97 var fn = this[x[1] + "Window"].bind(this);
98 var e = new Element("a");
99 e.addEvent("mousedown", function(e) { new Event(e).stop(); });
100 e.addEvent("click", function() {
101 dropdownMenu.hide();
102 fn();
103 });
104 e.set("text", text);
105 dropdownMenu.appendChild(e);
106 }.bind(this));
107
108 var dropdown = new Element("div");
109 dropdown.addClass("dropdown-tab");
110 dropdown.appendChild(new Element("img", {src: qwebirc.global.staticBaseURL + "images/icon.png", title: "menu", alt: "menu"}));
111 dropdown.setStyle("opacity", 1);
112
113 this.outerTabs.appendChild(dropdown);
114 dropdownMenu.show = function(x) {
115 new Event(x).stop();
116
117 if(dropdownMenu.visible) {
118 dropdownMenu.hide();
119 return;
120 }
121 var parentSize = this.outerTabs.parentNode.getSize().y;
122
123 dropdownMenu.setStyle("left", parentSize.x);
124 dropdownMenu.setStyle("top", top-1); /* -1 == top border */
125 dropdownMenu.setStyle("display", "inline-block");
126 dropdownMenu.visible = true;
127
128 document.addEvent("mousedown", hideEvent);
129 }.bind(this);
130 dropdown.addEvent("mousedown", function(e) { new Event(e).stop(); });
131 dropdown.addEvent("click", dropdownMenu.show);
132 },
133 createInput: function() {
134 var form = new Element("form");
135 this.input.appendChild(form);
136
137 form.addClass("input");
138
139 var inputbox = new Element("input");
140 this.addEvent("signedOn", function() {
141 inputbox.placeholder = "chat here! you can also use commands, like /JOIN or /HELP";
142 var d = function() { inputbox.addClass("input-flash"); }.delay(250);
143 var d = function() { inputbox.removeClass("input-flash"); }.delay(500);
144 var d = function() { inputbox.addClass("input-flash"); }.delay(750);
145 var d = function() { inputbox.removeClass("input-flash"); }.delay(1000);
146 var d = function() { inputbox.addClass("input-flash"); }.delay(1250);
147 var d = function() { inputbox.removeClass("input-flash"); }.delay(1750);
148 });
149 form.appendChild(inputbox);
150 this.inputbox = inputbox;
151 this.inputbox.maxLength = 470;
152
153 var sendInput = function() {
154 if(inputbox.value == "")
155 return;
156
157 this.resetTabComplete();
158 this.getActiveWindow().historyExec(inputbox.value);
159 inputbox.value = "";
160 inputbox.placeholder = "";
161 }.bind(this);
162
163 if(!qwebirc.util.deviceHasKeyboard()) {
164 inputbox.addClass("mobile-input");
165 var inputButton = new Element("input", {type: "button"});
166 inputButton.addClass("mobile-button");
167 inputButton.addEvent("click", function() {
168 sendInput();
169 inputbox.focus();
170 });
171 inputButton.value = ">";
172 this.input.appendChild(inputButton);
173 var reflowButton = function() {
174 var containerSize = this.input.getSize();
175 var buttonSize = inputButton.getSize();
176
177 var buttonLeft = containerSize.x - buttonSize.x - 5; /* lovely 5 */
178
179 inputButton.setStyle("left", buttonLeft);
180 inputbox.setStyle("width", buttonLeft - 5);
181 inputButton.setStyle("height", containerSize.y);
182 }.bind(this);
183 this.qjsui.addEvent("reflow", reflowButton);
184 } else {
185 inputbox.addClass("keyboard-input");
186 }
187
188 form.addEvent("submit", function(e) {
189 new Event(e).stop();
190 sendInput();
191 });
192
193 inputbox.addEvent("focus", this.resetTabComplete.bind(this));
194 inputbox.addEvent("mousedown", this.resetTabComplete.bind(this));
195
196 inputbox.addEvent("keydown", function(e) {
197 var resultfn;
198 var cvalue = inputbox.value;
199
200 if(e.key == "up") {
201 resultfn = this.commandhistory.upLine;
202 } else if(e.key == "down") {
203 resultfn = this.commandhistory.downLine;
204 } else if(e.key == "tab" && !e.altKey && !e.ctrlKey && !e.shiftKey) {
205 new Event(e).stop();
206 this.tabComplete(inputbox);
207 return;
208 } else {
209 /* ideally alt and other keys wouldn't break this */
210 this.resetTabComplete();
211 return;
212 }
213
214 this.resetTabComplete();
215 if((cvalue != "") && (this.lastcvalue != cvalue))
216 this.commandhistory.addLine(cvalue, true);
217
218 var result = resultfn.bind(this.commandhistory)();
219
220 new Event(e).stop();
221 if(!result)
222 result = "";
223 this.lastcvalue = result;
224
225 inputbox.value = result;
226 qwebirc.util.setAtEnd(inputbox);
227 }.bind(this));
228 },
229 setLines: function(lines) {
230 this.lines.parentNode.replaceChild(lines, this.lines);
231 this.qjsui.middle = this.lines = lines;
232 },
233 setChannelItems: function(nicklist, topic) {
234 if(!$defined(nicklist)) {
235 nicklist = this.orignicklist;
236 topic = this.origtopic;
237 }
238 this.nicklist.parentNode.replaceChild(nicklist, this.nicklist);
239 this.qjsui.right = this.nicklist = nicklist;
240
241 this.topic.parentNode.replaceChild(topic, this.topic);
242 this.qjsui.topic = this.topic = topic;
243 },
244 setSideTabs: function(value) {
245 if(value === this.sideTabs)
246 return;
247
248 if(this.sideTabs === true) {
249 this.qjsui.left.removeChild(this.outerTabs);
250 } else if(this.sideTabs === false) {
251 this.qjsui.top.removeChild(this.outerTabs);
252 }
253 if(value) {
254 this.qjsui.left.appendChild(this.outerTabs);
255 this.qjsui.top.style.display = "none";
256 this.qjsui.left.style.display = "";
257 } else {
258 this.qjsui.top.appendChild(this.outerTabs);
259 this.qjsui.top.style.display = "";
260 this.qjsui.left.style.display = "none";
261 }
262 this.sideTabs = value;
263 this.windows.each(function(k, v) {
264 v.each(function(k, v2) {
265 v2.setSideTabs(value);
266 });
267 });
268 }
269 });
270
271 qwebirc.ui.QUI.JSUI = new Class({
272 Implements: [Events],
273 initialize: function(class_, parent, sizer) {
274 this.parent = parent;
275 this.sizer = $defined(sizer)?sizer:parent;
276
277 this.class_ = class_;
278 this.create();
279
280 this.reflowevent = null;
281
282 window.addEvent("resize", function() {
283 this.reflow(100);
284 }.bind(this));
285 },
286 applyClasses: function(pos, l) {
287 l.addClass("dynamicpanel");
288 l.addClass(this.class_);
289 l.addClass(pos + "boundpanel");
290 },
291 create: function() {
292 var XE = function(pos) {
293 var element = new Element("div");
294 this.applyClasses(pos, element);
295
296 this.parent.appendChild(element);
297 return element;
298 }.bind(this);
299
300 this.top = XE("top");
301 this.left = XE("left");
302 this.topic = XE("topic");
303 this.middle = XE("middle");
304 this.right = XE("right");
305 this.bottom = XE("bottom");
306 },
307 reflow: function(delay) {
308 if(!delay)
309 delay = 1;
310
311 if(this.reflowevent)
312 $clear(this.reflowevent);
313 this.__reflow();
314 this.reflowevent = this.__reflow.delay(delay, this);
315 },
316 __reflow: function() {
317 var bottom = this.bottom;
318 var middle = this.middle;
319 var right = this.right;
320 var topic = this.topic;
321 var top = this.top;
322 var left = this.left;
323
324 /* |----------------------------------------------|
325 * | top |
326 * |----------------------------------------------|
327 * | left | topic | right |
328 * | |-------------------------------| |
329 * | | middle | |
330 * | | | |
331 * | | | |
332 * | |---------------------------------------|
333 * | | bottom |
334 * |----------------------------------------------|
335 */
336
337 var topicsize = topic.getSize();
338 var topsize = top.getSize();
339 var rightsize = right.getSize();
340 var bottomsize = bottom.getSize();
341 var leftsize = left.getSize();
342 var docsize = this.sizer.getSize();
343
344 var mheight = (docsize.y - topsize.y - bottomsize.y - topicsize.y);
345 var mwidth = (docsize.x - rightsize.x - leftsize.x);
346
347 left.setStyle("top", topsize.y);
348 topic.setStyle("top", topsize.y);
349 topic.setStyle("left", leftsize.x);
350 topic.setStyle("width", docsize.x - leftsize.x);
351
352 middle.setStyle("top", (topsize.y + topicsize.y));
353 middle.setStyle("left", leftsize.x);
354 if(mheight > 0) {
355 middle.setStyle("height", mheight);
356 right.setStyle("height", mheight);
357 }
358
359 if(mwidth > 0)
360 middle.setStyle("width", mwidth);
361 right.setStyle("top", (topsize.y + topicsize.y));
362
363 bottom.setStyle("left", leftsize.x);
364 this.fireEvent("reflow");
365 },
366 showChannel: function(state, nicklistVisible) {
367 var display = "none";
368 if(state)
369 display = "block";
370
371 this.right.setStyle("display", nicklistVisible ? display : "none");
372 this.topic.setStyle("display", display);
373 },
374 showInput: function(state) {
375 this.bottom.isVisible = state;
376 this.bottom.setStyle("display", state?"block":"none");
377 }
378 });
379
380 qwebirc.ui.QUI.Window = new Class({
381 Extends: qwebirc.ui.Window,
382
383 initialize: function(parentObject, client, type, name, identifier) {
384 this.parent(parentObject, client, type, name, identifier);
385
386 this.tab = new Element("a");
387 this.tab.addClass("tab");
388 this.tab.addEvent("focus", function() { this.blur() }.bind(this.tab));;
389
390 this.spaceNode = document.createTextNode(" ");
391 parentObject.tabs.appendChild(this.tab);
392 parentObject.tabs.appendChild(this.spaceNode);
393
394 if(type != qwebirc.ui.WINDOW_STATUS && type != qwebirc.ui.WINDOW_CONNECT) {
395 var tabclose = new Element("span");
396 this.tabclose = tabclose;
397 tabclose.set("text", "X");
398 tabclose.addClass("tabclose");
399 var close = function(e) {
400 new Event(e).stop();
401
402 if(this.closed)
403 return;
404
405 if(type == qwebirc.ui.WINDOW_CHANNEL)
406 this.client.exec("/PART " + name);
407
408 this.close();
409
410 //parentObject.inputbox.focus();
411 }.bind(this);
412
413 tabclose.addEvent("click", close);
414 this.tab.addEvent("mouseup", function(e) {
415 var button = 1;
416
417 if(Browser.Engine.trident)
418 button = 4;
419
420 if(e.event.button == button)
421 close(e);
422 }.bind(this));
423
424 this.tab.appendChild(tabclose);
425 } else {
426 this.tabclose = null;
427 }
428
429 this.tab.appendText(name);
430 this.tab.addEvent("click", function(e) {
431 new Event(e).stop();
432
433 if(this.closed)
434 return;
435
436 parentObject.selectWindow(this);
437 }.bind(this));
438
439
440 this.lines = new Element("div");
441 this.parentObject.qjsui.applyClasses("middle", this.lines);
442 this.lines.addClass("lines");
443 if(type != qwebirc.ui.WINDOW_CUSTOM && type != qwebirc.ui.WINDOW_CONNECT)
444 this.lines.addClass("ircwindow");
445
446 this.lines.addEvent("scroll", function() {
447 this.scrolleddown = this.scrolledDown();
448 this.scrollpos = this.getScrollParent().getScroll();
449 }.bind(this));
450
451 if(type == qwebirc.ui.WINDOW_CHANNEL || type == qwebirc.ui.WINDOW_QUERY) {
452 this.topic = new Element("div");
453 this.parentObject.qjsui.applyClasses("topic", this.topic);
454 this.topic.addClass("topic");
455 this.topic.addClass("tab-invisible");
456 this.topic.set("html", "&nbsp;");
457 this.topic.addEvent("dblclick", this.editTopic.bind(this));
458 this.parentObject.qjsui.applyClasses("topic", this.topic);
459
460 this.prevNick = null;
461 this.nicklist = new Element("div");
462 this.nicklist.addClass("nicklist");
463 this.nicklist.addClass("tab-invisible");
464 this.nicklist.addEvent("click", this.removePrevMenu.bind(this));
465 this.parentObject.qjsui.applyClasses("right", this.nicklist);
466
467 this.updateTopic("");
468 }
469
470 this.nicksColoured = this.parentObject.uiOptions.NICK_COLOURS;
471 this.reflow();
472 },
473 rename: function(name) {
474 var newNode = document.createTextNode(name);
475 if(this.parentObject.sideTabs) {
476 this.tab.replaceChild(newNode, this.tab.childNodes[1]);
477 } else {
478 this.tab.replaceChild(newNode, this.tab.firstChild);
479 }
480
481 if(this.type == qwebirc.ui.WINDOW_QUERY)
482 this.updateTopic("");
483 },
484 editTopic: function() {
485 if(this.type != qwebirc.ui.WINDOW_CHANNEL)
486 return;
487
488 if(!this.client.nickOnChanHasPrefix(this.client.nickname, this.name, "@")) {
489 /* var cmodes = this.client.getChannelModes(channel);
490 if(cmodes.indexOf("t")) {*/
491 alert("Sorry, you need to be a channel operator to change the topic!");
492 return;
493 /*}*/
494 }
495 var newTopic = prompt("Change topic of " + this.name + " to:", this.topic.topicText);
496 if(newTopic === null)
497 return;
498
499 this.client.exec("/TOPIC " + newTopic);
500 },
501 reflow: function() {
502 this.parentObject.reflow();
503 },
504 onResize: function() {
505 if(this.scrolleddown) {
506 if(Browser.Engine.trident) {
507 this.scrollToBottom.delay(5, this);
508 } else {
509 this.scrollToBottom();
510 }
511 } else if($defined(this.scrollpos)) {
512 if(Browser.Engine.trident) {
513 this.getScrollParent().scrollTo(this.scrollpos.x, this.scrollpos.y);
514 } else {
515 this.getScrollParent().scrollTo.delay(5, this, [this.scrollpos.x, this.scrollpos.y]);
516 }
517 }
518 },
519 createMenu: function(nick, parent) {
520 var e = new Element("div");
521 parent.appendChild(e);
522 e.addClass("menu");
523
524 var nickArray = [nick];
525 qwebirc.ui.MENU_ITEMS.forEach(function(x) {
526 if(!x.predicate || x.predicate !== true && !x.predicate.apply(this, nickArray))
527 return;
528
529 var e2 = new Element("a");
530 e.appendChild(e2);
531
532 e2.set("text", "- " + x.text);
533
534 e2.addEvent("focus", function() { this.blur() }.bind(e2));
535 e2.addEvent("click", function(ev) { new Event(ev.stop()); this.menuClick(x.fn); }.bind(this));
536 }.bind(this));
537 return e;
538 },
539 menuClick: function(fn) {
540 /*
541 this.prevNick.removeChild(this.prevNick.menu);
542 this.prevNick.menu = null;
543 */
544 fn.bind(this)(this.prevNick.realNick);
545 this.removePrevMenu();
546 },
547 moveMenuClass: function() {
548 if(!this.prevNick)
549 return;
550 if(this.nicklist.firstChild == this.prevNick) {
551 this.prevNick.removeClass("selected-middle");
552 } else {
553 this.prevNick.addClass("selected-middle");
554 }
555 },
556 removePrevMenu: function() {
557 if(!this.prevNick)
558 return;
559
560 this.prevNick.removeClass("selected");
561 this.prevNick.removeClass("selected-middle");
562 if(this.prevNick.menu)
563 this.prevNick.removeChild(this.prevNick.menu);
564 this.prevNick = null;
565 },
566 nickListAdd: function(nick, position) {
567 var realNick = this.client.stripPrefix(nick);
568
569 var e = new Element("a");
570 qwebirc.ui.insertAt(position, this.nicklist, e);
571 var span = new Element("span");
572 if(this.parentObject.uiOptions.NICK_COLOURS) {
573 var colour = realNick.toHSBColour(this.client);
574 if($defined(colour))
575 span.setStyle("color", colour.rgbToHex());
576 }
577 span.set("text", nick);
578 e.appendChild(span);
579
580 e.realNick = realNick;
581
582 e.addEvent("click", function(x) {
583 if(this.prevNick == e) {
584 this.removePrevMenu();
585 return;
586 }
587
588 this.removePrevMenu();
589 this.prevNick = e;
590 e.addClass("selected");
591 this.moveMenuClass();
592 e.menu = this.createMenu(e.realNick, e);
593 new Event(x).stop();
594 }.bind(this));
595
596 e.addEvent("focus", function() { this.blur() }.bind(e));
597 this.moveMenuClass();
598 return e;
599 },
600 nickListRemove: function(nick, stored) {
601 this.nicklist.removeChild(stored);
602 this.moveMenuClass();
603 },
604 updateTopic: function(topic) {
605 var t = this.topic;
606
607 while(t.firstChild)
608 t.removeChild(t.firstChild);
609
610 var suffix;
611 if(this.type == qwebirc.ui.WINDOW_CHANNEL) {
612 suffix = ": ";
613 } else {
614 suffix = "";
615 }
616 qwebirc.ui.Colourise(this.name + suffix, t, null, null, this);
617
618 if(this.type == qwebirc.ui.WINDOW_CHANNEL) {
619 t.topicText = topic;
620 if (topic) {
621 this.parent(topic, t);
622 } else {
623 t.appendChild(document.createTextNode("(no topic set)"));
624 }
625 }
626
627 this.reflow();
628 },
629 select: function() {
630 var inputVisible = this.type != qwebirc.ui.WINDOW_CONNECT && this.type != qwebirc.ui.WINDOW_CUSTOM;
631
632 this.tab.removeClass("tab-unselected");
633 this.tab.addClass("tab-selected");
634
635 this.parentObject.setLines(this.lines);
636 this.parentObject.setChannelItems(this.nicklist, this.topic);
637 this.parentObject.qjsui.showInput(inputVisible);
638 this.parentObject.qjsui.showChannel($defined(this.nicklist), this.parentObject.uiOptions.SHOW_NICKLIST);
639
640 this.reflow();
641
642 this.parent();
643
644 if(inputVisible)
645 this.parentObject.inputbox.focus();
646
647 if(this.type == qwebirc.ui.WINDOW_CHANNEL && this.nicksColoured != this.parentObject.uiOptions.NICK_COLOURS) {
648 this.nicksColoured = this.parentObject.uiOptions.NICK_COLOURS;
649
650 var nodes = this.nicklist.childNodes;
651 if(this.parentObject.uiOptions.NICK_COLOURS) {
652 for(var i=0;i<nodes.length;i++) {
653 var e = nodes[i], span = e.firstChild;
654 var colour = e.realNick.toHSBColour(this.client);
655 if($defined(colour))
656 span.setStyle("color", colour.rgbToHex());
657 };
658 } else {
659 for(var i=0;i<nodes.length;i++) {
660 var span = nodes[i].firstChild;
661 span.setStyle("color", null);
662 };
663 }
664 }
665 },
666 deselect: function() {
667 this.parent();
668
669 this.tab.removeClass("tab-selected");
670 this.tab.addClass("tab-unselected");
671 },
672 close: function() {
673 this.parent();
674
675 this.parentObject.tabs.removeChild(this.tab);
676 this.parentObject.tabs.removeChild(this.spaceNode);
677 this.reflow();
678 },
679 addLine: function(type, line, colourClass) {
680 var e = new Element("div");
681
682 if(colourClass) {
683 e.addClass(colourClass);
684 } else if(this.lastcolour) {
685 e.addClass("linestyle1");
686 } else {
687 e.addClass("linestyle2");
688 }
689 this.lastcolour = !this.lastcolour;
690
691 this.parent(type, line, colourClass, e);
692 },
693 setHilighted: function(state) {
694 var laststate = this.hilighted;
695
696 this.parent(state);
697
698 if(state == laststate)
699 return;
700
701 this.tab.removeClass("tab-hilight-activity");
702 this.tab.removeClass("tab-hilight-us");
703 this.tab.removeClass("tab-hilight-speech");
704
705 switch(this.hilighted) {
706 case qwebirc.ui.HILIGHT_US:
707 this.tab.addClass("tab-hilight-us");
708 break;
709 case qwebirc.ui.HILIGHT_SPEECH:
710 this.tab.addClass("tab-hilight-speech");
711 break;
712 case qwebirc.ui.HILIGHT_ACTIVITY:
713 this.tab.addClass("tab-hilight-activity");
714 break;
715 }
716 },
717 setSideTabs: function(value) {
718 if(this.tabclose === null)
719 return;
720 this.tab.removeChild(this.tabclose);
721 if(value) {
722 this.tab.insertBefore(this.tabclose, this.tab.firstChild);
723 } else {
724 this.tab.appendChild(this.tabclose);
725 }
726 }
727 });