diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js
index 2bb0ef4c04..63445dcf30 100644
--- a/src/HtmlUtils.js
+++ b/src/HtmlUtils.js
@@ -55,7 +55,30 @@ export function unicodeToImage(str) {
});
return str;
-};
+}
+
+export function stripParagraphs(html: string): string {
+ const contentDiv = document.createElement('div');
+ contentDiv.innerHTML = html;
+
+ if (contentDiv.children.length === 0) {
+ return contentDiv.innerHTML;
+ }
+
+ let contentHTML = "";
+ for (let i=0; i';
+ } else {
+ const temp = document.createElement('div');
+ temp.appendChild(element.cloneNode(true));
+ contentHTML += temp.innerHTML;
+ }
+ }
+
+ return contentHTML;
+}
var sanitizeHtmlParams = {
allowedTags: [
@@ -153,8 +176,8 @@ class BaseHighlighter {
}
// handle postamble
- if (lastOffset != safeSnippet.length) {
- var subSnippet = safeSnippet.substring(lastOffset, undefined);
+ if (lastOffset !== safeSnippet.length) {
+ subSnippet = safeSnippet.substring(lastOffset, undefined);
nodes = nodes.concat(this._applySubHighlights(subSnippet, safeHighlights));
}
return nodes;
@@ -219,7 +242,7 @@ class TextHighlighter extends BaseHighlighter {
;
if (highlight && this.highlightLink) {
- node = {node}
+ node = {node};
}
return node;
@@ -227,7 +250,6 @@ class TextHighlighter extends BaseHighlighter {
}
-module.exports = {
/* turn a matrix event body into html
*
* content: 'content' of the MatrixEvent
@@ -236,59 +258,57 @@ module.exports = {
*
* opts.highlightLink: optional href to add to highlighted words
*/
- bodyToHtml: function(content, highlights, opts) {
- opts = opts || {};
+export function bodyToHtml(content, highlights, opts) {
+ opts = opts || {};
- var isHtml = (content.format === "org.matrix.custom.html");
- let body = isHtml ? content.formatted_body : escape(content.body);
+ var isHtml = (content.format === "org.matrix.custom.html");
+ let body = isHtml ? content.formatted_body : escape(content.body);
- var safeBody;
- // XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying
- // to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which
- // are interrupted by HTML tags (not that we did before) - e.g. foobar won't get highlighted
- // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either
- try {
- if (highlights && highlights.length > 0) {
- var highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink);
- var safeHighlights = highlights.map(function(highlight) {
- return sanitizeHtml(highlight, sanitizeHtmlParams);
- });
- // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure.
- sanitizeHtmlParams.textFilter = function(safeText) {
- return highlighter.applyHighlights(safeText, safeHighlights).join('');
- };
- }
- safeBody = sanitizeHtml(body, sanitizeHtmlParams);
- safeBody = unicodeToImage(safeBody);
- }
- finally {
- delete sanitizeHtmlParams.textFilter;
+ var safeBody;
+ // XXX: We sanitize the HTML whilst also highlighting its text nodes, to avoid accidentally trying
+ // to highlight HTML tags themselves. However, this does mean that we don't highlight textnodes which
+ // are interrupted by HTML tags (not that we did before) - e.g. foobar won't get highlighted
+ // by an attempt to search for 'foobar'. Then again, the search query probably wouldn't work either
+ try {
+ if (highlights && highlights.length > 0) {
+ var highlighter = new HtmlHighlighter("mx_EventTile_searchHighlight", opts.highlightLink);
+ var safeHighlights = highlights.map(function(highlight) {
+ return sanitizeHtml(highlight, sanitizeHtmlParams);
+ });
+ // XXX: hacky bodge to temporarily apply a textFilter to the sanitizeHtmlParams structure.
+ sanitizeHtmlParams.textFilter = function(safeText) {
+ return highlighter.applyHighlights(safeText, safeHighlights).join('');
+ };
}
+ safeBody = sanitizeHtml(body, sanitizeHtmlParams);
+ safeBody = unicodeToImage(safeBody);
+ }
+ finally {
+ delete sanitizeHtmlParams.textFilter;
+ }
- EMOJI_REGEX.lastIndex = 0;
- let contentBodyTrimmed = content.body.trim();
- let match = EMOJI_REGEX.exec(contentBodyTrimmed);
- let emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length;
+ EMOJI_REGEX.lastIndex = 0;
+ let contentBodyTrimmed = content.body.trim();
+ let match = EMOJI_REGEX.exec(contentBodyTrimmed);
+ let emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length;
- const className = classNames({
- 'mx_EventTile_body': true,
- 'mx_EventTile_bigEmoji': emojiBody,
- 'markdown-body': isHtml,
- });
- return ;
- },
+ const className = classNames({
+ 'mx_EventTile_body': true,
+ 'mx_EventTile_bigEmoji': emojiBody,
+ 'markdown-body': isHtml,
+ });
+ return ;
+}
- highlightDom: function(element) {
- var blocks = element.getElementsByTagName("code");
- for (var i = 0; i < blocks.length; i++) {
- highlight.highlightBlock(blocks[i]);
- }
- },
-
- emojifyText: function(text) {
- return {
- __html: unicodeToImage(escape(text)),
- };
- },
-};
+export function highlightDom(element) {
+ var blocks = element.getElementsByTagName("code");
+ for (var i = 0; i < blocks.length; i++) {
+ highlight.highlightBlock(blocks[i]);
+ }
+}
+export function emojifyText(text) {
+ return {
+ __html: unicodeToImage(escape(text)),
+ };
+}
diff --git a/src/SlashCommands.js b/src/SlashCommands.js
index 16df5ef2e1..523d1d8f3c 100644
--- a/src/SlashCommands.js
+++ b/src/SlashCommands.js
@@ -304,7 +304,7 @@ module.exports = {
// IRC-style commands
input = input.replace(/\s+$/, "");
if (input[0] === "/" && input[1] !== "/") {
- var bits = input.match(/^(\S+?)( +(.*))?$/);
+ var bits = input.match(/^(\S+?)( +((.|\n)*))?$/);
var cmd, args;
if (bits) {
cmd = bits[1].substring(1).toLowerCase();
diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js
index 3c15259dd9..2733bddc14 100644
--- a/src/components/views/rooms/MessageComposerInput.js
+++ b/src/components/views/rooms/MessageComposerInput.js
@@ -47,6 +47,7 @@ import KeyCode from '../../../KeyCode';
import UserSettingsStore from '../../../UserSettingsStore';
import * as RichText from '../../../RichText';
+import * as HtmlUtils from '../../../HtmlUtils';
import Autocomplete from './Autocomplete';
import {Completion} from "../../../autocomplete/Autocompleter";
@@ -63,18 +64,9 @@ function stateToMarkdown(state) {
''); // this is *not* a zero width space, trust me :)
}
-
-// FIXME Breaks markdown with multiple paragraphs, since it only strips first and last
function mdownToHtml(mdown: string): string {
let html = marked(mdown) || "";
html = html.trim();
- // strip start and end
tags else you get 'orrible spacing
- if (html.indexOf("
") === 0) {
- html = html.substring("
".length);
- }
- if (html.lastIndexOf("
") === (html.length - "".length)) {
- html = html.substring(0, html.length - "".length);
- }
return html;
}
@@ -547,6 +539,8 @@ export default class MessageComposerInput extends React.Component {
contentHTML = mdownToHtml(contentText);
}
+ contentHTML = HtmlUtils.stripParagraphs(contentHTML);
+
let sendFn = this.client.sendHtmlMessage;
if (contentText.startsWith('/me')) {
@@ -561,16 +555,16 @@ export default class MessageComposerInput extends React.Component {
sendMessagePromise.then(() => {
dis.dispatch({
- action: 'message_sent'
+ action: 'message_sent',
});
}, () => {
dis.dispatch({
- action: 'message_send_failed'
+ action: 'message_send_failed',
});
});
this.setState({
- editorState: this.createEditorState()
+ editorState: this.createEditorState(),
});
return true;