default keybindings and other data now embedded in executable... again
[dyskinesia.git] / html / js / chat.js
blob038cf959cb92e4d7c7cc9ed6d0fc1495767e765b
1 var msgFirst = 1;
2 var msgCur = 0;
3 var msgCount = 0;
4 var lastUNI;
5 var lastMsgDiv;
8 function selectedText () {
9   var sel = window.getSelection();
10   if (!sel || sel.rangeCount < 1) return "";
11   var s = sel.toString();
13   for (var f = 0; f < s.length; f++) {
14     var ch = s.charAt(f);
15     if (ch < ' ') api.print("{"+ch.charCodeAt(0)+"}");
16     else if (ch == ' ') api.print("{ }");
17     else api.print(ch);
18   }
19   api.printLn("");
21   api.selectedText(s);
22   return s;
23   //.removeAllRanges();
27 function unselectText () {
28   var sel = window.getSelection();
29   if (!sel || sel.rangeCount < 1) return "";
30   sel.removeAllRanges();
35  *
36  */
37 function setMyUNI (uni) {
38   myUNI = uni;
43  *
44  */
45 function chatStarted (uni) {
46   //api.printLn(">>>"+uni);
50 function optionChanged (optname) {
54 function optionRemoved (optname) {
58 function date2Str (dt) {
59   function n2s (n, len) {
60     var res = ""+n;
61     len = len||2;
62     while (res.length < len) res = "0"+res;
63     return res;
64   }
65   var res = dt.getFullYear()+"/"+n2s(dt.getMonth()+1)+"/"+n2s(dt.getDate())+" "+
66     n2s(dt.getHours())+":"+n2s(dt.getMinutes())+":"+n2s(dt.getSeconds());
67   return res;
71 function delChatMessage (id) {
72   var div = document.getElementById("msg_"+id);
73   if (div) {
74     div.parentNode.removeChild(div);
75     api.scrollToBottom();
76   }
81  *
82  */
83 function addHR () {
84   var div = document.createElement("hr");
85   document.body.appendChild(div);
89 function chatAtBottom () {
90   var curY = window.pageYOffset;
91   var de = document.documentElement.offsetHeight;
92   var vh = window.innerHeight;
93   return (curY+vh >= de);
97 function isChatAtBottom () {
98   api.setWasBottom(chatAtBottom());
104  */
105 function addSysMsg (msg) {
106   //msg = msg.replace(/\n/g, "<br />\n");
108   api.printLn("----");
109   api.printLn("addSysMsg: before: h="+document.body.offsetHeight+"; ofs="+window.pageYOffset+"; ch="+document.body.clientHeight+"; ih="+window.innerHeight);
110   api.printLn("  "+document.documentElement.scrollHeight);
111   api.printLn("  "+document.documentElement.offsetHeight);
113   var wasAtBottom = chatAtBottom();
115   if (!lastMsgDiv || lastUNI != "system console") {
116     var div = document.createElement("div");
117     div.setAttribute("class", "message");
119     var hdrdiv = document.createElement("div");
120     hdrdiv.setAttribute("class", "sysmsghead");
121     hdrdiv.innerText = "system console";
122     div.appendChild(hdrdiv);
124     var txtdiv = document.createElement("div");
125     txtdiv.setAttribute("class", "sysmsgtext");
126     txtdiv.innerHTML = msg;
127     div.appendChild(txtdiv);
129     lastMsgDiv = div;
130     lastUNI = "system console";
132     document.body.appendChild(div);
133   } else {
134     var divdiv = document.createElement("div");
135     divdiv.setAttribute("class", "msgdelimiter");
136     lastMsgDiv.appendChild(divdiv);
138     var txtdiv = document.createElement("div");
139     txtdiv.setAttribute("class", "sysmsgtext");
140     txtdiv.innerHTML = msg;
141     lastMsgDiv.appendChild(txtdiv);
142   }
144   api.printLn("addSysMsg: after: h="+document.body.offsetHeight+"; ofs="+window.pageYOffset+"; ch="+document.body.clientHeight+"; ih="+window.innerHeight);
145   api.printLn("  "+document.documentElement.scrollHeight);
146   api.printLn("  "+document.documentElement.offsetHeight);
148   api.refresh();
149   api.printLn("addSysMsg: refreshed: h="+document.body.offsetHeight+"; ofs="+window.pageYOffset+"; ch="+document.body.clientHeight+"; ih="+window.innerHeight);
150   api.printLn("  "+document.documentElement.scrollHeight);
151   api.printLn("  "+document.documentElement.offsetHeight);
153   api.scrollToBottom();
154   api.printLn("addSysMsg: scrolled: h="+document.body.offsetHeight+"; ofs="+window.pageYOffset+"; ch="+document.body.clientHeight+"; ih="+window.innerHeight);
155   api.printLn("  "+document.documentElement.scrollHeight);
156   api.printLn("  "+document.documentElement.offsetHeight);
158   var fs = api.getOption("/chat/forcescroll");
159   //api.printLn("fs="+fs+"; wasBt="+wasAtBottom);
160   if (fs > 1 || ((fs === null || fs == 1) && wasAtBottom)) {
161     api.scrollToBottom();
162   } else {
163     api.refresh();
164   }
168 function shrinkList () {
169   var maxMsg = api.getOption("/chat/maxmessages");
170   if (typeof(maxMsg) == "number") {
171     if (maxMsg > 0 && msgCount > maxMsg) {
172       var toLeft = api.getOption("/chat/maxmessagesleft");
173       if (typeof(toLeft) != "number") toLeft = maxMsg;
174       while (msgCount > toLeft) {
175         delChatMessage(msgFirst);
176         msgFirst++;
177         msgCount--;
178       }
179     }
180   }
184 function fixLongWords (s) {
185   for (var f = 0; f < s.length; ) {
186     while (f < s.length && s.charAt(f) <= ' ') f++;
187     if (f >= s.length) break;
188     var st = 0;
189     while (f < s.length && s.charAt(f) > ' ') {
190       st++; f++;
191       if (s.charAt(f-1) == "&") {
192         while (f < s.length && s.charAt(f) != ';') f++;
193         f++;
194         continue;
195       }
196       if (s.charAt(f-1) == "<") {
197         while (f < s.length && s.charAt(f) != '>') f++;
198         f++;
199         continue;
200       }
201       if (st >= 32 && f < s.length) {
202         s = s.substr(0, f)+"&shy;"+s.substr(f, s.length);
203         st = 0;
204         f += 4;
205         continue;
206       }
207     }
208   }
209   return s;
213 function colorizeQuotes (s) {
215   var t = s.split("");
216   for (var f = 0; f < t.length; f++) {
217     //if (t[f] == '>') t[f] = '!';
218     t[f] = t[f]+"("+(t[f].charCodeAt(0))+")";
219   }
220   s = t.join("");
222   var lines = s.split("\n");
224   var trim = function (st) {
225     return st.replace(/^\s+|\s+$/g, "");
226   };
227   var trimR = function (st) {
228     return st.replace(/\s+$/g, "");
229   };
230   var jj = function (st, end, addnl) {
231     var res = "";
232     if (end >= lines.length) end = lines.length-1;
233     for (var f = st; f <= end; f++) {
234       if (res) res += "\n";
235       res += lines[f];
236     }
237     if (addnl) res += "\n";
238     return res;
239   };
240   var qlevel = function (s) {
241     var lvl = -1;
242     if (s.length < 1) return lvl;
243     var mir = (s.charAt(0) == '\u00bb');
244     for (var f = 0; f < s.length; f++) {
245       var ch = s.charAt(f);
246       if (ch <= ' ') {
247         if (mir) return lvl;
248         continue;
249       }
250       if (mir) {
251         if (ch == '\u00bb') lvl++; else break;
252       } else {
253         if (ch == '>') lvl++;
254         else if (ch == '&' && s.substr(f, 4) == "&gt;") { lvl++; f += 3; }
255         else break;
256       }
257     }
258     return lvl;
259   };
261   // search for "big" quotes
262   for (var f = 0; f < lines.length; f++) {
263     var s = trimR(lines[f]);
264     lines[f] = s;
265     if (s == "---") {
266       s = '<span class="msgquotebig">'+jj(0, f)+"</span>\n"+jj(f+1, lines.length-1);
267       return trimR(s);
268     }
269   }
270   // mark "small" quotes
271   for (var f = 0; f < lines.length; f++) {
272     var s = trimR(lines[f]);
273     var lvl = qlevel(s);
274     if (lvl >= 0) {
275       lvl = lvl%2;
276       s = '<span class="msgquote'+lvl+'">'+s+"</span>";
277     }
278     lines[f] = s;
279   }
280   //return "cnt: "+(lines.length)+ "\n"+jj(0, lines.length-1);
281   return jj(0, lines.length-1);
286  * bool .ismine
287  * int .unixdate
288  * string .uni
289  * string .text
290  * string .action (or null/false/empty string)
291  */
292 function addChatMessage (msg) {
293   var wasAtBottom = chatAtBottom();
295   var user = api.contactInfo(msg.uni);
296   var nick = user ? user.verbatim : msg.uni;
297   lastMsgDiv = false;
298   var isMine = msg.ismine, unixDate = msg.unixdate, text = msg.text;
300   //api.printLn("t=["+text+"]");
301   text = text.replace(/\r\n/g, "\n");
302   text = text.replace(/<br\s*>/g, "\n");
303   text = text.replace(/<br\s*\/>/g, "\n");
305   text = fixLongWords(text);
306   text = colorizeQuotes(text);
307   text = text.replace(/\n/g, "<br />\n");
309   msgCur++; msgCount++;
311   var div = document.createElement("div");
312   div.setAttribute("id", "msg_"+msgCur);
313   div.setAttribute("class", "message");
315   var timespan = document.createElement("span");
316   timespan.setAttribute("class", "time");
318   var dt = new Date();
319   dt.setTime(unixDate*1000);
320   timespan.innerHTML = date2Str(dt);
321   div.appendChild(timespan);
324   var markspan = document.createElement("span");
325   markspan.setAttribute("style", "float:right;padding:2px;color:#"+(id%2?"999":"0c0"));
326   markspan.setAttribute("id", "mark_msg_"+id);
327   markspan.innerHTML = "*";
328   div.appendChild(markspan);
331   var hdrdiv = document.createElement("div");
332   hdrdiv.setAttribute("class", "msghead "+(isMine?"msgmine":"msgalien"));
333   hdrdiv.innerText = nick;
334   if (!isMine) hdrdiv.innerHTML = "<b>"+hdrdiv.innerHTML+"</b>";
335   //hdrdiv.style.color = isMine?"#f70":"#aaa";
336   div.appendChild(hdrdiv);
338   if (msg.action) {
339     // has some action
340     var adiv = document.createElement("div");
341     adiv.setAttribute("class", "actiontext");
342     adiv.innerHTML = "*<b>"+nick+"</b> "+msg.action;
343     adiv.style.color = isMine?"#f70":"#aaa";
344     div.appendChild(adiv);
345   }
347   var txtdiv = document.createElement("div");
348   txtdiv.setAttribute("class", "msgtext");
349   txtdiv.innerHTML = text;
350   txtdiv.style.color = isMine?"#f70":"#aaa";
351   div.appendChild(txtdiv);
353   document.body.appendChild(div);
355   var fs = api.getOption("/chat/forcescroll");
356   //api.printLn("fs="+fs+"; wasBt="+wasAtBottom);
357   if (fs > 1 || ((fs === null || fs == 1) && wasAtBottom)) {
358     shrinkList();
359     api.scrollToBottom();
360   } else {
361     api.refresh();
362   }
367 function pval (v) {
368   api.printLn("type: "+typeof(v)+"; value: "+v);
371 function parr (arr) {
372   for (var k in arr) {
373     if (typeof(k) != "string") continue;
374     var v = arr[k];
375     api.printLn("["+k+"]: ("+typeof(v)+") ["+v+"]");
376   }
379 function pd () {
380   var info = api.contactInfo("psyc://ketmar.no-ip.org/@dyskgit");
381   api.printLn(">>"+info);
382   parr(info);
388 //api.test();
389 pval(api.test(10));
390 pval(api.test("str"));
391 pval(api.test(api));
392 pval(api.test([1,2]));
393 pval(api.test({a:4, b:5}));
398 var ok = api.addMessage(
399   "psyc://127.0.0.1/@Testbed", // place
400   "psyc://127.0.0.1/~rtorrent_bot", // user
401   0, // current time (or any other unixtime)
402   "test of the <b>rTorrent</b>-<i>bot</i>\nsecond line",
403   " DONTTOUCH|" // no action, "\x20DONTTOUCH|" means 'don't de-htmlize message' mode; action text can be added after '|'
406 api.addMessageEx() has one more arg:
407   `popupMsg` -- message for popup window; prepend it with '\x01' to
408                 disable de-htmlization
412 function testAddMessage () {
413   var ok = api.addMessage(
414     "psyc://ketmar.no-ip.org/@Testbed",
415     "psyc://ketmar.no-ip.org/~rtorrent_bot",
416     0, // current time
417     "test of the <b>rTorrent</b>-<i>bot</i>\nsecond line",
418     " DONTTOUCH|" // no action, 'don't de-htmlize message' mode
419   );
420   api.printLn("addMessage: "+(ok?"ok":"FAILED"));
424 window.onload = function () {
425   //api.printLn("!!!");
426   document.body.innerHTML = '<div style="position:fixed;right:4px;top:4px;background-color:#fff;border:1px solid #666;padding:5px;"><img alt="" width="130" height="26" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIIAAAAaCAAAAABeXdsPAAAAAnRSTlMA/1uRIrUAAAKmSURBVBgZxcFfaJV1HMfx93NOj5ud0kq7KLUJyUyPwSKjeRiRRTwIEhrK9MKLLsbsosgdDBklXUyk6I9mEWgXgpyr2M3AEPQmMEFEhIEP2J+xqRcqZtnSraPbp+f5PWuec57nG7s7rxdqOqTqvlefaZItg1MSuvkCTdQ7JdTD1kq2vQv4T+HdSqav34KPKrFd7dR7oxIbeBZY1FfJ1N8Bh8TIPI7JMLqCxLJTMozCqJzPqfPI73LehrXnZRiANvEd9MlyeT6xwo+y7IUjcsZaqeH1KdHB4jFZeuHhKwTw1J+yHG8h8olMqyBQ4vKlGj8rcbXgDcky+TQQEgDfy7QOaL8jy3Ae8mdke4dtMh0lEhIAW6uynAAOy3K3l8jGaVlGWvxhWf7ZSCQkILKylPKVnImlUJUzVEp5EueADBNraFOiv5SynFhIQLZFVTmdvKLEi1gKJ5Vp8k3olnN3MYaQAMMxxa6v5jU5FQ9T4VtlmNwEdMs5iCUkwOD5sTx4vuPxPzw/w0NEcr7jYQkJiHl+JMfc5f1YjkZ+jBo53/GwhARElpxW5LdtzNWyUcV+op5/cFqR3a3M+kzO+1hCAiLDcv5ewhx9IOcG9T5VYjuzDssJsYQEQFtViSIZFpbSxuQMUe+qEmVmdcu5/16p0SqckADo0owiGY7L9Dr1xpUoM+vR6zLcu72CSEgAdGlGkbTnJmS5Mp9640qUeWCPTHuIhARAl2YUSftYlqkNNBhXoswDj12U5RyRkNNA6x0liqQNy7KbRmeVKFOj85YM0y/DE7e4sRD4Us6vBVJW35Ohv4VGO+VUX6LW2j9kGID1QoeA/OYdscdJ67x2M9OFLaTleiqRb4rUe/5IJdMXHfih0NR+n6ZZ+oOEpF8Gd/Y0xYeDf0lCTfcvvNUp6vFYBGgAAAAASUVORK5CYII=" /></div>';
428   //setTimeout(testAddMessage, 5000);
431 function htmlEscape (s) {
432   s = s.replace(/[\x01-\x1f]/g, "_").
433     replace(/[&]/g, "&amp;").
434     replace(/[<]/g, "&lt;").
435     replace(/[>]/g, "&gt;").
436     replace(/[\x22]/g, "&quot;");
437   return s;
441 function doPktRTorrent (pkt) {
442   var s = "torrent: <b>"+htmlEscape(pkt.vars["_torrent_name"])+"</b> <i>("+pkt.vars["_torrent_hash"]+")</i>\n";
443   s += "action: <b>"+pkt.vars["_torrent_action"]+"</b>\n";
444   var udt = new Date();
445   udt = Math.floor(udt.getTime()/1000);
446   var msg = {
447     ismine:false,
448     unixdate:udt,
449     uni:"psyc://ketmar.no-ip.org/~rtorrent",
450     text:s,
451     action:""
452   };
453   addChatMessage(msg);
457 function onPSYCPacket () {
458   //dumpPacket(psycpkt);
459   return;
460   var pkt = psycpkt;
461   if (pkt.method && pkt.method == "_notice_rtorrent") doPktRTorrent(pkt);