]> jfr.im git - irc/quakenet/qwebirc.git/blob - js/ui/qui.js
Scroll position is now locked at the bottom if it's at the bottom when resizing begins.
[irc/quakenet/qwebirc.git] / js / ui / qui.js
1 qwebirc.ui.QUI = new Class({
2 Extends: qwebirc.ui.NewLoginUI,
3 initialize: function(parentElement, theme) {
4 this.parent(parentElement, qwebirc.ui.QUI.Window, "qui");
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("tabbar");
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.tabs = this.qjsui.top;
23 this.origtopic = this.topic = this.qjsui.topic;
24 this.origlines = this.lines = this.qjsui.middle;
25 this.orignicklist = this.nicklist = this.qjsui.right;
26
27 this.input = this.qjsui.bottom;
28 this.reflow = this.qjsui.reflow.bind(this.qjsui);
29
30 this.tabs.addEvent("mousewheel", function(x) {
31 var event = new Event(x);
32
33 /* up */
34 if(event.wheel > 0) {
35 this.nextWindow();
36 } else if(event.wheel < 0) {
37 /* down */
38 this.prevWindow();
39 }
40 event.stop();
41 }.bind(this));
42
43 this.createInput();
44 this.reflow();
45 },
46 createInput: function() {
47 var form = new Element("form");
48 this.input.appendChild(form);
49 form.addClass("input");
50
51 var inputbox = new Element("input");
52 form.appendChild(inputbox);
53 this.inputbox = inputbox;
54
55 form.addEvent("submit", function(e) {
56 new Event(e).stop();
57
58 if(inputbox.value == "")
59 return;
60
61 this.resetTabComplete();
62 this.getActiveWindow().historyExec(inputbox.value);
63 inputbox.value = "";
64 }.bind(this));
65
66 inputbox.addEvent("focus", this.resetTabComplete.bind(this));
67 inputbox.addEvent("mousedown", this.resetTabComplete.bind(this));
68
69 inputbox.addEvent("keydown", function(e) {
70 var resultfn;
71 var cvalue = inputbox.value;
72
73 if(e.key == "up") {
74 resultfn = this.commandhistory.upLine;
75 } else if(e.key == "down") {
76 resultfn = this.commandhistory.downLine;
77 } else if(e.key == "tab") {
78 new Event(e).stop();
79 this.tabComplete(inputbox);
80 return;
81 } else {
82 /* ideally alt and other keys wouldn't break this */
83 this.resetTabComplete();
84 return;
85 }
86
87 this.resetTabComplete();
88 if((cvalue != "") && (this.lastcvalue != cvalue))
89 this.commandhistory.addLine(cvalue, true);
90
91 var result = resultfn.bind(this.commandhistory)();
92
93 new Event(e).stop();
94 if(!result)
95 result = "";
96 this.lastcvalue = result;
97
98 inputbox.value = result;
99 qwebirc.util.setAtEnd(inputbox);
100 }.bind(this));
101 },
102 setLines: function(lines) {
103 this.lines.parentNode.replaceChild(lines, this.lines);
104 this.qjsui.middle = this.lines = lines;
105 },
106 setChannelItems: function(nicklist, topic) {
107 if(!$defined(nicklist)) {
108 nicklist = this.orignicklist;
109 topic = this.origtopic;
110 }
111 this.nicklist.parentNode.replaceChild(nicklist, this.nicklist);
112 this.qjsui.right = this.nicklist = nicklist;
113
114 this.topic.parentNode.replaceChild(topic, this.topic);
115 this.qjsui.topic = this.topic = topic;
116 }
117 });
118
119 qwebirc.ui.QUI.JSUI = new Class({
120 Implements: [Events],
121 initialize: function(class_, parent, sizer) {
122 this.parent = parent;
123 this.sizer = $defined(sizer)?sizer:parent;
124
125 this.class_ = class_;
126 this.create();
127
128 this.reflowevent = null;
129
130 window.addEvent("resize", function() {
131 this.reflow(100);
132 }.bind(this));
133 },
134 applyClasses: function(pos, l) {
135 l.addClass("dynamicpanel");
136 l.addClass(this.class_);
137
138 if(pos == "middle") {
139 l.addClass("leftboundpanel");
140 } else if(pos == "top") {
141 l.addClass("topboundpanel");
142 l.addClass("widepanel");
143 } else if(pos == "topic") {
144 l.addClass("widepanel");
145 } else if(pos == "right") {
146 l.addClass("rightboundpanel");
147 } else if(pos == "bottom") {
148 l.addClass("bottomboundpanel");
149 l.addClass("widepanel");
150 }
151 },
152 create: function() {
153 var XE = function(pos) {
154 var element = new Element("div");
155 this.applyClasses(pos, element);
156
157 this.parent.appendChild(element);
158 return element;
159 }.bind(this);
160
161 this.top = XE("top");
162 this.topic = XE("topic");
163 this.middle = XE("middle");
164 this.right = XE("right");
165 this.bottom = XE("bottom");
166 },
167 reflow: function(delay) {
168 if(!delay)
169 delay = 1;
170
171 if(this.reflowevent)
172 $clear(this.reflowevent);
173 this.__reflow();
174 this.reflowevent = this.__reflow.delay(delay, this);
175 },
176 __reflow: function() {
177 var bottom = this.bottom;
178 var middle = this.middle;
179 var right = this.right;
180 var topic = this.topic;
181 var top = this.top;
182
183 var topicsize = topic.getSize();
184 var topsize = top.getSize();
185 var rightsize = right.getSize();
186 var bottomsize = bottom.getSize();
187 var docsize = this.sizer.getSize();
188
189 var mheight = (docsize.y - topsize.y - bottomsize.y - topicsize.y);
190 var mwidth = (docsize.x - rightsize.x);
191
192 topic.setStyle("top", topsize.y + "px");
193
194 middle.setStyle("top", (topsize.y + topicsize.y) + "px");
195 if(mheight > 0) {
196 middle.setStyle("height", mheight + "px");
197 right.setStyle("height", mheight + "px");
198 }
199
200 if(mwidth > 0)
201 middle.setStyle("width", mwidth + "px");
202 right.setStyle("top", (topsize.y + topicsize.y) + "px");
203 right.setStyle("left", mwidth + "px");
204
205 bottom.setStyle("top", (docsize.y - bottomsize.y) + "px");
206 this.fireEvent("reflow");
207 },
208 showChannel: function(state) {
209 var display = "none";
210 if(state)
211 display = "block";
212
213 this.right.setStyle("display", display);
214 this.topic.setStyle("display", display);
215 },
216 showInput: function(state) {
217 this.bottom.setStyle("display", state?"block":"none");
218 }
219 });
220
221 qwebirc.ui.QUI.Window = new Class({
222 Extends: qwebirc.ui.Window,
223
224 initialize: function(parentObject, client, type, name) {
225 this.parent(parentObject, client, type, name);
226
227 this.tab = new Element("a", {"href": "#"});
228 this.tab.addClass("tab");
229 this.tab.addEvent("focus", function() { this.blur() }.bind(this.tab));;
230
231 parentObject.tabs.appendChild(this.tab);
232
233 this.tab.appendText(name);
234 this.tab.addEvent("click", function(e) {
235 new Event(e).stop();
236 parentObject.selectWindow(this);
237 }.bind(this));
238
239 if(type != qwebirc.ui.WINDOW_STATUS && type != qwebirc.ui.WINDOW_CONNECT) {
240 var tabclose = new Element("span");
241 tabclose.set("text", "X");
242 tabclose.addClass("tabclose");
243 tabclose.addEvent("click", function(e) {
244 new Event(e).stop();
245
246 if(type == qwebirc.ui.WINDOW_CHANNEL)
247 this.client.exec("/PART " + name);
248
249 this.close();
250
251 parentObject.inputbox.focus();
252 }.bind(this));
253
254 this.tab.appendChild(tabclose);
255 }
256
257 this.lines = new Element("div");
258 this.parentObject.qjsui.applyClasses("middle", this.lines);
259 this.lines.addClass("lines");
260
261 this.lines.addEvent("scroll", function() {
262 this.scrolleddown = this.scrolledDown();
263 }.bind(this));
264
265 if(type == qwebirc.ui.WINDOW_CHANNEL) {
266 this.topic = new Element("div");
267 this.topic.addClass("topic");
268 this.topic.addClass("tab-invisible");
269 this.topic.set("html", "&nbsp;");
270 this.parentObject.qjsui.applyClasses("topic", this.topic);
271
272 this.prevNick = null;
273 this.nicklist = new Element("div");
274 this.nicklist.addClass("nicklist");
275 this.nicklist.addClass("tab-invisible");
276 this.nicklist.addEvent("click", this.removePrevMenu.bind(this));
277 this.parentObject.qjsui.applyClasses("nicklist", this.nicklist);
278 }
279
280 if(type == qwebirc.ui.WINDOW_CHANNEL) {
281 this.updateTopic("");
282 } else {
283 this.reflow();
284 }
285 },
286 reflow: function() {
287 this.parentObject.reflow();
288 },
289 onResize: function() {
290 if(this.scrolleddown)
291 this.scrollToBottom();
292 },
293 createMenu: function(nick, parent) {
294 var e = new Element("div");
295 parent.appendChild(e);
296 e.addClass("menu");
297
298 qwebirc.ui.MENU_ITEMS.forEach(function(x) {
299 var e2 = new Element("a");
300 e.appendChild(e2);
301
302 e2.href = "#";
303 e2.set("text", "- " + x[0]);
304
305 e2.addEvent("focus", function() { this.blur() }.bind(e2));
306 e2.addEvent("click", function(ev) { new Event(ev.stop()); this.menuClick(x[1]); }.bind(this));
307 }.bind(this));
308 return e;
309 },
310 menuClick: function(fn) {
311 /*
312 this.prevNick.removeChild(this.prevNick.menu);
313 this.prevNick.menu = null;
314 */
315 fn.bind(this)(this.prevNick.realNick);
316 this.removePrevMenu();
317 },
318 moveMenuClass: function() {
319 if(!this.prevNick)
320 return;
321 if(this.nicklist.firstChild == this.prevNick) {
322 this.prevNick.removeClass("selected-middle");
323 } else {
324 this.prevNick.addClass("selected-middle");
325 }
326 },
327 removePrevMenu: function() {
328 if(!this.prevNick)
329 return;
330
331 this.prevNick.removeClass("selected");
332 this.prevNick.removeClass("selected-middle");
333 if(this.prevNick.menu)
334 this.prevNick.removeChild(this.prevNick.menu);
335 this.prevNick = null;
336 },
337 nickListAdd: function(nick, position) {
338 var e = new Element("a");
339 qwebirc.ui.insertAt(position, this.nicklist, e);
340
341 e.href = "#";
342 e.appendChild(document.createTextNode(nick));
343
344 e.realNick = this.client.stripPrefix(nick);
345
346 e.addEvent("click", function(x) {
347 if(this.prevNick == e) {
348 this.removePrevMenu();
349 return;
350 }
351
352 this.removePrevMenu();
353 this.prevNick = e;
354 e.addClass("selected");
355 this.moveMenuClass();
356 e.menu = this.createMenu(x.realNick, e);
357 new Event(x).stop();
358 }.bind(this));
359 e.addEvent("dblclick", function(x) {
360 new Event(x).stop();
361 this.client.exec("/QUERY " + e.realNick);
362 }.bind(this));
363
364 e.addEvent("focus", function() { this.blur() }.bind(e));
365 this.moveMenuClass();
366 return e;
367 },
368 nickListRemove: function(nick, stored) {
369 this.nicklist.removeChild(stored);
370 this.moveMenuClass();
371 },
372 updateTopic: function(topic) {
373 var t = this.topic;
374
375 while(t.firstChild)
376 t.removeChild(t.firstChild);
377
378 if(topic) {
379 this.parent(topic, t);
380 } else {
381 var e = new Element("div");
382 e.set("text", "(no topic set)");
383 e.addClass("emptytopic");
384 t.appendChild(e);
385 }
386 this.reflow();
387 },
388 select: function() {
389 var inputVisible = this.type != qwebirc.ui.WINDOW_CONNECT && this.type != qwebirc.ui.WINDOW_CUSTOM;
390
391 this.tab.removeClass("tab-unselected");
392 this.tab.addClass("tab-selected");
393
394 this.parentObject.setLines(this.lines);
395 this.parentObject.setChannelItems(this.nicklist, this.topic);
396 this.parentObject.qjsui.showInput(inputVisible);
397 this.parentObject.qjsui.showChannel($defined(this.nicklist));
398
399 this.reflow();
400
401 this.parent();
402
403 if(inputVisible)
404 this.parentObject.inputbox.focus();
405 },
406 deselect: function() {
407 this.parent();
408
409 this.tab.removeClass("tab-selected");
410 this.tab.addClass("tab-unselected");
411 },
412 close: function() {
413 this.parent();
414
415 this.parentObject.tabs.removeChild(this.tab);
416 },
417 addLine: function(type, line, colour) {
418 var e = new Element("div");
419
420 if(colour) {
421 e.setStyles({"background": colour});
422 } else if(this.lastcolour) {
423 e.addClass("linestyle1");
424 } else {
425 e.addClass("linestyle2");
426 }
427 this.lastcolour = !this.lastcolour;
428
429 this.parent(type, line, colour, e);
430 },
431 setHilighted: function(state) {
432 laststate = this.hilighted;
433
434 this.parent(state);
435
436 if(state == laststate)
437 return;
438
439 this.tab.removeClass("tab-hilight-activity");
440 this.tab.removeClass("tab-hilight-us");
441 this.tab.removeClass("tab-hilight-speech");
442
443 switch(this.hilighted) {
444 case qwebirc.ui.HILIGHT_US:
445 this.tab.addClass("tab-hilight-us");
446 break;
447 case qwebirc.ui.HILIGHT_SPEECH:
448 this.tab.addClass("tab-hilight-speech");
449 break;
450 case qwebirc.ui.HILIGHT_ACTIVITY:
451 this.tab.addClass("tab-hilight-activity");
452 break;
453 }
454 }
455 });