From d293bc0b799a679cd27ed4ef6e818b0ca96998d9 Mon Sep 17 00:00:00 2001
From: Reed Garmsen <rgarmsen2295@gmail.com>
Date: Wed, 8 Jul 2015 11:34:34 -0700
Subject: Implemented basic text formatting package using a modfied version of
 the marked js library.  Supports *bold*, _italics_, and `code`

---
 web/react/utils/utils.jsx | 153 ++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 126 insertions(+), 27 deletions(-)

(limited to 'web/react/utils/utils.jsx')

diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 2214b6239..d4654d7d5 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -9,6 +9,7 @@ var ActionTypes = Constants.ActionTypes;
 var AsyncClient = require('./async_client.jsx');
 var client = require('./client.jsx');
 var Autolinker = require('autolinker');
+var formatText = require('../../static/js/marked/lib/marked.js');
 
 module.exports.isEmail = function(email) {
   var regex = /^([a-zA-Z0-9_.+-])+\@(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
@@ -95,9 +96,8 @@ module.exports.getCookie = function(name) {
   if (parts.length == 2) return parts.pop().split(";").shift();
 }
 
-
 module.exports.notifyMe = function(title, body, channel) {
-  if ("Notification" in window && Notification.permission !== 'denied') {
+    if ("Notification" in window && Notification.permission !== 'denied') {
     Notification.requestPermission(function (permission) {
       if (Notification.permission !== permission) {
           Notification.permission = permission;
@@ -385,11 +385,53 @@ module.exports.searchForTerm = function(term) {
     });
 }
 
+/* Options:
+      - disable: Parses out *'s and other format specifiers in the text, but doesn't convert to html
+*/
+module.exports.customMarkedRenderer = function(options) {
+    var customTextRenderer = new formatText.Renderer();
+    if (options && options.disable) {
+        customTextRenderer.paragraph = function(text) {
+            return text + ' ';
+        };
+        customTextRenderer.strong = function(text) {
+            return text;
+        };
+        customTextRenderer.em = function(text) {
+            return text;
+        };
+        customTextRenderer.codespan = function(code) {
+            return code;
+        };
+        customTextRenderer.link = function(href, title, text) {
+            return href;
+        };
+        customTextRenderer.image = function(href, title, text) {
+            return href;
+        };
+    } else {
+        customTextRenderer.link = function(href, title, text) {
+            return href;
+        };
+        customTextRenderer.image = function(href, title, text) {
+            return href;
+        };
+    }
+    return customTextRenderer;
+};
+
 var oldExplicitMentionRegex = /(?:<mention>)([\s\S]*?)(?:<\/mention>)/g;
-var puncStartRegex = /^((?![@#])\W)+/g;
-var puncEndRegex = /(\W)+$/g;
+var puncStartRegex = /^((?![@#])[^A-Za-z0-9_<>])+/g;
+var puncEndRegex = /([^A-Za-z0-9_<>])+$/g;
+var startTagRegex = /(<\s*\w.*?>)+/g;
+var endTagRegex = /(<\s*\/\s*\w\s*.*?>|<\s*br\s*>)+/g;
 
 module.exports.textToJsx = function(text, options) {
+    var useTextFormatting = config.AllowTextFormatting && (!options || !options.noTextFormatting);
+
+    if (useTextFormatting) {
+        text = formatText(text, {sanitize: true, mangle: false, gfm: true, breaks: true, tables: false, smartypants: true, renderer: module.exports.customMarkedRenderer()});
+    }
 
     if (options && options['singleline']) {
         var repRegex = new RegExp("\n", "g");
@@ -398,7 +440,7 @@ module.exports.textToJsx = function(text, options) {
 
     var searchTerm = ""
     if (options && options['searchTerm']) {
-        searchTerm = options['searchTerm'].toLowerCase()
+        searchTerm = options['searchTerm'].toLowerCase();
     }
 
     var mentionClass = "mention-highlight";
@@ -407,6 +449,8 @@ module.exports.textToJsx = function(text, options) {
     }
 
     var inner = [];
+    var codeFlag = false;
+    var codeString = '';
 
     // Function specific regex
     var hashRegex = /^href="#[^"]+"|(#[A-Za-z]+[A-Za-z0-9_\-]*[A-Za-z0-9])$/g;
@@ -420,16 +464,45 @@ module.exports.textToJsx = function(text, options) {
         var highlightSearchClass = "";
         for (var z = 0; z < words.length; z++) {
             var word = words[z];
-            var trimWord = word.replace(puncStartRegex, '').replace(puncEndRegex, '').trim();
+            var trimWord;
+            if (useTextFormatting) {
+                trimWord = word.replace(endTagRegex, '').replace(startTagRegex, '').replace(puncStartRegex, '').replace(puncEndRegex, '').trim();
+            } else {
+                trimWord = word.replace(puncStartRegex, '').replace(puncEndRegex, '').trim();
+            }
             var mentionRegex = /^(?:@)([a-z0-9_]+)$/gi; // looks loop invariant but a weird JS bug needs it to be redefined here
             var explicitMention = mentionRegex.exec(trimWord);
 
-            if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm != "") {
+            var prefix;
+            var suffix;
+            var prefixSpan;
+            var suffixSpan;
+            if (useTextFormatting) {
+                prefix = (word.match(startTagRegex) ? word.match(startTagRegex) : "") + (word.replace(startTagRegex, '').match(puncStartRegex) ? word.replace(startTagRegex, '').match(puncStartRegex) : "");
+                suffix = (word.match(endTagRegex) ? word.match(endTagRegex) : "") + (word.replace(endTagRegex, '').match(puncEndRegex) ? word.replace(endTagRegex, '').match(puncEndRegex) : "");
+                prefixSpan = prefix ? (<span key={word+i+z+"pre_span"}><span dangerouslySetInnerHTML={{__html: prefix}} /></span>) : null;
+                suffixSpan = suffix ? (<span key={word+i+z+"suf_span"}><span dangerouslySetInnerHTML={{__html: suffix}} /> </span>) : (<span key={word+i+z+"suf_span"}> </span>);
+            } else {
+                prefix = word.match(puncStartRegex);
+                suffix = word.match(puncEndRegex);
+                prefixSpan = prefix ? (<span key={word+i+z+"pre_span"}>{prefix}</span>) : null;
+                suffixSpan = suffix ? (<span key={word+i+z+"suf_span"}>{suffix} </span>) : (<span key={word+i+z+"suf_span"}> </span>);
+            }
 
+            if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm != "") {
                 highlightSearchClass = " search-highlight";
             }
 
-            if (explicitMention &&
+            if (useTextFormatting && (codeFlag || word.indexOf('<code>') !== -1)) {
+                codeString += word + ' ';
+                codeFlag = true;
+
+                if (word.indexOf('</code>') !== -1) {
+                    inner.push(<span key={word+i+z+"_span"} className={highlightSearchClass} dangerouslySetInnerHTML={{__html: codeString}} />);
+                    codeString = '';
+                    codeFlag = false;
+                }
+            } else if (explicitMention &&
                 (UserStore.getProfileByUsername(explicitMention[1]) ||
                 Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1))
             {
@@ -437,60 +510,86 @@ module.exports.textToJsx = function(text, options) {
                 // do both a non-case sensitive and case senstive check
                 var mClass = implicitKeywords.indexOf('@'+name.toLowerCase()) !== -1 || implicitKeywords.indexOf('@'+name) !== -1 ? mentionClass : "";
 
-                var suffix = word.match(puncEndRegex);
-                var prefix = word.match(puncStartRegex);
-
                 if (searchTerm === name) {
                     highlightSearchClass = " search-highlight";
                 }
 
-                inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>);
+                if (useTextFormatting) {
+                    prefixSpan ? inner.push(prefixSpan) : null;
+                    inner.push(<span key={word+i+z+"word_span"}><a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>{"@" + name}</a></span>);
+                    suffixSpan ? inner.push(suffixSpan) : null;
+                }
+                else
+                    inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>);
             } else if (testUrlMatch(word).length) {
                 var match = testUrlMatch(word)[0];
                 var link = match.link;
 
-                var prefix = word.substring(0,word.indexOf(match.text));
-                var suffix = word.substring(word.indexOf(match.text)+match.text.length);
-
-                inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_link"} className={"theme" + highlightSearchClass} target="_blank" href={link}>{match.text}</a>{suffix} </span>);
+                prefix = word.substring(0,word.indexOf(match.text));
+                suffix = word.substring(word.indexOf(match.text)+match.text.length);
+                prefixSpan = prefix ? (<span key={word+i+z+"pre_span"}><span dangerouslySetInnerHTML={{__html: prefix}} /></span>) : null;
+                suffixSpan = suffix ? (<span key={word+i+z+"suf_span"}><span dangerouslySetInnerHTML={{__html: suffix}} /> </span>) : <span key={word+i+z+"suf_span"}> </span>;
 
+                if (useTextFormatting) {
+                    prefixSpan ? inner.push(prefixSpan) : null;
+                    inner.push(<span key={word+i+z+"_span"}><a key={name+i+z+"_link"} className={"theme" + highlightSearchClass} target="_blank" href={link}>{match.text}</a></span>);
+                    suffixSpan ? inner.push(suffixSpan) : null;
+                }
+                else
+                    inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_link"} className={"theme" + highlightSearchClass} target="_blank" href={link}>{match.text}</a>{suffix} </span>);
             } else if (trimWord.match(hashRegex)) {
-                var suffix = word.match(puncEndRegex);
-                var prefix = word.match(puncStartRegex);
                 var mClass = implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1 ? mentionClass : "";
 
                 if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) {
                     highlightSearchClass = " search-highlight";
                 }
 
-                inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>);
+                if (useTextFormatting) {
+                    prefixSpan ? inner.push(prefixSpan) : null;
+                    inner.push(<span key={word+i+z+"_span"}><a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a></span>);
+                    suffixSpan ? inner.push(suffixSpan) : null;
+                }
+                else
+                    inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>);
 
             } else if (implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1) {
-                var suffix = word.match(puncEndRegex);
-                var prefix = word.match(puncStartRegex);
-
                 if (trimWord.charAt(0) === '@') {
                     if (searchTerm === trimWord.substring(1).toLowerCase()) {
                         highlightSearchClass = " search-highlight";
                     }
-                    inner.push(<span key={word+i+z+"_span"} key={name+i+z+"_span"}>{prefix}<a className={mentionClass + highlightSearchClass} key={name+i+z+"_link"} href="#">{trimWord}</a>{suffix} </span>);
+                    if (useTextFormatting) {
+                        prefixSpan ? inner.push(prefixSpan) : null;
+                        inner.push(<span key={word+i+z+"_span"} key={name+i+z+"_span"}><a className={mentionClass + highlightSearchClass} key={name+i+z+"_link"} href="#">{trimWord}</a></span>);
+                        suffixSpan ? inner.push(suffixSpan) : null;
+                    }
+                    else
+                        inner.push(<span key={word+i+z+"_span"} key={name+i+z+"_span"}>{prefix}<a className={mentionClass + highlightSearchClass} key={name+i+z+"_link"} href="#">{trimWord}</a>{suffix} </span>);
                 } else {
-                    inner.push(<span key={word+i+z+"_span"}>{prefix}<span className={mentionClass + highlightSearchClass}>{module.exports.replaceHtmlEntities(trimWord)}</span>{suffix} </span>);
+                    if (useTextFormatting) {
+                        prefixSpan ? inner.push(prefixSpan) : null;
+                        inner.push(<span key={word+i+z+"_span"}><span className={mentionClass + highlightSearchClass}>{trimWord}</span></span>);
+                        suffixSpan ? inner.push(suffixSpan) : null;
+                    }
+                    else
+                        inner.push(<span key={word+i+z+"_span"}>{prefix}<span className={mentionClass + highlightSearchClass}>{module.exports.replaceHtmlEntities(trimWord)}</span>{suffix} </span>);
                 }
 
             } else if (word === "") {
                 // if word is empty dont include a span
             } else {
-                inner.push(<span key={word+i+z+"_span"}><span className={highlightSearchClass}>{module.exports.replaceHtmlEntities(word)}</span> </span>);
+                if (useTextFormatting)
+                    inner.push(<span key={word+i+z+"_span"} className={highlightSearchClass} dangerouslySetInnerHTML={{__html: word + ' '}} />);
+                else
+                    inner.push(<span key={word+i+z+"_span"}><span className={highlightSearchClass}>{module.exports.replaceHtmlEntities(word)}</span> </span>);
             }
             highlightSearchClass = "";
         }
-        if (i != lines.length-1)
+        if (!useTextFormatting && i != lines.length-1)
             inner.push(<br key={"br_"+i+z}/>);
     }
 
     return inner;
-}
+};
 
 module.exports.getFileType = function(ext) {
     ext = ext.toLowerCase();
-- 
cgit v1.2.3-1-g7c22


From 23a331f933af834ed1a26806f087e61ca6ddd93a Mon Sep 17 00:00:00 2001
From: Reed Garmsen <rgarmsen2295@gmail.com>
Date: Mon, 10 Aug 2015 16:44:54 -0700
Subject: Cosmetic refactoring of textToJsx function

---
 web/react/utils/utils.jsx | 213 ++++++++++++++++++++++++++++------------------
 1 file changed, 131 insertions(+), 82 deletions(-)

(limited to 'web/react/utils/utils.jsx')

diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index d4654d7d5..bbe5003bc 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -403,49 +403,49 @@ module.exports.customMarkedRenderer = function(options) {
         customTextRenderer.codespan = function(code) {
             return code;
         };
-        customTextRenderer.link = function(href, title, text) {
+        customTextRenderer.link = function(href) {
             return href;
         };
-        customTextRenderer.image = function(href, title, text) {
+        customTextRenderer.image = function(href) {
             return href;
         };
     } else {
-        customTextRenderer.link = function(href, title, text) {
+        customTextRenderer.link = function(href) {
             return href;
         };
-        customTextRenderer.image = function(href, title, text) {
+        customTextRenderer.image = function(href) {
             return href;
         };
     }
     return customTextRenderer;
 };
 
-var oldExplicitMentionRegex = /(?:<mention>)([\s\S]*?)(?:<\/mention>)/g;
 var puncStartRegex = /^((?![@#])[^A-Za-z0-9_<>])+/g;
 var puncEndRegex = /([^A-Za-z0-9_<>])+$/g;
 var startTagRegex = /(<\s*\w.*?>)+/g;
 var endTagRegex = /(<\s*\/\s*\w\s*.*?>|<\s*br\s*>)+/g;
 
-module.exports.textToJsx = function(text, options) {
+module.exports.textToJsx = function(textToChange, options) {
     var useTextFormatting = config.AllowTextFormatting && (!options || !options.noTextFormatting);
+    var text = textToChange;
 
     if (useTextFormatting) {
         text = formatText(text, {sanitize: true, mangle: false, gfm: true, breaks: true, tables: false, smartypants: true, renderer: module.exports.customMarkedRenderer()});
     }
 
-    if (options && options['singleline']) {
-        var repRegex = new RegExp("\n", "g");
-        text = text.replace(repRegex, " ");
+    if (options && options.singleline) {
+        var repRegex = new RegExp('\n', 'g');
+        text = text.replace(repRegex, ' ');
     }
 
-    var searchTerm = ""
-    if (options && options['searchTerm']) {
-        searchTerm = options['searchTerm'].toLowerCase();
+    var searchTerm = '';
+    if (options && options.searchTerm) {
+        searchTerm = options.searchTerm.toLowerCase();
     }
 
-    var mentionClass = "mention-highlight";
-    if (options && options['noMentionHighlight']) {
-        mentionClass = "";
+    var mentionClass = 'mention-highlight';
+    if (options && options.noMentionHighlight) {
+        mentionClass = '';
     }
 
     var inner = [];
@@ -457,11 +457,11 @@ module.exports.textToJsx = function(text, options) {
 
     var implicitKeywords = UserStore.getCurrentMentionKeys();
 
-    var lines = text.split("\n");
+    var lines = text.split('\n');
     for (var i = 0; i < lines.length; i++) {
         var line = lines[i];
-        var words = line.split(" ");
-        var highlightSearchClass = "";
+        var words = line.split(' ');
+        var highlightSearchClass = '';
         for (var z = 0; z < words.length; z++) {
             var word = words[z];
             var trimWord;
@@ -472,25 +472,46 @@ module.exports.textToJsx = function(text, options) {
             }
             var mentionRegex = /^(?:@)([a-z0-9_]+)$/gi; // looks loop invariant but a weird JS bug needs it to be redefined here
             var explicitMention = mentionRegex.exec(trimWord);
+            var mClass;
 
-            var prefix;
-            var suffix;
-            var prefixSpan;
-            var suffixSpan;
+            var prefix = '';
+            var suffix = '';
+            var prefixSpan = null;
+            var suffixSpan = <span key={word + i + z + 'suf_span'}> </span>;
             if (useTextFormatting) {
-                prefix = (word.match(startTagRegex) ? word.match(startTagRegex) : "") + (word.replace(startTagRegex, '').match(puncStartRegex) ? word.replace(startTagRegex, '').match(puncStartRegex) : "");
-                suffix = (word.match(endTagRegex) ? word.match(endTagRegex) : "") + (word.replace(endTagRegex, '').match(puncEndRegex) ? word.replace(endTagRegex, '').match(puncEndRegex) : "");
-                prefixSpan = prefix ? (<span key={word+i+z+"pre_span"}><span dangerouslySetInnerHTML={{__html: prefix}} /></span>) : null;
-                suffixSpan = suffix ? (<span key={word+i+z+"suf_span"}><span dangerouslySetInnerHTML={{__html: suffix}} /> </span>) : (<span key={word+i+z+"suf_span"}> </span>);
+                if (word.match(startTagRegex)) {
+                    prefix += word.match(startTagRegex);
+                }
+                if (word.replace(startTagRegex, '').match(puncStartRegex)) {
+                    prefix += word.replace(startTagRegex, '').match(puncStartRegex);
+                }
+
+                if (word.match(endTagRegex)) {
+                    suffix += word.match(endTagRegex);
+                }
+                if (word.replace(endTagRegex, '').match(puncEndRegex)) {
+                    suffix += word.replace(endTagRegex, '').match(puncEndRegex);
+                }
+
+                if (prefix) {
+                    prefixSpan = <span key={word + i + z + 'pre_span'}><span dangerouslySetInnerHTML={{__html: prefix}} /></span>;
+                }
+                if (suffix) {
+                    suffixSpan = <span key={word + i + z + 'suf_span'}><span dangerouslySetInnerHTML={{__html: suffix}} /> </span>;
+                }
             } else {
                 prefix = word.match(puncStartRegex);
                 suffix = word.match(puncEndRegex);
-                prefixSpan = prefix ? (<span key={word+i+z+"pre_span"}>{prefix}</span>) : null;
-                suffixSpan = suffix ? (<span key={word+i+z+"suf_span"}>{suffix} </span>) : (<span key={word+i+z+"suf_span"}> </span>);
+                if (prefix) {
+                    prefixSpan = <span key={word + i + z + 'pre_span'}>{prefix}</span>;
+                }
+                if (suffix) {
+                    suffixSpan = <span key={word + i + z + 'suf_span'}>{suffix} </span>;
+                }
             }
 
-            if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm != "") {
-                highlightSearchClass = " search-highlight";
+            if ((trimWord.toLowerCase().indexOf(searchTerm) > -1 || word.toLowerCase().indexOf(searchTerm) > -1) && searchTerm !== '') {
+                highlightSearchClass = ' search-highlight';
             }
 
             if (useTextFormatting && (codeFlag || word.indexOf('<code>') !== -1)) {
@@ -498,94 +519,122 @@ module.exports.textToJsx = function(text, options) {
                 codeFlag = true;
 
                 if (word.indexOf('</code>') !== -1) {
-                    inner.push(<span key={word+i+z+"_span"} className={highlightSearchClass} dangerouslySetInnerHTML={{__html: codeString}} />);
+                    inner.push(<span key={word + i + z + '_span'} className={highlightSearchClass} dangerouslySetInnerHTML={{__html: codeString}} />);
                     codeString = '';
                     codeFlag = false;
                 }
             } else if (explicitMention &&
-                (UserStore.getProfileByUsername(explicitMention[1]) ||
-                Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1))
-            {
+                      (UserStore.getProfileByUsername(explicitMention[1]) ||
+                      Constants.SPECIAL_MENTIONS.indexOf(explicitMention[1]) !== -1)) {
                 var name = explicitMention[1];
+
                 // do both a non-case sensitive and case senstive check
-                var mClass = implicitKeywords.indexOf('@'+name.toLowerCase()) !== -1 || implicitKeywords.indexOf('@'+name) !== -1 ? mentionClass : "";
+                mClass = '';
+                if (implicitKeywords.indexOf('@' + name.toLowerCase()) !== -1 || implicitKeywords.indexOf('@' + name) !== -1) {
+                    mClass = mentionClass;
+                }
 
                 if (searchTerm === name) {
-                    highlightSearchClass = " search-highlight";
+                    highlightSearchClass = ' search-highlight';
                 }
 
                 if (useTextFormatting) {
-                    prefixSpan ? inner.push(prefixSpan) : null;
-                    inner.push(<span key={word+i+z+"word_span"}><a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>{"@" + name}</a></span>);
-                    suffixSpan ? inner.push(suffixSpan) : null;
+                    if (prefixSpan) {
+                        inner.push(prefixSpan);
+                    }
+                    inner.push(<span key={word + i + z + 'word_span'}><a className={mClass + highlightSearchClass + ' mention-link'} key={name + i + z + '_link'} href='#' onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>{'@' + name}</a></span>);
+                    if (suffixSpan) {
+                        inner.push(suffixSpan);
+                    }
+                } else {
+                    inner.push(<span key={name + i + z + '_span'}>{prefix}<a className={mClass + highlightSearchClass + ' mention-link'} key={name + i + z + '_link'} href='#' onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>);
                 }
-                else
-                    inner.push(<span key={name+i+z+"_span"}>{prefix}<a className={mClass + highlightSearchClass + " mention-link"} key={name+i+z+"_link"} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(name)}>@{name}</a>{suffix} </span>);
             } else if (testUrlMatch(word).length) {
                 var match = testUrlMatch(word)[0];
                 var link = match.link;
 
-                prefix = word.substring(0,word.indexOf(match.text));
-                suffix = word.substring(word.indexOf(match.text)+match.text.length);
-                prefixSpan = prefix ? (<span key={word+i+z+"pre_span"}><span dangerouslySetInnerHTML={{__html: prefix}} /></span>) : null;
-                suffixSpan = suffix ? (<span key={word+i+z+"suf_span"}><span dangerouslySetInnerHTML={{__html: suffix}} /> </span>) : <span key={word+i+z+"suf_span"}> </span>;
+                prefix = word.substring(0, word.indexOf(match.text));
+                suffix = word.substring(word.indexOf(match.text) + match.text.length);
+                if (prefix) {
+                    prefixSpan = <span key={word + i + z + 'pre_span'}><span dangerouslySetInnerHTML={{__html: prefix}} /></span>;
+                }
+                if (suffix) {
+                    suffixSpan = <span key={word + i + z + 'suf_span'}><span dangerouslySetInnerHTML={{__html: suffix}} /> </span>;
+                } else {
+                    suffixSpan = <span key={word + i + z + 'suf_span'}> </span>;
+                }
 
                 if (useTextFormatting) {
-                    prefixSpan ? inner.push(prefixSpan) : null;
-                    inner.push(<span key={word+i+z+"_span"}><a key={name+i+z+"_link"} className={"theme" + highlightSearchClass} target="_blank" href={link}>{match.text}</a></span>);
-                    suffixSpan ? inner.push(suffixSpan) : null;
+                    if (prefixSpan) {
+                        inner.push(prefixSpan);
+                    }
+                    inner.push(<span key={word + i + z + '_span'}><a key={name + i + z + '_link'} className={'theme' + highlightSearchClass} target='_blank' href={link}>{match.text}</a></span>);
+                    if (suffixSpan) {
+                        inner.push(suffixSpan);
+                    }
+                } else {
+                    inner.push(<span key={word + i + z + '_span'}>{prefix}<a key={word + i + z + '_link'} className={'theme' + highlightSearchClass} target='_blank' href={link}>{match.text}</a>{suffix} </span>);
                 }
-                else
-                    inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_link"} className={"theme" + highlightSearchClass} target="_blank" href={link}>{match.text}</a>{suffix} </span>);
             } else if (trimWord.match(hashRegex)) {
-                var mClass = implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1 ? mentionClass : "";
+                mClass = '';
+                if (implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1) {
+                    mClass = mentionClass;
+                }
 
                 if (searchTerm === trimWord.substring(1).toLowerCase() || searchTerm === trimWord.toLowerCase()) {
-                    highlightSearchClass = " search-highlight";
+                    highlightSearchClass = ' search-highlight';
                 }
 
                 if (useTextFormatting) {
-                    prefixSpan ? inner.push(prefixSpan) : null;
-                    inner.push(<span key={word+i+z+"_span"}><a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a></span>);
-                    suffixSpan ? inner.push(suffixSpan) : null;
+                    if (prefixSpan) {
+                        inner.push(prefixSpan);
+                    }
+                    inner.push(<span key={word + i + z + '_span'}><a key={word + i + z + '_hash'} className={'theme ' + mClass + highlightSearchClass} href='#' onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a></span>);
+                    if (suffixSpan) {
+                        inner.push(suffixSpan);
+                    }
+                } else {
+                    inner.push(<span key={word + i + z + '_span'}>{prefix}<a key={word + i + z + '_hash'} className={'theme ' + mClass + highlightSearchClass} href='#' onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>);
                 }
-                else
-                    inner.push(<span key={word+i+z+"_span"}>{prefix}<a key={word+i+z+"_hash"} className={"theme " + mClass + highlightSearchClass} href="#" onClick={function(value) { return function() { module.exports.searchForTerm(value); } }(trimWord)}>{trimWord}</a>{suffix} </span>);
-
             } else if (implicitKeywords.indexOf(trimWord) !== -1 || implicitKeywords.indexOf(trimWord.toLowerCase()) !== -1) {
                 if (trimWord.charAt(0) === '@') {
                     if (searchTerm === trimWord.substring(1).toLowerCase()) {
-                        highlightSearchClass = " search-highlight";
+                        highlightSearchClass = ' search-highlight';
                     }
                     if (useTextFormatting) {
-                        prefixSpan ? inner.push(prefixSpan) : null;
-                        inner.push(<span key={word+i+z+"_span"} key={name+i+z+"_span"}><a className={mentionClass + highlightSearchClass} key={name+i+z+"_link"} href="#">{trimWord}</a></span>);
-                        suffixSpan ? inner.push(suffixSpan) : null;
+                        if (prefixSpan) {
+                            inner.push(prefixSpan);
+                        }
+                        inner.push(<span key={word + i + z + '_span'}><a className={mentionClass + highlightSearchClass} key={name + i + z + '_link'} href='#'>{trimWord}</a></span>);
+                        if (suffixSpan) {
+                            inner.push(suffixSpan);
+                        }
+                    } else {
+                        inner.push(<span key={word + i + z + '_span'}>{prefix}<a className={mentionClass + highlightSearchClass} key={name + i + z + '_link'} href='#'>{trimWord}</a>{suffix} </span>);
                     }
-                    else
-                        inner.push(<span key={word+i+z+"_span"} key={name+i+z+"_span"}>{prefix}<a className={mentionClass + highlightSearchClass} key={name+i+z+"_link"} href="#">{trimWord}</a>{suffix} </span>);
-                } else {
-                    if (useTextFormatting) {
-                        prefixSpan ? inner.push(prefixSpan) : null;
-                        inner.push(<span key={word+i+z+"_span"}><span className={mentionClass + highlightSearchClass}>{trimWord}</span></span>);
-                        suffixSpan ? inner.push(suffixSpan) : null;
+                } else if (useTextFormatting) {
+                    if (prefixSpan) {
+                        inner.push(prefixSpan);
+                    }
+                    inner.push(<span key={word + i + z + '_span'}><span className={mentionClass + highlightSearchClass}>{trimWord}</span></span>);
+                    if (suffixSpan) {
+                        inner.push(suffixSpan);
                     }
-                    else
-                        inner.push(<span key={word+i+z+"_span"}>{prefix}<span className={mentionClass + highlightSearchClass}>{module.exports.replaceHtmlEntities(trimWord)}</span>{suffix} </span>);
+                } else {
+                    inner.push(<span key={word + i + z + '_span'}>{prefix}<span className={mentionClass + highlightSearchClass}>{module.exports.replaceHtmlEntities(trimWord)}</span>{suffix} </span>);
+                }
+            } else if (word !== '') {
+                if (useTextFormatting) {
+                    inner.push(<span key={word + i + z + '_span'} className={highlightSearchClass} dangerouslySetInnerHTML={{__html: word + ' '}} />);
+                } else {
+                    inner.push(<span key={word + i + z + '_span'}><span className={highlightSearchClass}>{module.exports.replaceHtmlEntities(word)}</span> </span>);
                 }
-
-            } else if (word === "") {
-                // if word is empty dont include a span
-            } else {
-                if (useTextFormatting)
-                    inner.push(<span key={word+i+z+"_span"} className={highlightSearchClass} dangerouslySetInnerHTML={{__html: word + ' '}} />);
-                else
-                    inner.push(<span key={word+i+z+"_span"}><span className={highlightSearchClass}>{module.exports.replaceHtmlEntities(word)}</span> </span>);
             }
-            highlightSearchClass = "";
+            highlightSearchClass = '';
+        }
+        if (!useTextFormatting && i !== lines.length - 1) {
+            inner.push(<br key={'br_' + i}/>);
         }
-        if (!useTextFormatting && i != lines.length-1)
-            inner.push(<br key={"br_"+i+z}/>);
     }
 
     return inner;
-- 
cgit v1.2.3-1-g7c22


From e02883baf2fd2a0cfc21bd557bc0ecc69afd707c Mon Sep 17 00:00:00 2001
From: Reed Garmsen <rgarmsen2295@gmail.com>
Date: Mon, 10 Aug 2015 16:51:59 -0700
Subject: Cosmetic refactoring of post_body.jsx

---
 web/react/utils/utils.jsx | 2 ++
 1 file changed, 2 insertions(+)

(limited to 'web/react/utils/utils.jsx')

diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index bbe5003bc..2136accb4 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -634,6 +634,8 @@ module.exports.textToJsx = function(textToChange, options) {
         }
         if (!useTextFormatting && i !== lines.length - 1) {
             inner.push(<br key={'br_' + i}/>);
+        } else if (useTextFormatting && !codeFlag && i < lines.length - 2) {
+            inner.push(<br key={'br_' + i}/>);
         }
     }
 
-- 
cgit v1.2.3-1-g7c22


From 41e74860d8abc8e33648471249b127dd3f60fb88 Mon Sep 17 00:00:00 2001
From: Reed Garmsen <rgarmsen2295@gmail.com>
Date: Wed, 12 Aug 2015 11:02:47 -0700
Subject: Cosmetic refactoring of the notifyMe function in utils.jsx and
 removed unnecessary files

---
 web/react/utils/utils.jsx | 48 +++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

(limited to 'web/react/utils/utils.jsx')

diff --git a/web/react/utils/utils.jsx b/web/react/utils/utils.jsx
index 2136accb4..2a4cbaa62 100644
--- a/web/react/utils/utils.jsx
+++ b/web/react/utils/utils.jsx
@@ -97,31 +97,31 @@ module.exports.getCookie = function(name) {
 }
 
 module.exports.notifyMe = function(title, body, channel) {
-    if ("Notification" in window && Notification.permission !== 'denied') {
-    Notification.requestPermission(function (permission) {
-      if (Notification.permission !== permission) {
-          Notification.permission = permission;
-      }
+    if ('Notification' in window && Notification.permission !== 'denied') {
+        Notification.requestPermission(function(permission) {
+            if (Notification.permission !== permission) {
+                Notification.permission = permission;
+            }
 
-      if (permission === "granted") {
-        var notification = new Notification(title,
-            { body: body, tag: body, icon: '/static/images/icon50x50.gif' }
-        );
-        notification.onclick = function() {
-          window.focus();
-          if (channel) {
-              module.exports.switchChannel(channel);
-          } else {
-              window.location.href = "/";
-          }
-        };
-        setTimeout(function(){
-          notification.close();
-        }, 5000);
-      }
-    });
-  }
-}
+            if (permission === 'granted') {
+                var notification = new Notification(title,
+                    {body: body, tag: body, icon: '/static/images/icon50x50.gif'}
+                );
+                notification.onclick = function() {
+                    window.focus();
+                    if (channel) {
+                        module.exports.switchChannel(channel);
+                    } else {
+                        window.location.href = '/';
+                    }
+                };
+                setTimeout(function() {
+                    notification.close();
+                }, 5000);
+            }
+        });
+    }
+};
 
 module.exports.ding = function() {
   var audio = new Audio('/static/images/ding.mp3');
-- 
cgit v1.2.3-1-g7c22

