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