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