diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js new file mode 100644 index 00000000..ff7af121 --- /dev/null +++ b/src/HtmlUtils.js @@ -0,0 +1,79 @@ +/* +Copyright 2015 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +var React = require('react'); +var sanitizeHtml = require('sanitize-html'); + +var allowedAttributes = sanitizeHtml.defaults.allowedAttributes; +allowedAttributes['font'] = ['color']; +var sanitizeHtmlParams = { + allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font', 'h1', 'h2' ]), + allowedAttributes: allowedAttributes, +}; + +module.exports = { + bodyToHtml: function(content, searchTerm) { + var originalBody = content.body; + var body; + + if (searchTerm) { + var lastOffset = 0; + var bodyList = []; + var k = 0; + var offset; + + // XXX: rather than searching for the search term in the body, + // we should be looking at the match delimiters returned by the FTS engine + if (content.format === "org.matrix.custom.html") { + + var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); + var safeSearchTerm = sanitizeHtml(searchTerm, sanitizeHtmlParams); + while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) { + // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means + // hooking into the sanitizer parser rather than treating it as a string. Otherwise + // the act of highlighting a or whatever will break the HTML badly. + bodyList.push(); + bodyList.push(); + lastOffset = offset + safeSearchTerm.length; + } + bodyList.push(); + } + else { + while ((offset = originalBody.indexOf(searchTerm, lastOffset)) >= 0) { + bodyList.push({ originalBody.substring(lastOffset, offset) }); + bodyList.push({ searchTerm }); + lastOffset = offset + searchTerm.length; + } + bodyList.push({ originalBody.substring(lastOffset) }); + } + body = bodyList; + } + else { + if (content.format === "org.matrix.custom.html") { + var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); + body = ; + } + else { + body = originalBody; + } + } + + return body; + } +} + diff --git a/src/skins/vector/views/molecules/MNoticeTile.js b/src/skins/vector/views/molecules/MNoticeTile.js index a0cedb1d..8d8c7792 100644 --- a/src/skins/vector/views/molecules/MNoticeTile.js +++ b/src/skins/vector/views/molecules/MNoticeTile.js @@ -17,67 +17,18 @@ limitations under the License. 'use strict'; var React = require('react'); -var sanitizeHtml = require('sanitize-html'); +var HtmlUtils = require('../../../../HtmlUtils'); var MNoticeTileController = require('matrix-react-sdk/lib/controllers/molecules/MNoticeTile') -var allowedAttributes = sanitizeHtml.defaults.allowedAttributes; -allowedAttributes['font'] = ['color']; -var sanitizeHtmlParams = { - allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]), - allowedAttributes: allowedAttributes, -}; - module.exports = React.createClass({ displayName: 'MNoticeTile', mixins: [MNoticeTileController], - // FIXME: this entire class is copy-pasted from MTextTile :( + // XXX: fix horrible duplication with MTextTile render: function() { var content = this.props.mxEvent.getContent(); - var originalBody = content.body; - var body; - - if (this.props.searchTerm) { - var lastOffset = 0; - var bodyList = []; - var k = 0; - var offset; - - // XXX: rather than searching for the search term in the body, - // we should be looking at the match delimiters returned by the FTS engine - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams); - while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) { - // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means - // hooking into the sanitizer parser rather than treating it as a string. Otherwise - // the act of highlighting a or whatever will break the HTML badly. - bodyList.push(); - bodyList.push(); - lastOffset = offset + safeSearchTerm.length; - } - bodyList.push(); - } - else { - while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) { - bodyList.push({ originalBody.substring(lastOffset, offset) }); - bodyList.push({ this.props.searchTerm }); - lastOffset = offset + this.props.searchTerm.length; - } - bodyList.push({ originalBody.substring(lastOffset) }); - } - body = bodyList; - } - else { - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - body = ; - } - else { - body = originalBody; - } - } + var body = HtmlUtils.bodyToHtml(content, this.props.searchTerm); return ( diff --git a/src/skins/vector/views/molecules/MTextTile.js b/src/skins/vector/views/molecules/MTextTile.js index 12bafa37..aa5291d7 100644 --- a/src/skins/vector/views/molecules/MTextTile.js +++ b/src/skins/vector/views/molecules/MTextTile.js @@ -17,67 +17,17 @@ limitations under the License. 'use strict'; var React = require('react'); -var sanitizeHtml = require('sanitize-html'); +var HtmlUtils = require('../../../../HtmlUtils'); var MTextTileController = require('matrix-react-sdk/lib/controllers/molecules/MTextTile') -var allowedAttributes = sanitizeHtml.defaults.allowedAttributes; -allowedAttributes['font'] = ['color']; -var sanitizeHtmlParams = { - allowedTags: sanitizeHtml.defaults.allowedTags.concat([ 'font' ]), - allowedAttributes: allowedAttributes, -}; - module.exports = React.createClass({ displayName: 'MTextTile', mixins: [MTextTileController], - // FIXME: this entire class is copy-pasted from MTextTile :( render: function() { var content = this.props.mxEvent.getContent(); - var originalBody = content.body; - var body; - - if (this.props.searchTerm) { - var lastOffset = 0; - var bodyList = []; - var k = 0; - var offset; - - // XXX: rather than searching for the search term in the body, - // we should be looking at the match delimiters returned by the FTS engine - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - var safeSearchTerm = sanitizeHtml(this.props.searchTerm, sanitizeHtmlParams); - while ((offset = safeBody.indexOf(safeSearchTerm, lastOffset)) >= 0) { - // FIXME: we need to apply the search highlighting to only the text elements of HTML, which means - // hooking into the sanitizer parser rather than treating it as a string. Otherwise - // the act of highlighting a or whatever will break the HTML badly. - bodyList.push(); - bodyList.push(); - lastOffset = offset + safeSearchTerm.length; - } - bodyList.push(); - } - else { - while ((offset = originalBody.indexOf(this.props.searchTerm, lastOffset)) >= 0) { - bodyList.push({ originalBody.substring(lastOffset, offset) }); - bodyList.push({ this.props.searchTerm }); - lastOffset = offset + this.props.searchTerm.length; - } - bodyList.push({ originalBody.substring(lastOffset) }); - } - body = bodyList; - } - else { - if (content.format === "org.matrix.custom.html") { - var safeBody = sanitizeHtml(content.formatted_body, sanitizeHtmlParams); - body = ; - } - else { - body = originalBody; - } - } + var body = HtmlUtils.bodyToHtml(content, this.props.searchTerm); return (