Ver código fonte

Merge branch 'attachments'

B Thibault 8 anos atrás
pai
commit
5e37be04cb
9 arquivos alterados com 302 adições e 85 exclusões
  1. 14 0
      cli/resources.js
  2. 216 52
      cli/ui.js
  3. 6 4
      srv/public/index.html
  4. 29 23
      srv/public/slack.min.js
  5. 9 1
      srv/public/style.css
  6. 6 2
      srv/src/httpServ.js
  7. 1 1
      srv/src/session.js
  8. 18 1
      srv/src/slack.js
  9. 3 1
      srv/src/url.js

+ 14 - 0
cli/resources.js

@@ -57,6 +57,20 @@ var R = {
             ,linkuser: "slackmsg-link-user"
             ,linkchan: "slackmsg-link-chan"
 
+            ,attachment: {
+                container: "slackmsg-attachment"
+                ,list: "slackmsg-attachments"
+
+                ,pretext: "slackmsg-attachment-pretext"
+                ,title: "slackmsg-attachment-title"
+                ,text: "slackmsg-attachment-text"
+                ,thumbImg: "slackmsg-attachment-thumb"
+                ,img: "slackmsg-attachment-img"
+                ,footer: "slackmsg-attachment-footer"
+                ,footerText: "slackmsg-attachment-footer-text"
+                ,footerIcon: "slackmsg-attachment-footer-icon"
+            }
+
             ,style: {
                 bold: "slackmsg-style-bold"
                 ,code: "slackmsg-style-code"

+ 216 - 52
cli/ui.js

@@ -104,7 +104,7 @@ function onReplyingToUpdated() {
         closeLink.textContent = 'x';
         domParent.textContent = "";
         domParent.appendChild(closeLink);
-        domParent.appendChild(createMessageDom("reply_" +SELECTED_ROOM.id, REPLYING_TO));
+        domParent.appendChild(createMessageDom("reply_" +SELECTED_ROOM.id, REPLYING_TO, true));
     } else {
         document.body.classList.remove(R.klass.replyingTo);
     }
@@ -113,9 +113,10 @@ function onReplyingToUpdated() {
 /**
  * @param {string} channelId
  * @param {SlackMessage} msg
+ * @param {boolean=} skipAttachment
  * @return {Element}
 **/
-function doCreateMessageDom(channelId, msg) {
+function doCreateMessageDom(channelId, msg, skipAttachment) {
     var dom = document.createElement("div")
         ,ts = document.createElement("div")
         ,text = document.createElement("div")
@@ -124,6 +125,7 @@ function doCreateMessageDom(channelId, msg) {
         ,authorName = document.createElement("span")
         ,hover = document.createElement("ul")
         ,hoverReply = document.createElement("li")
+        ,attachments = document.createElement("ul")
         ,sender = msg.raw["user"] ?
             SLACK.context.users[msg.raw["user"]] :
             SLACK.context.bots[msg.raw["bot_id"]];
@@ -138,39 +140,37 @@ function doCreateMessageDom(channelId, msg) {
     hover.className = R.klass.msg.hover.container;
     hoverReply.className = R.klass.msg.hover.reply;
     ts.textContent = (new Date(msg.ts * 1000)).toLocaleTimeString();
-    var msgContents = msg.raw["text"] || "";
-    msgContents = msgContents
-        .replace(new RegExp('<([@#]?)([^>]*)>', 'g'),
-            function(_, type, entity) {
-                var sub = entity.split('|');
+    text.innerHTML = formatSlackText(msg.raw["text"] || "");
+    authorName.textContent = sender ? sender.name : (msg.raw["username"] || "?");
+    if (!sender && !msg.raw["username"])
+        text.textContent = msg.raw["subtype"] || JSON.stringify(msg.raw);
+    authorImg.src = sender ? sender.icons.image_48 : "";
+    author.appendChild(authorImg);
+    author.appendChild(authorName);
+    hover.appendChild(hoverReply);
+    dom.appendChild(author);
+    dom.appendChild(text);
+    dom.appendChild(ts);
+    dom.appendChild(attachments);
+    attachments.className = R.klass.msg.attachment.list;
+    if (skipAttachment !== true && msg.raw["attachments"]) {
+        msg.raw["attachments"].forEach(function(attachment) {
+            var domAttachment = createAttachmentDom(channelId, msg, attachment);
+            if (domAttachment)
+                attachments.appendChild(domAttachment);
+        });
+    }
+    dom.appendChild(hover);
+    return dom;
+}
+
+/**
+ * @param {string} fullText
+ * @return {string}
+**/
+function formatSlackText(fullText) {
+    var msgContents = fullText.split(/\r?\n/g);
 
-                if (type === '@') {
-                    if (!sub[1]) {
-                        var user = SLACK.context.getMember(sub[0]);
-                        sub[1] = user ? ('@' +user.name) : "Unknown member"; // TODO locale
-                    } else if ('@' !== sub[1][0]) {
-                        sub[1] = '@' +sub[1];
-                    }
-                    sub[0] = '#' +sub[0];
-                    sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkuser;
-                } else if (type === '#') {
-                    if (!sub[1]) {
-                        var chan = SLACK.context.getChannel(sub[0]);
-                        sub[1] = chan ? ('#' +chan.name) : "Unknown channel"; // TODO locale
-                    } else if ('#' !== sub[1][0]) {
-                        sub[1] = '#' +sub[1];
-                    }
-                    sub[0] = '#' +sub[0];
-                    sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkchan;
-                } else {
-                    if (!sub[1])
-                        sub[1] = sub[0];
-                    sub[2] = R.klass.msg.link;
-                }
-                return '<a href="' +sub[0] +'" class="' +sub[2] +'"' +(!type ? ' target="_blank"' : '') +'>' +sub[1] +'</a>';
-            })
-        .split(/\r?\n/g);
-    ;
     for (var msgContentIndex=0, nbMsgContents = msgContents.length; msgContentIndex < nbMsgContents; msgContentIndex++) {
         var msgContent = msgContents[msgContentIndex]
             ,_msgContent = ""
@@ -192,6 +192,7 @@ function doCreateMessageDom(channelId, msg) {
                 return "";
             return '<span class="' +Object.keys(mods).join(' ') +'">';
         };
+
         // Skip trailing
         while (i < msgLength && (msgContent[i] === ' ' || msgContent[i] === '\t'))
             i++;
@@ -252,33 +253,195 @@ function doCreateMessageDom(channelId, msg) {
             // Should not append
             _msgContent += '</span>';
         }
+        _msgContent = _msgContent.replace(new RegExp('<([@#]?)([^>]*)>', 'g'),
+            function(matched, type, entity) {
+                var sub = entity.split('|');
+
+                if (type === '@') {
+                    if (!sub[1]) {
+                        var user = SLACK.context.getMember(sub[0]);
+                        sub[1] = user ? ('@' +user.name) : "Unknown member"; // TODO locale
+                    } else if ('@' !== sub[1][0]) {
+                        sub[1] = '@' +sub[1];
+                    }
+                    sub[0] = '#' +sub[0];
+                    sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkuser;
+                } else if (type === '#') {
+                    if (!sub[1]) {
+                        var chan = SLACK.context.getChannel(sub[0]);
+                        sub[1] = chan ? ('#' +chan.name) : "Unknown channel"; // TODO locale
+                    } else if ('#' !== sub[1][0]) {
+                        sub[1] = '#' +sub[1];
+                    }
+                    sub[0] = '#' +sub[0];
+                    sub[2] = R.klass.msg.link +' ' +R.klass.msg.linkchan;
+                } else if (sub[0].indexOf("://") !== -1) {
+                    if (!sub[1])
+                        sub[1] = sub[0];
+                    sub[2] = R.klass.msg.link;
+                } else {
+                    return matched;
+                }
+                return '<a href="' +sub[0] +'" class="' +sub[2] +'"' +(!type ? ' target="_blank"' : '') +'>' +sub[1] +'</a>';
+            });
         if (quote)
             msgContents[msgContentIndex] = '<span class="' +R.klass.msg.style.quote +'">' +_msgContent +'</span>';
         else
             msgContents[msgContentIndex] = _msgContent;
     }
-    text.innerHTML = msgContents.join('<br/>');
-    authorName.textContent = sender ? sender.name : (msg.raw["username"] || "?");
-    if (!sender && !msg.raw["username"])
-        text.textContent = msg.raw["subtype"] || JSON.stringify(msg.raw);
-    authorImg.src = sender ? sender.icons.image_48 : "";
-    author.appendChild(authorImg);
-    author.appendChild(authorName);
-    hover.appendChild(hoverReply);
-    dom.appendChild(author);
-    dom.appendChild(text);
-    dom.appendChild(ts);
-    dom.appendChild(hover);
-    return dom;
+    return msgContents.join('<br/>');
+}
+
+/**
+ * @param {string} channelId
+ * @param {SlackMessage} msg
+ * @param {*} attachment
+ * @return {Element|null}
+**/
+function createAttachmentDom(channelId, msg, attachment) {
+    var rootDom = document.createElement("li")
+        ,attachmentBlock = document.createElement("div")
+
+        ,pretext = document.createElement("div")
+
+        ,titleBlock = document.createElement("a")
+
+        ,authorBlock = document.createElement("div")
+        ,authorImg = document.createElement("img")
+        ,authorName = document.createElement("a")
+
+        ,textBlock = document.createElement("div")
+        ,textDom = document.createElement("div")
+        ,thumbImgDom = document.createElement("img")
+        ,imgDom = document.createElement("img")
+
+        ,footerBlock = document.createElement("div")
+        ,footerIcon = document.createElement("img")
+        ,footerText = document.createElement("span")
+        ,footerTs = document.createElement("span")
+    ;
+
+    rootDom.className = R.klass.msg.attachment.container;
+    //Color
+    var color = "#e3e4e6";
+    if (attachment["color"]) {
+        if (attachment["color"][0] === '#')
+            color = attachment["color"][0];
+        else if (attachment["color"] === "good")
+            color = "#2fa44f";
+        else if (attachment["color"] === "warning")
+            color = "#de9e31";
+        else if (attachment["color"] === "danger")
+            color = "#d50200";
+    }
+    attachmentBlock.style.borderColor = color;
+
+    //Pretext
+    pretext.className = R.klass.msg.attachment.pretext;
+    if (attachment["pretext"]) {
+        pretext.innerHTML = formatSlackText(attachment["pretext"]);
+    } else {
+        pretext.classList.add(R.klass.hidden);
+    }
+
+    //Title
+    titleBlock.target = "_blank";
+    if (attachment["title"]) {
+        titleBlock.innerHTML = formatSlackText(attachment["title"]);
+        if (attachment["title_link"]) {
+            titleBlock.href = attachment["title_link"];
+        }
+        titleBlock.className = R.klass.msg.attachment.title;
+
+    } else {
+        titleBlock.className = R.klass.hidden + " " +R.klass.msg.attachment.title;
+    }
+
+    //Author
+    authorName.target = "_blank";
+    authorBlock.className = R.klass.msg.author;
+    if (attachment["author_name"]) {
+        authorName.innerHTML = formatSlackText(attachment["author_name"]);
+        authorName.href = attachment["author_link"] || "";
+        authorName.className = R.klass.msg.authorname;
+        authorImg.className = R.klass.msg.authorAvatar;
+        if (attachment["author_icon"])
+            authorImg.src = attachment["author_icon"];
+        else
+            authorImg.classList.add(R.klass.hidden);
+    } else {
+        authorBlock.classList.add(R.klass.hidden);
+    }
+
+    //Text
+    textDom.innerHTML = formatSlackText(attachment["text"] || "");
+    textDom.klassName = R.klass.msg.attachment.text;
+
+    // Img (small one)
+    thumbImgDom.className = R.klass.msg.attachment.thumbImg;
+    if (attachment["thumb_url"])
+        thumbImgDom.src = attachment["thumb_url"];
+    else
+        thumbImgDom.classList.add(R.klass.hidden);
+
+    //Img (the big one)
+    imgDom.className = R.klass.msg.attachment.img;
+    if (attachment["image_url"])
+        imgDom.src = attachment["image_url"];
+    else
+        imgDom.classList.add(R.klass.hidden);
+
+    //Footer
+    footerBlock.className = R.klass.msg.attachment.footer;
+    footerText.className = R.klass.msg.attachment.footerText;
+    footerIcon.className = R.klass.msg.attachment.footerIcon;
+    if (attachment["footer"]) {
+        footerText.innerHTML = formatSlackText(attachment["footer"]);
+        if (attachment["footer_icon"])
+            footerIcon.src = attachment["footer_icon"];
+        else
+            footerIcon.classList.add(R.klass.hidden);
+    } else {
+        footerIcon.classList.add(R.klass.hidden);
+        footerText.classList.add(R.klass.hidden);
+    }
+
+    //Ts
+    footerTs.className = R.klass.msg.ts;
+    if (attachment["ts"])
+        footerTs.textContent = new Date(parseFloat(attachment["ts"])).toString();
+    else
+        footerTs.classList.add(R.klass.hidden);
+
+    // TODO Field [ {title, value, short } ]
+    // TODO actions (button stuff)
+
+    authorBlock.appendChild(authorImg);
+    authorBlock.appendChild(authorName);
+    textBlock.appendChild(textDom);
+    textBlock.appendChild(thumbImgDom);
+    footerBlock.appendChild(footerIcon);
+    footerBlock.appendChild(footerText);
+    footerBlock.appendChild(footerTs);
+    attachmentBlock.appendChild(titleBlock);
+    attachmentBlock.appendChild(authorBlock);
+    attachmentBlock.appendChild(textBlock);
+    attachmentBlock.appendChild(imgDom);
+    attachmentBlock.appendChild(footerBlock);
+    rootDom.appendChild(pretext);
+    rootDom.appendChild(attachmentBlock);
+    console.log(attachment);
+    return rootDom;
 }
 
 /**
  * @param {string} channelId
  * @param {SlackMessage} msg
+ * @param {boolean=} skipAttachment
  * @return {Element}
 **/
-function doCreateMeMessageDom(channelId, msg) {
-    var dom = doCreateMessageDom(channelId, msg);
+function doCreateMeMessageDom(channelId, msg, skipAttachment) {
+    var dom = doCreateMessageDom(channelId, msg, skipAttachment);
     dom.classList.add(R.klass.msg.meMessage);
     return dom;
 }
@@ -286,13 +449,14 @@ function doCreateMeMessageDom(channelId, msg) {
 /**
  * @param {string} channelId
  * @param {SlackMessage} msg
+ * @param {boolean=} skipAttachment
  * @return {Element}
 **/
-function createMessageDom(channelId, msg) {
+function createMessageDom(channelId, msg, skipAttachment) {
     if (msg.subtype === "me_message") {
-        return doCreateMeMessageDom(channelId, msg);
+        return doCreateMeMessageDom(channelId, msg, skipAttachment);
     }
-    return doCreateMessageDom(channelId, msg);
+    return doCreateMessageDom(channelId, msg, skipAttachment);
 }
 
 function updateTitle() {

+ 6 - 4
srv/public/index.html

@@ -14,12 +14,14 @@
             <div class="slack-chat-title" id="currentRoomTitle"></div>
             <div class="slack-chat-content" id="chatWindow"></div>
             <div class="slack-chat-control">
-                <form id="msgForm">
+                <form id="msgForm" class="msgform">
                     <div id="replyToContainer" class="replyto-container">
                     </div>
-                    <input type="text" id="msgInput" />
-                    <a id="attachFile" href="#!"><img src="paperclip.svg" alt="Send file" class="attach-file-icon" /></a>
-                    <input type="submit" class="button" />
+                    <input type="text" id="msgInput" class="msgform-input" autocomplete="off" />
+                    <div class="msgform-action">
+                        <input type="submit" class="button" value=">" />
+                        <a id="attachFile" href="#!" class="button"><img src="paperclip.svg" alt="Send file" class="attach-file-icon" /></a>
+                    </div>
                 </form>
             </div>
         </div>

+ 29 - 23
srv/public/slack.min.js

@@ -1,24 +1,30 @@
-function g(a){this.id=a.id;this.name=a.name}function k(a,b){this.id=a.id;this.name=a.name;this.b=parseFloat(a.last_read);this.a={};if(a.members)for(var c=0,d=a.members.length;c<d;c++){var f=r(b,a.members[c]);this.a[f.id]=f;f.f[this.id]=this}}function t(a,b){var c=[];this.id=b.id;this.a={};for(var d=0,f=b.members.length;d<f;d++){var e=r(a,b.members[d]);this.a[b.members[d]]=e;e.f[this.id]=this;c.push(e.name)}this.name=c.join(", ");this.b=parseFloat(b.last_read)}
-function u(a,b){this.id=b.id;this.c=a;this.b=parseFloat(b.last_read)}function x(a){this.id=a.id;this.name=a.name;this.status=a.status;this.b={B:a.profile.image_24,C:a.profile.image_32,l:a.profile.image_48,s:a.profile.image_72,A:a.profile.image_192,F:a.profile.image_512};this.f={};this.a=null}function aa(a){this.id=a.id;this.name=a.name;this.b={D:a.icons.image_36,l:a.icons.image_48,s:a.icons.image_72};this.f={};this.a=null}
-function D(){this.i=null;this.f={};this.b={};this.g={};this.a={};this.c=null;this.h={}}function r(a,b){return a.a[b]||a.h[b]||null}function G(a,b){return a.f[b]||a.g[b]||a.b[b]||null}"undefined"!==typeof module&&(module.o.u=D);function ba(a){this.g=a.user;this.b=parseFloat(a.ts);this.type=a.type;this.c=a.subtype;this.a=a}function H(a,b,c){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;c&&J(this,c)}function J(a,b){var c=0;b.forEach(function(a){c=Math.max(this.push(a),c)}.bind(a));ca(a)}H.prototype.push=function(a){for(var b=parseFloat(a.ts),c=0,d=this.a.length;c<d;c++)if(this.a[c].b===b)return b;for(this.a.push(new ba(a));this.a.length>this.b;)this.a.shift();return b};
-function ca(a){a.a.sort(function(a,c){return a.b-c.b})}"undefined"!==typeof module&&(module.o.w=H);var K=null;
-function da(){var a=document.createDocumentFragment(),b=L.a.c?Object.keys(L.a.c.f):[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:(L.a.f[a]||L.a.b[a]).name.localeCompare((L.a.f[b]||L.a.b[b]).name)});b.forEach(function(b){b=L.a.f[b]||L.a.b[b];var c=document.createElement("li");c.id=b.id;"D"===b.id[0]?c.className="slack-context-room slack-ims":"G"===b.id[0]?c.className="slack-context-room slack-group":"C"===b.id[0]&&(c.className="slack-context-room slack-channel");c.textContent=b.name;c&&a.appendChild(c)});
+function h(a){this.id=a.id;this.name=a.name}function l(a,b){this.id=a.id;this.name=a.name;this.b=parseFloat(a.last_read);this.a={};if(a.members)for(var e=0,c=a.members.length;e<c;e++){var f=t(b,a.members[e]);this.a[f.id]=f;f.f[this.id]=this}}function u(a,b){var e=[];this.id=b.id;this.a={};for(var c=0,f=b.members.length;c<f;c++){var d=t(a,b.members[c]);this.a[b.members[c]]=d;d.f[this.id]=this;e.push(d.name)}this.name=e.join(", ");this.b=parseFloat(b.last_read)}
+function v(a,b){this.id=b.id;this.c=a;this.b=parseFloat(b.last_read)}function w(a){this.id=a.id;this.name=a.name;this.status=a.status;this.b={B:a.profile.image_24,C:a.profile.image_32,l:a.profile.image_48,s:a.profile.image_72,A:a.profile.image_192,F:a.profile.image_512};this.f={};this.a=null}function x(a){this.id=a.id;this.name=a.name;this.b={D:a.icons.image_36,l:a.icons.image_48,s:a.icons.image_72};this.f={};this.a=null}
+function A(){this.i=null;this.f={};this.b={};this.g={};this.a={};this.c=null;this.h={}}function t(a,b){return a.a[b]||a.h[b]||null}function D(a,b){return a.f[b]||a.g[b]||a.b[b]||null}"undefined"!==typeof module&&(module.o.u=A);function E(a){this.g=a.user;this.b=parseFloat(a.ts);this.type=a.type;this.c=a.subtype;this.a=a}function G(a,b,e){this.id="string"===typeof a?a:a.id;this.a=[];this.b=b;e&&H(this,e)}function H(a,b){var e=0;b.forEach(function(a){e=Math.max(this.push(a),e)}.bind(a));I(a)}G.prototype.push=function(a){for(var b=parseFloat(a.ts),e=0,c=this.a.length;e<c;e++)if(this.a[e].b===b)return b;for(this.a.push(new E(a));this.a.length>this.b;)this.a.shift();return b};
+function I(a){a.a.sort(function(a,e){return a.b-e.b})}"undefined"!==typeof module&&(module.o.w=G);var J=null;
+function K(){var a=document.createDocumentFragment(),b=L.a.c?Object.keys(L.a.c.f):[];b.sort(function(a,b){return a[0]!==b[0]?a[0]-b[0]:(L.a.f[a]||L.a.b[a]).name.localeCompare((L.a.f[b]||L.a.b[b]).name)});b.forEach(function(b){b=L.a.f[b]||L.a.b[b];var c=document.createElement("li");c.id=b.id;"D"===b.id[0]?c.className="slack-context-room slack-ims":"G"===b.id[0]?c.className="slack-context-room slack-group":"C"===b.id[0]&&(c.className="slack-context-room slack-channel");c.textContent=b.name;c&&a.appendChild(c)});
 b=L.a.a?Object.keys(L.a.a):[];b.sort(function(a,b){return L.a.a[a].name.localeCompare(L.a.a[b].name)});b.forEach(function(b){b=L.a.a[b].a;var c=document.createElement("li");c.id=b.id;c.className="slack-context-room";c.textContent=b.c.name;c&&a.appendChild(c)});document.getElementById("chanList").textContent="";document.getElementById("chanList").appendChild(a)}function M(a){a?document.body.classList.remove("no-network"):document.body.classList.add("no-network")}
-function N(){if(K){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){K=null;N()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(O("reply_"+P.id,K))}else document.body.classList.remove("replyingTo")}
-function R(a,b){var c=document.createElement("div"),d=document.createElement("div"),f=document.createElement("div"),e=document.createElement("div"),m=document.createElement("img"),v=document.createElement("span"),w=document.createElement("ul"),z=document.createElement("li"),A=b.a.user?L.a.a[b.a.user]:L.a.h[b.a.bot_id];c.id=a+"_"+b.b;c.className="slackmsg-item";d.className="slackmsg-ts";f.className="slackmsg-msg";e.className="slackmsg-author";m.className="slackmsg-author-img";v.className="slackmsg-author-name";
-w.className="slackmsg-hover";z.className="slackmsg-hover-reply";d.textContent=(new Date(1E3*b.b)).toLocaleTimeString();for(var y=b.a.text||"",y=y.replace(RegExp("<([@#]?)([^>]*)>","g"),function(a,b,c){a=c.split("|");"@"===b?(a[1]?"@"!==a[1][0]&&(a[1]="@"+a[1]):(c=r(L.a,a[0]),a[1]=c?"@"+c.name:"Unknown member"),a[0]="#"+a[0],a[2]="slackmsg-link slackmsg-link-user"):"#"===b?(a[1]?"#"!==a[1][0]&&(a[1]="#"+a[1]):(c=G(L.a,a[0]),a[1]=c?"#"+c.name:"Unknown channel"),a[0]="#"+a[0],a[2]="slackmsg-link slackmsg-link-chan"):
-(a[1]||(a[1]=a[0]),a[2]="slackmsg-link");return'<a href="'+a[0]+'" class="'+a[2]+'"'+(b?"":' target="_blank"')+">"+a[1]+"</a>"}).split(/\r?\n/g),E=0,ea=y.length;E<ea;E++){for(var n=y[E],p="",h={},Q=!1,l=0,I=n.length,F=function(a,b,c){for(;a[b];){if(" "!=a[b]&&a[b]!=c&&a[b+1]==c)return!0;b++}return!1},B=function(a){return Object.keys(h).length?'<span class="'+Object.keys(a).join(" ")+'">':""};l<I&&(" "===n[l]||"\t"===n[l]);)l++;"&gt;"===n.substr(l,4)&&(Q=!0,l+=4);for(;l<I;l++){var q=n[l];if(!h["slackmsg-style-bold"]&&
-"*"===q&&n[l+1]&&F(n,l,q))Object.keys(h).length&&(p+="</span>"),h["slackmsg-style-bold"]=!0,p+=B(h);else if(!h["slackmsg-style-strike"]&&"~"===q&&n[l+1]&&F(n,l,q))Object.keys(h).length&&(p+="</span>"),h["slackmsg-style-strike"]=!0,p+=B(h);else if(!h["slackmsg-style-code"]&&"`"===q&&n[l+1]&&F(n,l,q))Object.keys(h).length&&(p+="</span>"),h["slackmsg-style-code"]=!0,p+=B(h);else if(!h["slackmsg-style-italic"]&&"_"===q&&n[l+1]&&F(n,l,q))Object.keys(h).length&&(p+="</span>"),h["slackmsg-style-italic"]=
-!0,p+=B(h);else{var C=!1,p=p+q;do{if(h["slackmsg-style-bold"]&&"*"!==q&&"*"===n[l+1])delete h["slackmsg-style-bold"],C=!0;else if(h["slackmsg-style-strike"]&&"~"!==q&&"~"===n[l+1])delete h["slackmsg-style-strike"],C=!0;else if(h["slackmsg-style-code"]&&"`"!==q&&"`"===n[l+1])delete h["slackmsg-style-code"],C=!0;else if(h["slackmsg-style-italic"]&&"_"!==q&&"_"===n[l+1])delete h["slackmsg-style-italic"],C=!0;else break;q=n[++l]}while(l<I);C&&(p+="</span>"+B(h))}}h&&(p+="</span>");y[E]=Q?'<span class="slackmsg-style-quote">'+
-p+"</span>":p}f.innerHTML=y.join("<br/>");v.textContent=A?A.name:b.a.username||"?";A||b.a.username||(f.textContent=b.a.subtype||JSON.stringify(b.a));m.src=A?A.b.l:"";e.appendChild(m);e.appendChild(v);w.appendChild(z);c.appendChild(e);c.appendChild(f);c.appendChild(d);c.appendChild(w);return c}function O(a,b){var c;"me_message"===b.c?(c=R(a,b),c.classList.add("slackmsg-me_message")):c=R(a,b);return c}
-function S(){var a=0,b=0,c,d;for(d in T)T.hasOwnProperty(d)&&(a+=T[d].m,b+=T[d].j);b&&(c="(!"+b);a&&(c=(c?c+" - ":"(")+a);c=c?c+(") "+L.a.i.name):L.a.i.name;document.title=c}function U(){var a=document.createDocumentFragment(),b=P.id;document.getElementById("chatWindow").textContent="";L.b[b]&&L.b[b].a.forEach(function(c){"message"===c.type&&(c=O(b,c),a.appendChild(c))});var c=document.getElementById("chatWindow");c.appendChild(a);c.scrollTop=c.scrollHeight-c.clientHeight}
-function fa(a){for(;a.target!==a.currentTarget&&a.target;){if(a.target.classList.contains("slack-context-room")){if((a=L.a.f[a.target.id]||L.a.g[a.target.id]||L.a.b[a.target.id])&&a!==P){P&&document.getElementById(P.id).classList.remove("selected");document.getElementById(a.id).classList.add("selected");document.body.classList.remove("no-room-selected");P=a;a=void 0;var b=P.name||(P.c?P.c.name:void 0);if(!b){b=[];for(a in P.a)b.push(P.a[a].name);b=b.join(", ")}document.getElementById("currentRoomTitle").textContent=
-b;U();V();document.getElementById("fileUploadContainer").classList.add("hidden");W();K&&(K=null,N());P.b&&!L.b[P.id]&&(a=new XMLHttpRequest,a.open("GET","api/hist?room="+P.id,!0),a.send(null))}break}a.target=a.target.parentElement}}
-function ga(a){for(var b=a.target;b!==a.currentTarget&&b&&!b.classList.contains("slackmsg-hover");){if(b.classList.contains("slackmsg-hover-reply")){a:{for(b=b||a.target;b!==a.currentTarget&&b;){if(b.classList.contains("slackmsg-item")){a=b.id;break a}b=b.parentElement}a=void 0}if(a){a=parseFloat(a.split("_")[1]);for(var b=L.b[P.id].a,c=0,d=b.length;c<d&&b[c].b<=a;c++)if(b[c].b===a){K!==b[c]&&(K=b[c],N());break}}break}b=b.parentElement}}function V(){document.getElementById("msgInput").focus()}
-document.addEventListener("DOMContentLoaded",function(){document.getElementById("chatList").addEventListener("click",fa);document.getElementById("chatWindow").addEventListener("click",ga);document.getElementById("fileUploadCancel").addEventListener("click",function(a){a.preventDefault();document.getElementById("fileUploadError").classList.add("hidden");document.getElementById("fileUploadContainer").classList.add("hidden");document.getElementById("fileUploadInput").value="";return!1});document.getElementById("fileUploadForm").addEventListener("submit",
-function(a){a.preventDefault();a=document.getElementById("fileUploadInput");var b=a.value;b&&(b=b.substr(b.lastIndexOf("\\")+1),ha(b,a.files[0],function(a){var b=document.getElementById("fileUploadError");a?(b.textContent=a,b.classList.remove("hidden")):(b.classList.add("hidden"),document.getElementById("fileUploadInput").value="",document.getElementById("fileUploadContainer").classList.add("hidden"))}));return!1});document.getElementById("attachFile").addEventListener("click",function(a){a.preventDefault();
-P&&document.getElementById("fileUploadContainer").classList.remove("hidden");return!1});document.getElementById("msgForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("msgInput");if(P&&a.value){var b=P,c=K,d=new XMLHttpRequest,f="api/msg?room="+b.id+"&text="+encodeURIComponent(a.value);if(c){var e=r(L.a,c.g),m="Message";"C"===b.id[0]?m="Channel message":"D"===b.id[0]?m="Direct message":"G"===b.id[0]&&(m="Group message");f+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:c.a.text||
-"",author_name:"<@"+e.id+"|"+e.name+">",author_icon:e.b.l,text:c.a.text||"",footer:m,ts:c.b}]))}d.open("POST",f,!0);d.send(null);a.value="";K&&(K=null,N())}V();return!1});window.addEventListener("blur",function(){window.hasFocus=!1});window.addEventListener("focus",function(){window.hasFocus=!0;P&&W();V()});window.hasFocus=!0;X()});var L,T={};function ia(a,b){if(a&&(a!==P||!window.hasFocus)){var c=new RegExp("<@"+L.a.c.id),d=!1;T[a.id]||(T[a.id]={j:0,m:0});b.forEach(function(b){"message"===b.type&&("D"===a.id[0]||b.text.match(c)?(T[a.id].j++,d=!0):T[a.id].m++)});S();document.getElementById(a.id).classList.add("unread");d&&document.getElementById(a.id).classList.add("unreadHi")}}function W(){var a=P;T[a.id]&&(T[a.id]={j:0,m:0},S());a=document.getElementById(a.id);a.classList.remove("unread");a.classList.remove("unreadHi")}
-L=new function(){this.c=0;this.a=new D;this.b={}};var Y=0,P=null;function Z(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var c=null,d=2===Math.floor(b.status/100);if(d){Y&&(Y=0,M(!0));c=b.response;try{c=JSON.parse(c)}catch(f){c=null}}else Y?(Y+=Math.floor((Y||5)/2),Y=Math.min(60,Y)):(Y=5,M(!1));a(d,c)}else Y&&(Y=0,M(!0)),Z(a)};b.open("GET","api?v="+L.c,!0);b.send(null)}
-function ja(a,b){if(a){if(b){var c=L;b.v&&(c.c=b.v);if(b["static"]){for(var d=c.a,f=b["static"],e=0,m=f.bots.length;e<m;e++)d.h[f.bots[e].id]=new aa(f.bots[e]);e=0;for(m=f.users.length;e<m;e++)d.a[f.users[e].id]=new x(f.users[e]);e=0;for(m=f.ims.length;e<m;e++){var v=r(d,f.ims[e].user);v&&(v.a=new u(v,f.ims[e]),d.g[v.a.id]=v.a)}e=0;for(m=f.channels.length;e<m;e++)d.f[f.channels[e].id]=new k(f.channels[e],d);e=0;for(m=f.groups.length;e<m;e++)d.b[f.groups[e].id]=new t(d,f.groups[e]);d.i=new g(f.team);
-d.c=r(d,f.self.id);da()}if(b.live){for(var w in b.live)(d=c.b[w])?J(d,b.live[w]):c.b[w]=new H(w,250,b.live[w]);for(var z in b.live)ia(G(c.a,z),b.live[z]),P&&b.live[P.id]&&U()}}X()}else setTimeout(X,1E3*Y)}function X(){Z(ja)}function ha(a,b,c){var d=P;new FileReader;var f=new FormData,e=new XMLHttpRequest;f.append("file",b);f.append("filename",a);e.onreadystatechange=function(){4===e.readyState&&(204===e.status?c(null):c(e.statusText))};e.open("POST","api/file?room="+d.id);e.send(f)};
+function N(){if(J){document.body.classList.add("replyingTo");var a=document.getElementById("replyToContainer"),b=document.createElement("a");b.addEventListener("click",function(){J=null;N()});b.className="replyto-close";b.textContent="x";a.textContent="";a.appendChild(b);a.appendChild(O("reply_"+P.id,J,!0))}else document.body.classList.remove("replyingTo")}
+function Q(a,b,e){var c=document.createElement("div"),f=document.createElement("div"),d=document.createElement("div"),k=document.createElement("div"),g=document.createElement("img"),n=document.createElement("span"),p=document.createElement("ul"),r=document.createElement("li"),m=document.createElement("ul"),q=b.a.user?L.a.a[b.a.user]:L.a.h[b.a.bot_id];c.id=a+"_"+b.b;c.className="slackmsg-item";f.className="slackmsg-ts";d.className="slackmsg-msg";k.className="slackmsg-author";g.className="slackmsg-author-img";
+n.className="slackmsg-author-name";p.className="slackmsg-hover";r.className="slackmsg-hover-reply";f.textContent=(new Date(1E3*b.b)).toLocaleTimeString();d.innerHTML=R(b.a.text||"");n.textContent=q?q.name:b.a.username||"?";q||b.a.username||(d.textContent=b.a.subtype||JSON.stringify(b.a));g.src=q?q.b.l:"";k.appendChild(g);k.appendChild(n);p.appendChild(r);c.appendChild(k);c.appendChild(d);c.appendChild(f);c.appendChild(m);m.className="slackmsg-attachments";!0!==e&&b.a.attachments&&b.a.attachments.forEach(function(a){var b=
+document.createElement("li"),c=document.createElement("div"),e=document.createElement("div"),d=document.createElement("a"),f=document.createElement("div"),g=document.createElement("img"),k=document.createElement("a"),F=document.createElement("div"),n=document.createElement("div"),p=document.createElement("img"),q=document.createElement("img"),r=document.createElement("div"),y=document.createElement("img"),B=document.createElement("span"),C=document.createElement("span");b.className="slackmsg-attachment";
+var z="#e3e4e6";a.color&&("#"===a.color[0]?z=a.color[0]:"good"===a.color?z="#2fa44f":"warning"===a.color?z="#de9e31":"danger"===a.color&&(z="#d50200"));c.style.borderColor=z;e.className="slackmsg-attachment-pretext";a.pretext?e.innerHTML=R(a.pretext):e.classList.add("hidden");d.target="_blank";a.title?(d.innerHTML=R(a.title),a.title_link&&(d.href=a.title_link),d.className="slackmsg-attachment-title"):d.className="hidden slackmsg-attachment-title";k.target="_blank";f.className="slackmsg-author";a.author_name?
+(k.innerHTML=R(a.author_name),k.href=a.author_link||"",k.className="slackmsg-author-name",g.className="slackmsg-author-img",a.author_icon?g.src=a.author_icon:g.classList.add("hidden")):f.classList.add("hidden");n.innerHTML=R(a.text||"");n.a="slackmsg-attachment-text";p.className="slackmsg-attachment-thumb";a.thumb_url?p.src=a.thumb_url:p.classList.add("hidden");q.className="slackmsg-attachment-img";a.image_url?q.src=a.image_url:q.classList.add("hidden");r.className="slackmsg-attachment-footer";B.className=
+"slackmsg-attachment-footer-text";y.className="slackmsg-attachment-footer-icon";a.footer?(B.innerHTML=R(a.footer),a.footer_icon?y.src=a.footer_icon:y.classList.add("hidden")):(y.classList.add("hidden"),B.classList.add("hidden"));C.className="slackmsg-ts";a.ts?C.textContent=(new Date(parseFloat(a.ts))).toString():C.classList.add("hidden");f.appendChild(g);f.appendChild(k);F.appendChild(n);F.appendChild(p);r.appendChild(y);r.appendChild(B);r.appendChild(C);c.appendChild(d);c.appendChild(f);c.appendChild(F);
+c.appendChild(q);c.appendChild(r);b.appendChild(e);b.appendChild(c);console.log(a);b&&m.appendChild(b)});c.appendChild(p);return c}
+function R(a){a=a.split(/\r?\n/g);for(var b=0,e=a.length;b<e;b++){for(var c=a[b],f="",d={},k=!1,g=0,n=c.length,p=function(a,b,c){for(;a[b];){if(" "!=a[b]&&a[b]!=c&&a[b+1]==c)return!0;b++}return!1},r=function(a){return Object.keys(d).length?'<span class="'+Object.keys(a).join(" ")+'">':""};g<n&&(" "===c[g]||"\t"===c[g]);)g++;"&gt;"===c.substr(g,4)&&(k=!0,g+=4);for(;g<n;g++){var m=c[g];if(!d["slackmsg-style-bold"]&&"*"===m&&c[g+1]&&p(c,g,m))Object.keys(d).length&&(f+="</span>"),d["slackmsg-style-bold"]=
+!0,f+=r(d);else if(!d["slackmsg-style-strike"]&&"~"===m&&c[g+1]&&p(c,g,m))Object.keys(d).length&&(f+="</span>"),d["slackmsg-style-strike"]=!0,f+=r(d);else if(!d["slackmsg-style-code"]&&"`"===m&&c[g+1]&&p(c,g,m))Object.keys(d).length&&(f+="</span>"),d["slackmsg-style-code"]=!0,f+=r(d);else if(!d["slackmsg-style-italic"]&&"_"===m&&c[g+1]&&p(c,g,m))Object.keys(d).length&&(f+="</span>"),d["slackmsg-style-italic"]=!0,f+=r(d);else{var q=!1,f=f+m;do{if(d["slackmsg-style-bold"]&&"*"!==m&&"*"===c[g+1])delete d["slackmsg-style-bold"],
+q=!0;else if(d["slackmsg-style-strike"]&&"~"!==m&&"~"===c[g+1])delete d["slackmsg-style-strike"],q=!0;else if(d["slackmsg-style-code"]&&"`"!==m&&"`"===c[g+1])delete d["slackmsg-style-code"],q=!0;else if(d["slackmsg-style-italic"]&&"_"!==m&&"_"===c[g+1])delete d["slackmsg-style-italic"],q=!0;else break;m=c[++g]}while(g<n);q&&(f+="</span>"+r(d))}}d&&(f+="</span>");f=f.replace(RegExp("<([@#]?)([^>]*)>","g"),function(a,b,c){c=c.split("|");if("@"===b)c[1]?"@"!==c[1][0]&&(c[1]="@"+c[1]):(a=t(L.a,c[0]),
+c[1]=a?"@"+a.name:"Unknown member"),c[0]="#"+c[0],c[2]="slackmsg-link slackmsg-link-user";else if("#"===b)c[1]?"#"!==c[1][0]&&(c[1]="#"+c[1]):(a=D(L.a,c[0]),c[1]=a?"#"+a.name:"Unknown channel"),c[0]="#"+c[0],c[2]="slackmsg-link slackmsg-link-chan";else if(-1!==c[0].indexOf("://"))c[1]||(c[1]=c[0]),c[2]="slackmsg-link";else return a;return'<a href="'+c[0]+'" class="'+c[2]+'"'+(b?"":' target="_blank"')+">"+c[1]+"</a>"});a[b]=k?'<span class="slackmsg-style-quote">'+f+"</span>":f}return a.join("<br/>")}
+function O(a,b,e){"me_message"===b.c?(a=Q(a,b,e),a.classList.add("slackmsg-me_message")):a=Q(a,b,e);return a}function S(){var a=0,b=0,e,c;for(c in T)T.hasOwnProperty(c)&&(a+=T[c].m,b+=T[c].j);b&&(e="(!"+b);a&&(e=(e?e+" - ":"(")+a);e=e?e+(") "+L.a.i.name):L.a.i.name;document.title=e}
+function U(){var a=document.createDocumentFragment(),b=P.id;document.getElementById("chatWindow").textContent="";L.b[b]&&L.b[b].a.forEach(function(c){"message"===c.type&&(c=O(b,c),a.appendChild(c))});var e=document.getElementById("chatWindow");e.appendChild(a);e.scrollTop=e.scrollHeight-e.clientHeight}
+function aa(a){for(;a.target!==a.currentTarget&&a.target;){if(a.target.classList.contains("slack-context-room")){if((a=L.a.f[a.target.id]||L.a.g[a.target.id]||L.a.b[a.target.id])&&a!==P){P&&document.getElementById(P.id).classList.remove("selected");document.getElementById(a.id).classList.add("selected");document.body.classList.remove("no-room-selected");P=a;a=void 0;var b=P.name||(P.c?P.c.name:void 0);if(!b){b=[];for(a in P.a)b.push(P.a[a].name);b=b.join(", ")}document.getElementById("currentRoomTitle").textContent=
+b;U();V();document.getElementById("fileUploadContainer").classList.add("hidden");W();J&&(J=null,N());P.b&&!L.b[P.id]&&(a=new XMLHttpRequest,a.open("GET","api/hist?room="+P.id,!0),a.send(null))}break}a.target=a.target.parentElement}}
+function ba(a){for(var b=a.target;b!==a.currentTarget&&b&&!b.classList.contains("slackmsg-hover");){if(b.classList.contains("slackmsg-hover-reply")){a:{for(b=b||a.target;b!==a.currentTarget&&b;){if(b.classList.contains("slackmsg-item")){a=b.id;break a}b=b.parentElement}a=void 0}if(a){a=parseFloat(a.split("_")[1]);for(var b=L.b[P.id].a,e=0,c=b.length;e<c&&b[e].b<=a;e++)if(b[e].b===a){J!==b[e]&&(J=b[e],N());break}}break}b=b.parentElement}}function V(){document.getElementById("msgInput").focus()}
+document.addEventListener("DOMContentLoaded",function(){document.getElementById("chatList").addEventListener("click",aa);document.getElementById("chatWindow").addEventListener("click",ba);document.getElementById("fileUploadCancel").addEventListener("click",function(a){a.preventDefault();document.getElementById("fileUploadError").classList.add("hidden");document.getElementById("fileUploadContainer").classList.add("hidden");document.getElementById("fileUploadInput").value="";return!1});document.getElementById("fileUploadForm").addEventListener("submit",
+function(a){a.preventDefault();a=document.getElementById("fileUploadInput");var b=a.value;b&&(b=b.substr(b.lastIndexOf("\\")+1),ca(b,a.files[0],function(a){var b=document.getElementById("fileUploadError");a?(b.textContent=a,b.classList.remove("hidden")):(b.classList.add("hidden"),document.getElementById("fileUploadInput").value="",document.getElementById("fileUploadContainer").classList.add("hidden"))}));return!1});document.getElementById("attachFile").addEventListener("click",function(a){a.preventDefault();
+P&&document.getElementById("fileUploadContainer").classList.remove("hidden");return!1});document.getElementById("msgForm").addEventListener("submit",function(a){a.preventDefault();a=document.getElementById("msgInput");if(P&&a.value){var b=P,e=J,c=new XMLHttpRequest,f="api/msg?room="+b.id+"&text="+encodeURIComponent(a.value);if(e){var d=t(L.a,e.g),k="Message";"C"===b.id[0]?k="Channel message":"D"===b.id[0]?k="Direct message":"G"===b.id[0]&&(k="Group message");f+="&attachments="+encodeURIComponent(JSON.stringify([{fallback:e.a.text||
+"",author_name:"<@"+d.id+"|"+d.name+">",author_icon:d.b.l,text:e.a.text||"",footer:k,ts:e.b}]))}c.open("POST",f,!0);c.send(null);a.value="";J&&(J=null,N())}V();return!1});window.addEventListener("blur",function(){window.hasFocus=!1});window.addEventListener("focus",function(){window.hasFocus=!0;P&&W();V()});window.hasFocus=!0;X()});var L,T={};function da(a,b){if(a&&(a!==P||!window.hasFocus)){var e=new RegExp("<@"+L.a.c.id),c=!1;T[a.id]||(T[a.id]={j:0,m:0});b.forEach(function(b){"message"===b.type&&("D"===a.id[0]||b.text.match(e)?(T[a.id].j++,c=!0):T[a.id].m++)});S();document.getElementById(a.id).classList.add("unread");c&&document.getElementById(a.id).classList.add("unreadHi")}}function W(){var a=P;T[a.id]&&(T[a.id]={j:0,m:0},S());a=document.getElementById(a.id);a.classList.remove("unread");a.classList.remove("unreadHi")}
+L=new function(){this.c=0;this.a=new A;this.b={}};var Y=0,P=null;function Z(a){var b=new XMLHttpRequest;b.timeout=6E4;b.onreadystatechange=function(){if(4===b.readyState)if(b.status){var e=null,c=2===Math.floor(b.status/100);if(c){Y&&(Y=0,M(!0));e=b.response;try{e=JSON.parse(e)}catch(f){e=null}}else Y?(Y+=Math.floor((Y||5)/2),Y=Math.min(60,Y)):(Y=5,M(!1));a(c,e)}else Y&&(Y=0,M(!0)),Z(a)};b.open("GET","api?v="+L.c,!0);b.send(null)}
+function ea(a,b){if(a){if(b){var e=L;b.v&&(e.c=b.v);if(b["static"]){for(var c=e.a,f=b["static"],d=0,k=f.bots.length;d<k;d++)c.h[f.bots[d].id]=new x(f.bots[d]);d=0;for(k=f.users.length;d<k;d++)c.a[f.users[d].id]=new w(f.users[d]);d=0;for(k=f.ims.length;d<k;d++){var g=t(c,f.ims[d].user);g&&(g.a=new v(g,f.ims[d]),c.g[g.a.id]=g.a)}d=0;for(k=f.channels.length;d<k;d++)c.f[f.channels[d].id]=new l(f.channels[d],c);d=0;for(k=f.groups.length;d<k;d++)c.b[f.groups[d].id]=new u(c,f.groups[d]);c.i=new h(f.team);
+c.c=t(c,f.self.id);K()}if(b.live){for(var n in b.live)(c=e.b[n])?H(c,b.live[n]):e.b[n]=new G(n,250,b.live[n]);for(var p in b.live)da(D(e.a,p),b.live[p]),P&&b.live[P.id]&&U()}}X()}else setTimeout(X,1E3*Y)}function X(){Z(ea)}function ca(a,b,e){var c=P;new FileReader;var f=new FormData,d=new XMLHttpRequest;f.append("file",b);f.append("filename",a);d.onreadystatechange=function(){4===d.readyState&&(204===d.status?e(null):e(d.statusText))};d.open("POST","api/file?room="+c.id);d.send(f)};

+ 9 - 1
srv/public/style.css

@@ -67,9 +67,17 @@ body {
 .slackmsg-style-italic { font-style: italic; }
 .slackmsg-style-strike { text-decoration: line-through; }
 .slackmsg-style-code { background: #FFF6B4; color: red; font-weight: normal; font-style: normal; text-decoration: none; }
-.slackmsg-style-quote { border-left: 3px solid grey; padding-left: 10px; }
+.slackmsg-style-quote { border-left: 3px solid #e3e4e6; padding-left: 10px; }
+
+.slackmsg-attachments { list-style: none; }
+.slackmsg-attachment .slackmsg-author-img { max-height: 18px; max-width: 18px; vertical-align: middle; margin-right: 5px; }
+.slackmsg-attachment-img { max-height: 200px; max-width: 400px; }
 
 .attach-file-icon { height: 1.5em; vertical-align: bottom; }
+.msgform { width: 100%; }
+.msgform-input { width: calc(100% - 175px); margin-left: 15px; }
+.msgform-action { display: inline-block; }
+.msgform-action * { height: 1.5em; }
 
 .file-upload-container { position: fixed; z-index: 5000; background: rgba(55, 55, 55, 0.8); top: 0; bottom: 0; right: 0; left: 0; max-width: 100%; margin: auto; height: 200px; width: 500px; max-height: 100%; padding: 1.5em; border: 1px solid rgba(55, 55, 55, 1); border-radius: 10px; }
 .file-upload-container > form { display: inline-block; background: white; height: 100%; width: 100%; }

+ 6 - 2
srv/src/httpServ.js

@@ -65,7 +65,7 @@ function redirectToSlackAuth(res) {
         Location: "https://slack.com/oauth/authorize"
         +"?client_id=" +config.clientId
         +"&scope=" +slackManager.getScope().join(",")
-        +"&redirect=" +config.rootUrl
+        +"&redirect_uri=" +config.rootUrl
     });
     res.end();
 }
@@ -78,7 +78,7 @@ Server.prototype.onRequest = function(req, res) {
 
     if (!req.session || !req.session.slackToken) {
         if (req.urlObj.queryTokens.code) {
-            Slack.getOauthToken(req.urlObj.queryTokens.code, (token) => {
+            Slack.getOauthToken(req.urlObj.queryTokens.code, config.rootUrl, (token) => {
                 if (token) {
                     req.session = sessionManager.lazyForRequest(req);
                     req.session.setSlackToken(req.reqT, token);
@@ -104,6 +104,7 @@ Server.prototype.onRequest = function(req, res) {
         sessionManager.saveSession(req.session);
         return; // async pipe will close when finished
     } else {
+        // Api / dynamic content
         var apiSuccess = false;
 
         res.slack = slackManager.lazyGet(req.session);
@@ -192,6 +193,9 @@ Server.prototype.onRequest = function(req, res) {
                 sessionManager.saveSession(req.session);
             });
         } else {
+            console.log(JSON.stringify(req.session));
+            console.log(JSON.stringify(req.urlObj));
+            console.log(JSON.stringify(req.cookies));
             res.writeHeader("404", "Not Found");
             res.end();
         }

+ 1 - 1
srv/src/session.js

@@ -5,7 +5,7 @@ function SessionManager() {
     this.sessions = Cache.create();
 }
 
-SessionManager.defaultTtl = 60 * 60 * 24;
+SessionManager.defaultTtl = 60 * 60 * 24; // keep sessions 24h
 
 function Session(reqT, sessId) {
     this.sessId = sessId;

+ 18 - 1
srv/src/slack.js

@@ -24,6 +24,16 @@ const SLACK_ENDPOINT = "https://slack.com/api/"
     ,HISTORY_LENGTH = 35
 ;
 
+var
+    SLACK_SESSIONS = []
+;
+
+setInterval(function() {
+    SLACK_SESSIONS.forEach(function(slackInst) {
+        slackInst.closeIfInnactive();
+    });
+}, 60000);
+
 function Slack(sess) {
     this.token = sess.slackToken;
     this.rtm = null;
@@ -154,6 +164,7 @@ Slack.prototype.onMessage = function(msg) {
         var histo = this.history[msg["channel"]];
         if (histo) {
             histo.push(msg);
+            histo.resort();
             this.data.liveV = Math.max(this.data.liveV, parseFloat(msg["ts"]));
         } else if (this.data.getChannel(msg["channel"])) {
             this.fetchHistory(msg["channel"]);
@@ -171,6 +182,7 @@ Slack.prototype.connectRtm = function(url, cb) {
         }
         _this.connected = true;
         _this.onMessage(JSON.parse(msg));
+        SLACK_SESSIONS.push(_this);
     });
     this.rtm.once("error", function(e) {
         _this.connected = false;
@@ -184,13 +196,18 @@ Slack.prototype.connectRtm = function(url, cb) {
     });
 };
 
+Slack.prototype.closeIfInnactive = function() {
+    //TODO
+};
+
 Slack.prototype.close = function() {
 };
 
-Slack.getOauthToken = function(code, cb) {
+Slack.getOauthToken = function(code, redirectUri, cb) {
     httpsRequest(SLACK_ENDPOINT+GETAPI.oauth
         +"?client_id=" +config.clientId
         +"&client_secret=" +config.clientSecret
+        +"&redirect_uri=" +redirectUri
         +"&code=" +code,
     (status, resp) => {
         if (status === 200 && resp.ok) {

+ 3 - 1
srv/src/url.js

@@ -42,7 +42,9 @@ function Url(url) {
                 ,stat: fs.statSync("public/" +this.urlParts[0])
             };
         }
-    } catch (e) {}
+    } catch (e) {
+        // Not a public/ file
+    }
 }
 
 Url.prototype.isPublic = function() {