1
0
Эх сурвалжийг харах

[add][wip] parse messages

B Thibault 8 жил өмнө
parent
commit
b4a26e73f5
2 өөрчлөгдсөн 288 нэмэгдсэн , 291 устгасан
  1. 277 282
      cli/msgFormatter.js
  2. 11 9
      srv/public/slack.min.js

+ 277 - 282
cli/msgFormatter.js

@@ -1,3 +1,5 @@
+"use strict";
+
 /**
  * replace all :emoji: codes with corresponding image
  * @param {string} inputString
@@ -19,339 +21,332 @@ function formatEmojis(inputString) {
 /** @type {function(string):string} */
 var formatText = (function() {
     /**
-     * @constructor
-     * @param {string} fullText
-     */
-    function MessagePart(fullText) {
-        /** @type {Array.<MessagePart>} */
-        this.subParts = [];
+     * @param {string} c
+     * @return {boolean}
+     **/
+    function isAlphadec(c) {
+        return ((c >= 'A' && c <= 'Z') ||
+            (c >= 'a' && c <= 'z') ||
+            (c >= '0' && c <= '9') ||
+            "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ".indexOf(c) !== -1);
+
+    }
 
+    /**
+     * @constructor
+     * @param {MsgBranch!} _parent
+    **/
+    function MsgTextLeaf(_parent) {
         /** @type {string} */
         this.text = "";
 
-        /** @type {string} */
-        this.fullText = fullText;
+        /** @const @type {MsgBranch} */
+        this._parent = _parent;
+    }
+
+    /**
+     * @constructor
+     * @param {MsgBranch|MsgTree} _parent
+     * @param {number} triggerIndex
+     * @param {string=} trigger
+     */
+    function MsgBranch(_parent, triggerIndex, trigger) {
+        /** @const @type {number} */
+        this.triggerIndex = triggerIndex;
+
+        /** @type {MsgBranch|MsgTextLeaf} */
+        this.lastNode = new MsgTextLeaf(this);
+
+        /** @type {Array<MsgBranch|MsgTextLeaf>} */
+        this.subNodes = [ this.lastNode ];
+
+        /** @const @type {string} */
+        this.trigger = trigger || '';
 
         /** @type {boolean} */
-        this.italic = false;
+        this.isLink = this.trigger === '<';
 
         /** @type {boolean} */
-        this.bold = false;
+        this.isBold = this.trigger === '*';
 
         /** @type {boolean} */
-        this.strike = false;
+        this.isItalic = this.trigger === '_';
 
         /** @type {boolean} */
-        this.code = false;
+        this.isStrike = this.trigger === '~' || this.trigger === '-';
 
         /** @type {boolean} */
-        this.longCode = false;
+        this.isQuote = this.trigger === '>';
 
         /** @type {boolean} */
-        this.quote = false;
-    }
+        this.isEmoji = this.trigger === ':';
 
-    var isAlphadec = function(c) {
-        return ((c >= 'A' && c <= 'Z') ||
-            (c >= 'a' && c <= 'z') ||
-            (c >= '0' && c <= '9') ||
-            "àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇߨøÅ寿œ".indexOf(c) !== -1);
-    }
-    ,checkEnd = function(str, pos, c) {
-        while (str[pos]) {
-            if (isAlphadec(str[pos]) && str[pos] != c && str[pos +1] == c) {
-                return true;
-            }
-            pos++;
-        }
-        return false;
+        /** @type {boolean} */
+        this.isCode = this.trigger === '`';
+
+        /** @type {boolean} */
+        this.isCodeBlock = this.trigger === '```';
+
+        /** @type {boolean} */
+        this.isEol = this.trigger === '\n';
+
+        /** @const @type {MsgBranch|MsgTree} */
+        this._parent = _parent;
+
+        /** @type {boolean} */
+        this.terminated = false;
     }
 
-    MessagePart.prototype.isFinished = function() {
-        var result = false;
-        if (this.longCode)
-            return this.text.substr(this.text.length -3) === '```';
-        if (this.code)
-            return this.text[this.text.length -1] === '`' && this.text.length > 1;
-        if (this.bold)
-            result |= this.text[this.text.length -1] === '*' && this.text.length > 1;
-        if (this.strike)
-            result |= this.text[this.text.length -1] === '~' && this.text.length > 1;
-        if (this.italic)
-            result |= this.text[this.text.length -1] === '_' && this.text.length > 1;
-        if (this.quote)
-            result |= this.text[this.text.length -1] === '\n';
-        return result || this.isOnlyText();
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsBold = function() {
+        return (this.isBold && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsBold());
+    };
+
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsItalic = function() {
+        return (this.isItalic && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsItalic());
+    };
+
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsStrike = function() {
+        return (this.isStrike && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsStrike());
+    };
+
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsQuote = function() {
+        return (this.isQuote && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsQuote());
+    };
+
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsEmoji = function() {
+        return (this.isEmoji && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsEmoji());
     };
 
-    MessagePart.prototype.addChar = function(c) {
-        this.text += c;
-        return this;
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsCode = function() {
+        return (this.isCode && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsCode());
     };
 
-    MessagePart.prototype.isOnlyText = function() {
-        return !this.italic &&
-            !this.bold &&
-            !this.strike &&
-            !this.code &&
-            !this.longCode &&
-            !this.quote;
+    /** @return {boolean} */
+    MsgBranch.prototype.checkIsCodeBlock = function() {
+        return (this.isCodeBlock && this.terminated) || (this._parent instanceof MsgBranch && this._parent.checkIsCodeBlock());
     };
 
     /**
-     * @return {MessagePart}
+     * Check if this token closes the branch
+     * FIXME it is possible that an inner branch still lives
+     * In this case, finishWith should return the new leave
+     * Exemple: `_text *bold_ bold continue*` should be
+     * + root
+     * | italic
+     *   | "text"
+     *   | bold
+     *     | "bold"
+     * | bold
+     *   | "bold continue"
+     * and this function will return a "bold" branch
+     *
+     * @param {string} str
+     * @param {number} i
+     * @return {MsgTextLeaf|MsgBranch|null}
     **/
-    MessagePart.prototype.finalize = function() {
-        if (this.longCode)
-            this.text = this.text.substr(0, this.text.length -3);
-        else if (this.code)
-            this.text = this.text.substr(0, this.text.length -1);
-        else if (this.bold)
-            this.text = this.text.substr(0, this.text.length -1);
-        else if (this.strike)
-            this.text = this.text.substr(0, this.text.length -1);
-        else if (this.italic)
-            this.text = this.text.substr(0, this.text.length -1);
-        else if (this.quote)
-            this.text = this.text.substr(0, this.text.length -1);
-        return this;
+    MsgBranch.prototype.finishWith = function(str, i) {
+        if (this.trigger === '<' && str[i] === '>') {
+            return new MsgTextLeaf(/** @type {MsgBranch!} */ (this._parent));
+        }
+        if (str.substr(i, this.trigger.length) === this.trigger) {
+            return new MsgTextLeaf(/** @type {MsgBranch!} */ (this._parent));
+        }
+        // FIXME
+        // check if branch terminates here
+        // If yes return a branche containing all unfinished children
+        // If yes but no children, return a new MsgTextLeaf
+        // If not return null
+        return null;
     };
 
-    MessagePart.prototype.cloneType = function() {
-        var clone = new MessagePart(this.fullText);
-        clone.italic = this.italic;
-        clone.bold = this.bold;
-        clone.strike = this.strike;
-        clone.code = this.code;
-        clone.longCode = this.longCode;
-        clone.quote = this.quote;
-        return clone;
+    /**
+     * Check if this token is compatible with this branch
+     * @param {string} str
+     * @param {number} i
+     * @return {boolean}
+    **/
+    MsgBranch.prototype.isAcceptable = function(str, i) {
+        if (this.isEmoji && (str[i] === ' ' || str[i] === '\t'))
+            return false;
+        return true;
     };
 
     /**
-     * @param {boolean=} skipFirst
+     * Check if str[i] is a trigger for a new node
+     * if true, return the trigger
+     * @param {string} str
+     * @param {number} i
+     * @return {string|null}
     **/
-    MessagePart.prototype.formatText = function(skipFirst) {
-        var lastChild = new MessagePart(this.fullText);
-
-        for (var i =skipFirst === true ? 1 : 0, textLength =this.fullText.length; i < textLength; i++) {
-            //First, check if we have a ``` code block
-            if (!lastChild.longCode && this.fullText.substr(i, 3) === '```') {
-                this.subParts.push(lastChild.finalize());
-                lastChild = new MessagePart(this.fullText.substr(i));
-                lastChild.longCode = true;
-                i += 2;
-            } else if (!lastChild.longCode && !lastChild.code && this.fullText[i] === '`') {
-                this.subParts.push(lastChild.finalize());
-                lastChild = new MessagePart(this.fullText.substr(i));
-                lastChild.code = true;
-            } else if (!lastChild.longCode && !lastChild.italic && this.fullText[i] === '_') {
-                this.subParts.push(lastChild.finalize());
-                lastChild = new MessagePart(this.fullText.substr(i));
-                lastChild.italic = true;
-            } else if (!lastChild.longCode && !lastChild.bold && this.fullText[i] === '*') {
-                this.subParts.push(lastChild.finalize());
-                lastChild = new MessagePart(this.fullText.substr(i));
-                lastChild.bold = true;
-            } else if (!lastChild.longCode && !lastChild.strike && this.fullText[i] === '~') {
-                this.subParts.push(lastChild.finalize());
-                lastChild = new MessagePart(this.fullText.substr(i));
-                lastChild.strike = true;
-            } else {
-                lastChild.addChar(this.fullText[i]);
-                if (!lastChild.isOnlyText() && lastChild.isFinished()) {
-                    this.subParts.push(lastChild.finalize());
-                    lastChild = new MessagePart(this.fullText.substr(i +1));
-                }
-            }
+    MsgBranch.prototype.isNewToken = function(str, i) {
+        if (this.lastNode instanceof MsgTextLeaf) {
+            if (str.substr(i, 3) === '```')
+                return '```';
+            if ('`' === str[i])
+                return str[i];
+            if (['*', '~', '-', '_' ].indexOf(str[i]) !== -1 && (isAlphadec(str[i +1]) || ['*', '`', '~', '-', '_', ':', '<'].indexOf(str[i+1]) !== -1))
+                return str[i];
+            if ([':', '<'].indexOf(str[i]) !== -1 && isAlphadec(str[i +1]))
+                return str[i];
         }
-        if (lastChild.isFinished()) {
-            this.subParts.push(lastChild.finalize());
+        return null;
+    };
+
+    /** @return {number} */
+    MsgTextLeaf.prototype.addChar = function(str, i) {
+        this.text += str[i];
+        return 1;
+    };
+
+    /**
+     * Parse next char
+     * @param {string} str
+     * @param {number} i
+     * @return {number}
+    **/
+    MsgBranch.prototype.addChar = function(str, i) {
+        var isFinished = this.lastNode.finishWith ? this.lastNode.finishWith(str, i) : null;
+
+        if (isFinished) {
+            // terminated token, move pointer to a new leaf
+            this.lastNode.terminated = true;
+            this.lastNode = isFinished;
+            this.subNodes.push(isFinished);
+            return 1;
         } else {
-            // Unterminated sequence
-            var textChild = new MessagePart(lastChild.fullText[0]);
-            textChild.text = lastChild.fullText[0];
-            this.subParts.push(textChild.finalize());
-            lastChild.formatText(true);
-            this.subParts.push(lastChild);
-        }
-        /*
-        var _msgContent = ""
-            ,currentMods = {}
-            ,quote = false
-            ,i =0
-
-        var msgContent = this.text.replace(new RegExp('<([@#]?)([^>]*)>', 'g'),
-            function(matched, type, entity) {
-                var sub = entity.split('|');
-
-                if (type === '@') {
-                    if (!sub[1]) {
-                        var user = SLACK.context.users[sub[0]];
-                        sub[1] = user ? ('@' +user.name) : locale.unknownMember;
-                    } 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.channels[sub[0]];
-                        sub[1] = chan ? ('#' +chan.name) : locale.unknownChannel;
-                    } 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;
+            if (this.lastNode instanceof MsgTextLeaf || this.lastNode.isAcceptable(str, i)) {
+                var isNewToken = this.isNewToken(str, i);
+                if (isNewToken) {
+                    this.lastNode = new MsgBranch(this, i, isNewToken);
+                    this.subNodes.push(this.lastNode);
+                    return this.lastNode.trigger.length;
                 } else {
-                    return matched;
+                    return this.lastNode.addChar(str, i);
                 }
-                return '<a href="' +sub[0] +'" class="' +sub[2] +'"' +(!type ? ' target="_blank"' : '') +'>' +sub[1] +'</a>';
-            });
-        msgContent = formatEmojis(msgContent);
-        var msgLength = msgContent.length,
-            appendMod = function(mods) {
-                if (!Object.keys(currentMods).length)
-                    return "";
-                return '<span class="' +Object.keys(mods).join(' ') +'">';
-            };
-
-        // Skip trailing
-        while (i < msgLength && (msgContent[i] === ' ' || msgContent[i] === '\t'))
-            i++;
-        if (msgContent.substr(i, 4) === '&gt;') {
-            quote = true;
-            i += 4;
-        }
-        for (; i < msgLength; i++) {
-            var c = msgContent[i];
-
-            if (c === '<') {
-                do {
-                    _msgContent += msgContent[i++];
-                } while (msgContent[i -1] !== '>' && msgContent[i]);
-                i--;
-                continue;
-            }
-            if (!(currentMods[R.klass.msg.style.bold]) && c === '*' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.bold] = true;
-                _msgContent += appendMod(currentMods);
-            } else if (!(currentMods[R.klass.msg.style.strike]) && c === '~' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.strike] = true;
-                _msgContent += appendMod(currentMods);
-            } else if (!(currentMods[R.klass.msg.style.code]) && c === '`' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.code] = true;
-                _msgContent += appendMod(currentMods);
-            } else if (!(currentMods[R.klass.msg.style.italic]) && c === '_' && msgContent[i +1] && checkEnd(msgContent, i, c)) {
-                if (Object.keys(currentMods).length)
-                    _msgContent += '</span>';
-                currentMods[R.klass.msg.style.italic] = true;
-                _msgContent += appendMod(currentMods);
             } else {
-                var finalFound = false;
-                _msgContent += c;
-
-                do {
-                    if ((currentMods[R.klass.msg.style.bold]) && c !== '*' && msgContent[i +1] === '*') {
-                        delete currentMods[R.klass.msg.style.bold];
-                        finalFound = true;
-                    } else if ((currentMods[R.klass.msg.style.strike]) && c !== '~' && msgContent[i +1] === '~') {
-                        delete currentMods[R.klass.msg.style.strike];
-                        finalFound = true;
-                    } else if ((currentMods[R.klass.msg.style.code]) && c !== '`' && msgContent[i +1] === '`') {
-                        delete currentMods[R.klass.msg.style.code];
-                        finalFound = true;
-                    } else if ((currentMods[R.klass.msg.style.italic]) && c !== '_' && msgContent[i +1] === '_') {
-                        delete currentMods[R.klass.msg.style.italic];
-                        finalFound = true;
-                    } else {
-                        break;
-                    }
-                    c = msgContent[++i];
-                } while (i < msgLength);
-                if (finalFound)
-                    _msgContent += '</span>' +appendMod(currentMods);
+                // last branch child is not compatible with this token.
+                // So, lastBranch is not a branch
+                // Add a new "escaped" node to replace trigger
+                var textNode = new MsgTextLeaf(this);
+                textNode.addChar(str, this.lastNode.trigger);
+                this.subNodes.pop();
+                this.subNodes.push(textNode);
+
+                // new root
+                var newBranch = new MsgBranch(this, this.lastNode.triggerIndex +1);
+                for (var charIndex = this.lastNode.triggerIndex +1; charIndex <= i;)
+                    charIndex += newBranch.addChar(str, charIndex);
+                this.lastNode = this.subNodes[this.subNodes.length -1];
+                return 1;
             }
         }
-        if (!isObjectEmpty(currentMods)) {
-            // Should not append
-            console.warn("formatter warning");
-            _msgContent += '</span>';
-        }
-        /*
-        if (quote)
-            msgContents[msgContentIndex] = '<span class="' +R.klass.msg.style.quote +'">' +_msgContent +'</span>';
-        else
-            msgContents[msgContentIndex] = _msgContent;
-        */
     };
 
-    MessagePart.prototype.getOuterClass = function() {
-        var classList = '';
-
-        if (this.longCode) {
-            classList += ' ' +R.klass.msg.style.longCode;
-        }
-        if (this.bold) {
-            classList += ' ' +R.klass.msg.style.bold;
-        }
-        if (this.code) {
-            classList += ' ' +R.klass.msg.style.code;
-        }
-        if (this.italic) {
-            classList += ' ' +R.klass.msg.style.italic;
-        }
-        if (this.strike) {
-            classList += ' ' +R.klass.msg.style.strike;
-        }
-        if (this.quote) {
-            classList += ' ' +R.klass.msg.style.quote;
-        }
-        return classList;
+    /**
+     * @return {boolean} true if still contains stuff
+    **/
+    MsgBranch.prototype.prune = function() {
+        var branches = [];
+        this.subNodes.forEach(function(i) {
+            if (i instanceof MsgTextLeaf) {
+                if (i.text !== '')
+                    branches.push(i);
+            } else if (i.prune()) {
+                branches.push(i);
+            }
+        });
+        this.subNodes = branches;
+        this.lastNode = branches[branches.length -1];
+        return !!this.subNodes.length;
     };
 
-    /** @return {string} */
-    MessagePart.prototype.getInnerHTML = function() {
-        return this.text
-            .replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '&nbsp;') // replace trailing spaces par non-secable spaces
-            .replace(/\n/g, '<br/>');
+    /**
+     * @return {string}
+    **/
+    MsgTextLeaf.prototype.innerHTML = function() {
+        return this.text;
     };
 
-    /** @return {string} */
-    MessagePart.prototype.toHTML = function() {
-        if (this.subParts.length) {
-            var result = "";
-            this.subParts.forEach(function(part) {
-                result += part.toHTML();
-            });
-            return result;
+    /**
+     * @return {string}
+    **/
+    MsgTextLeaf.prototype.outerHTML = function() {
+        var tagName = 'span'
+            ,classList = [];
+
+        if (this._parent.checkIsCodeBlock()) {
+            classList.push('codeblock');
+        } else if (this._parent.checkIsCode()) {
+            classList.push('code');
         } else {
-            if (this.isOnlyText() && !this.text.length)
-                return "";
-            return '<span class="' +this.getOuterClass() +'">' +this.getInnerHTML() +'</span>';
+            if (this.isLink)
+                tagName = 'a';
+            if (this._parent.checkIsBold())
+                classList.push('bold');
+            if (this._parent.checkIsItalic())
+                classList.push('italic');
+            if (this._parent.checkIsStrike())
+                classList.push('strike');
+            if (this._parent.checkIsQuote()) // FIXME nope.
+                classList.push('quote');
+            if (this._parent.checkIsEmoji())
+                classList.push('emoji');
         }
+        return '<' +tagName +(classList.length ? ' class="' +classList.join(' ') +'"' : '') +'>' +this.innerHTML() +'</' +tagName +'>';
+    };
+
+    MsgBranch.prototype.outerHTML = function() {
+        var html = "";
+
+        this.subNodes.forEach(function(node) {
+            html += node.outerHTML();
+        });
+        return html;
+    };
+
+    MsgBranch.prototype.eof = function() {
+        // FIXME check for any unterminated branches to reinterpret them
     };
 
-    return function (fullText) {
-        // trivial empty string
-        var text = (fullText || "").trim();
-        if (text == "")
-            return "";
+    /**
+     * @constructor
+     * @param {string} text
+     */
+    function MsgTree(text) {
+        /** @const @type {string} */
+        this.text = text;
 
-        var msgContent = new MessagePart(text);
-        msgContent.formatText();
-        console.log(msgContent);
-        return msgContent.toHTML();
+        /** @type {MsgBranch|null} */
+        this.root = null;
     }
+
+    MsgTree.prototype.parse = function() {
+        this.root = new MsgBranch(this, 0);
+        for (var i =0, textLen = this.text.length; i < textLen;)
+            i += this.root.addChar(this.text, i);
+        this.root.eof();
+        if (!this.root.prune()) {
+            this.root = null;
+        }
+    };
+
+    /**
+     * @return {string}
+    **/
+    MsgTree.prototype.toHTML = function() {
+        return this.root ? this.root.outerHTML() : "";
+    };
+
+    return function(text) {
+        var root = new MsgTree(text);
+        root.parse();
+        return (root.toHTML());
+    };
 })();
 

+ 11 - 9
srv/public/slack.min.js

@@ -45,15 +45,17 @@ meMessage:"slackmsg-me_message",ts:"slackmsg-ts",author:"slackmsg-author",author
 link:"slackmsg-link",linkuser:"slackmsg-link-user",linkchan:"slackmsg-link-chan",attachment:{container:"slackmsg-attachment",list:"slackmsg-attachments",hasThumb:"has-thumb",pretext:"slackmsg-attachment-pretext",block:"slackmsg-attachment-block",title:"slackmsg-attachment-title",content:"slackmsg-attachment-content",text:"slackmsg-attachment-text",thumbImg:"slackmsg-attachment-thumb",img:"slackmsg-attachment-img",actions:"slackmsg-attachment-actions",actionItem:"slackmsg-attachment-actions-item",
 field:{container:"slackmsg-attachment-fields",item:"field",title:"field-title",text:"field-text",longField:"field-long"},footer:"slackmsg-attachment-footer",footerText:"slackmsg-attachment-footer-text",footerIcon:"slackmsg-attachment-footer-icon"},reactions:{container:"slackmsg-reactions",item:"slackmsg-reaction-item"},style:{bold:"slackmsg-style-bold",code:"slackmsg-style-code",longCode:"slackmsg-style-longcode",italic:"slackmsg-style-italic",strike:"slackmsg-style-strike",quote:"slackmsg-style-quote"}},
 dialog:{container:"dialog",overlay:"dialog-overlay",title:"dialog-title",titleLabel:"dialog-title-label",closeButton:"dialog-title-close",body:"dialog-body",buttonBar:"dialog-footer"}}};function formatEmojis(inputString){return inputString.replace(/:([^ \t:]+):/g,function(returnFailed,emoji){var emojiDom=makeEmojiDom(emoji);if(emojiDom){var domParent=document.createElement("span");domParent.className=returnFailed===inputString?R.klass.emoji.medium:R.klass.emoji.small;domParent.appendChild(emojiDom);return domParent.outerHTML}return returnFailed})}
-var formatText=function(){function MessagePart(fullText){this.subParts=[];this.text="";this.fullText=fullText;this.italic=false;this.bold=false;this.strike=false;this.code=false;this.longCode=false;this.quote=false}var isAlphadec=function(c){return c>="A"&&c<="Z"||c>="a"&&c<="z"||c>="0"&&c<="9"||"\u00e0\u00e8\u00ec\u00f2\u00f9\u00c0\u00c8\u00cc\u00d2\u00d9\u00e1\u00e9\u00ed\u00f3\u00fa\u00fd\u00c1\u00c9\u00cd\u00d3\u00da\u00dd\u00e2\u00ea\u00ee\u00f4\u00fb\u00c2\u00ca\u00ce\u00d4\u00db\u00e3\u00f1\u00f5\u00c3\u00d1\u00d5\u00e4\u00eb\u00ef\u00f6\u00fc\u00ff\u00c4\u00cb\u00cf\u00d6\u00dc\u0178\u00e7\u00c7\u00df\u00d8\u00f8\u00c5\u00e5\u00c6\u00e6\u0153".indexOf(c)!==
--1},checkEnd=function(str,pos,c){while(str[pos]){if(isAlphadec(str[pos])&&str[pos]!=c&&str[pos+1]==c)return true;pos++}return false};MessagePart.prototype.isFinished=function(){var result=false;if(this.longCode)return this.text.substr(this.text.length-3)==="```";if(this.code)return this.text[this.text.length-1]==="`"&&this.text.length>1;if(this.bold)result|=this.text[this.text.length-1]==="*"&&this.text.length>1;if(this.strike)result|=this.text[this.text.length-1]==="~"&&this.text.length>1;if(this.italic)result|=
-this.text[this.text.length-1]==="_"&&this.text.length>1;if(this.quote)result|=this.text[this.text.length-1]==="\n";return result||this.isOnlyText()};MessagePart.prototype.addChar=function(c){this.text+=c;return this};MessagePart.prototype.isOnlyText=function(){return!this.italic&&!this.bold&&!this.strike&&!this.code&&!this.longCode&&!this.quote};MessagePart.prototype.finalize=function(){if(this.longCode)this.text=this.text.substr(0,this.text.length-3);else if(this.code)this.text=this.text.substr(0,
-this.text.length-1);else if(this.bold)this.text=this.text.substr(0,this.text.length-1);else if(this.strike)this.text=this.text.substr(0,this.text.length-1);else if(this.italic)this.text=this.text.substr(0,this.text.length-1);else if(this.quote)this.text=this.text.substr(0,this.text.length-1);return this};MessagePart.prototype.cloneType=function(){var clone=new MessagePart(this.fullText);clone.italic=this.italic;clone.bold=this.bold;clone.strike=this.strike;clone.code=this.code;clone.longCode=this.longCode;
-clone.quote=this.quote;return clone};MessagePart.prototype.formatText=function(skipFirst){var lastChild=new MessagePart(this.fullText);for(var i=skipFirst===true?1:0,textLength=this.fullText.length;i<textLength;i++)if(!lastChild.longCode&&this.fullText.substr(i,3)==="```"){this.subParts.push(lastChild.finalize());lastChild=new MessagePart(this.fullText.substr(i));lastChild.longCode=true;i+=2}else if(!lastChild.longCode&&!lastChild.code&&this.fullText[i]==="`"){this.subParts.push(lastChild.finalize());
-lastChild=new MessagePart(this.fullText.substr(i));lastChild.code=true}else if(!lastChild.longCode&&!lastChild.italic&&this.fullText[i]==="_"){this.subParts.push(lastChild.finalize());lastChild=new MessagePart(this.fullText.substr(i));lastChild.italic=true}else if(!lastChild.longCode&&!lastChild.bold&&this.fullText[i]==="*"){this.subParts.push(lastChild.finalize());lastChild=new MessagePart(this.fullText.substr(i));lastChild.bold=true}else if(!lastChild.longCode&&!lastChild.strike&&this.fullText[i]===
-"~"){this.subParts.push(lastChild.finalize());lastChild=new MessagePart(this.fullText.substr(i));lastChild.strike=true}else{lastChild.addChar(this.fullText[i]);if(!lastChild.isOnlyText()&&lastChild.isFinished()){this.subParts.push(lastChild.finalize());lastChild=new MessagePart(this.fullText.substr(i+1))}}if(lastChild.isFinished())this.subParts.push(lastChild.finalize());else{var textChild=new MessagePart(lastChild.fullText[0]);textChild.text=lastChild.fullText[0];this.subParts.push(textChild.finalize());
-lastChild.formatText(true);this.subParts.push(lastChild)}};MessagePart.prototype.getOuterClass=function(){var classList="";if(this.longCode)classList+=" "+R.klass.msg.style.longCode;if(this.bold)classList+=" "+R.klass.msg.style.bold;if(this.code)classList+=" "+R.klass.msg.style.code;if(this.italic)classList+=" "+R.klass.msg.style.italic;if(this.strike)classList+=" "+R.klass.msg.style.strike;if(this.quote)classList+=" "+R.klass.msg.style.quote;return classList};MessagePart.prototype.getInnerHTML=function(){return this.text.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-"&nbsp;").replace(/\n/g,"<br/>")};MessagePart.prototype.toHTML=function(){if(this.subParts.length){var result="";this.subParts.forEach(function(part){result+=part.toHTML()});return result}else{if(this.isOnlyText()&&!this.text.length)return"";return'<span class="'+this.getOuterClass()+'">'+this.getInnerHTML()+"</span>"}};return function(fullText){var text=(fullText||"").trim();if(text=="")return"";var msgContent=new MessagePart(text);msgContent.formatText();console.log(msgContent);return msgContent.toHTML()}}();var NOTIFICATION_COOLDOWN=30*1E3,NOTIFICATION_DELAY=5*1E3,MSG_GROUPS=[],lastNotificationSpawn=0;
+var formatText=function(){function isAlphadec(c){return c>="A"&&c<="Z"||c>="a"&&c<="z"||c>="0"&&c<="9"||"\u00e0\u00e8\u00ec\u00f2\u00f9\u00c0\u00c8\u00cc\u00d2\u00d9\u00e1\u00e9\u00ed\u00f3\u00fa\u00fd\u00c1\u00c9\u00cd\u00d3\u00da\u00dd\u00e2\u00ea\u00ee\u00f4\u00fb\u00c2\u00ca\u00ce\u00d4\u00db\u00e3\u00f1\u00f5\u00c3\u00d1\u00d5\u00e4\u00eb\u00ef\u00f6\u00fc\u00ff\u00c4\u00cb\u00cf\u00d6\u00dc\u0178\u00e7\u00c7\u00df\u00d8\u00f8\u00c5\u00e5\u00c6\u00e6\u0153".indexOf(c)!==-1}function MsgTextLeaf(_parent){this.text=
+"";this._parent=_parent}function MsgBranch(_parent,triggerIndex,trigger){this.triggerIndex=triggerIndex;this.lastNode=new MsgTextLeaf(this);this.subNodes=[this.lastNode];this.trigger=trigger||"";this.isLink=this.trigger==="<";this.isBold=this.trigger==="*";this.isItalic=this.trigger==="_";this.isStrike=this.trigger==="~"||this.trigger==="-";this.isQuote=this.trigger===">";this.isEmoji=this.trigger===":";this.isCode=this.trigger==="`";this.isCodeBlock=this.trigger==="```";this.isEol=this.trigger===
+"\n";this._parent=_parent;this.terminated=false}MsgBranch.prototype.checkIsBold=function(){return this.isBold&&this.terminated||this._parent instanceof MsgBranch&&this._parent.checkIsBold()};MsgBranch.prototype.checkIsItalic=function(){return this.isItalic&&this.terminated||this._parent instanceof MsgBranch&&this._parent.checkIsItalic()};MsgBranch.prototype.checkIsStrike=function(){return this.isStrike&&this.terminated||this._parent instanceof MsgBranch&&this._parent.checkIsStrike()};MsgBranch.prototype.checkIsQuote=
+function(){return this.isQuote&&this.terminated||this._parent instanceof MsgBranch&&this._parent.checkIsQuote()};MsgBranch.prototype.checkIsEmoji=function(){return this.isEmoji&&this.terminated||this._parent instanceof MsgBranch&&this._parent.checkIsEmoji()};MsgBranch.prototype.checkIsCode=function(){return this.isCode&&this.terminated||this._parent instanceof MsgBranch&&this._parent.checkIsCode()};MsgBranch.prototype.checkIsCodeBlock=function(){return this.isCodeBlock&&this.terminated||this._parent instanceof
+MsgBranch&&this._parent.checkIsCodeBlock()};MsgBranch.prototype.finishWith=function(str,i){if(this.trigger==="<"&&str[i]===">")return new MsgTextLeaf(this._parent);if(str.substr(i,this.trigger.length)===this.trigger)return new MsgTextLeaf(this._parent);return null};MsgBranch.prototype.isAcceptable=function(str,i){if(this.isEmoji&&(str[i]===" "||str[i]==="\t"))return false;return true};MsgBranch.prototype.isNewToken=function(str,i){if(this.lastNode instanceof MsgTextLeaf){if(str.substr(i,3)==="```")return"```";
+if("`"===str[i])return str[i];if(["*","~","-","_"].indexOf(str[i])!==-1&&(isAlphadec(str[i+1])||["*","`","~","-","_",":","<"].indexOf(str[i+1])!==-1))return str[i];if([":","<"].indexOf(str[i])!==-1&&isAlphadec(str[i+1]))return str[i]}return null};MsgTextLeaf.prototype.addChar=function(str,i){this.text+=str[i];return 1};MsgBranch.prototype.addChar=function(str,i){var isFinished=this.lastNode.finishWith?this.lastNode.finishWith(str,i):null;if(isFinished){this.lastNode.terminated=true;this.lastNode=
+isFinished;this.subNodes.push(isFinished);return 1}else if(this.lastNode instanceof MsgTextLeaf||this.lastNode.isAcceptable(str,i)){var isNewToken=this.isNewToken(str,i);if(isNewToken){this.lastNode=new MsgBranch(this,i,isNewToken);this.subNodes.push(this.lastNode);return this.lastNode.trigger.length}else return this.lastNode.addChar(str,i)}else{var textNode=new MsgTextLeaf(this);textNode.addChar(str,this.lastNode.trigger);this.subNodes.pop();this.subNodes.push(textNode);var newBranch=new MsgBranch(this,
+this.lastNode.triggerIndex+1);for(var charIndex=this.lastNode.triggerIndex+1;charIndex<=i;)charIndex+=newBranch.addChar(str,charIndex);this.lastNode=this.subNodes[this.subNodes.length-1];return 1}};MsgBranch.prototype.prune=function(){var branches=[];this.subNodes.forEach(function(i){if(i instanceof MsgTextLeaf){if(i.text!=="")branches.push(i)}else if(i.prune())branches.push(i)});this.subNodes=branches;this.lastNode=branches[branches.length-1];return!!this.subNodes.length};MsgTextLeaf.prototype.innerHTML=
+function(){return this.text};MsgTextLeaf.prototype.outerHTML=function(){var tagName="span",classList=[];if(this._parent.checkIsCodeBlock())classList.push("codeblock");else if(this._parent.checkIsCode())classList.push("code");else{if(this.isLink)tagName="a";if(this._parent.checkIsBold())classList.push("bold");if(this._parent.checkIsItalic())classList.push("italic");if(this._parent.checkIsStrike())classList.push("strike");if(this._parent.checkIsQuote())classList.push("quote");if(this._parent.checkIsEmoji())classList.push("emoji")}return"<"+
+tagName+(classList.length?' class="'+classList.join(" ")+'"':"")+">"+this.innerHTML()+"</"+tagName+">"};MsgBranch.prototype.outerHTML=function(){var html="";this.subNodes.forEach(function(node){html+=node.outerHTML()});return html};MsgBranch.prototype.eof=function(){};function MsgTree(text){this.text=text;this.root=null}MsgTree.prototype.parse=function(){this.root=new MsgBranch(this,0);for(var i=0,textLen=this.text.length;i<textLen;)i+=this.root.addChar(this.text,i);this.root.eof();if(!this.root.prune())this.root=
+null};MsgTree.prototype.toHTML=function(){return this.root?this.root.outerHTML():""};return function(text){var root=new MsgTree(text);root.parse();return root.toHTML()}}();var NOTIFICATION_COOLDOWN=30*1E3,NOTIFICATION_DELAY=5*1E3,MSG_GROUPS=[],lastNotificationSpawn=0;
 function onContextUpdated(){var chanListFram=document.createDocumentFragment(),sortedChans=Object.keys(SLACK.context.channels||{}),starred=[],channels=[],privs=[],priv=[];sortedChans.sort(function(a,b){if(a[0]!==b[0])return a[0]-b[0];return SLACK.context.channels[a].name.localeCompare(SLACK.context.channels[b].name)});sortedChans.forEach(function(chanId){var chan=SLACK.context.channels[chanId];if(!chan.archived&&chan.isMember!==false)if(chan instanceof PrivateMessageRoom){if(!chan.user.deleted){var chanListItem=
 createImsListItem(chan);if(chanListItem)if(chan.starred)starred.push(chanListItem);else priv.push(chanListItem)}}else{var chanListItem=createChanListItem(chan);if(chanListItem)if(chan.starred)starred.push(chanListItem);else if(chan.isPrivate)privs.push(chanListItem);else channels.push(chanListItem)}});if(starred.length)chanListFram.appendChild(createChanListHeader(locale.starred));starred.forEach(function(dom){chanListFram.appendChild(dom)});if(channels.length)chanListFram.appendChild(createChanListHeader(locale.channels));
 channels.forEach(function(dom){chanListFram.appendChild(dom)});privs.forEach(function(dom){chanListFram.appendChild(dom)});if(priv.length)chanListFram.appendChild(createChanListHeader(locale.privateMessageRoom));priv.forEach(function(dom){chanListFram.appendChild(dom)});document.getElementById(R.id.chanList).textContent="";document.getElementById(R.id.chanList).appendChild(chanListFram);setRoomFromHashBang();updateTitle();createContextBackground(function(imgData){document.getElementById(R.id.context).style.backgroundImage=