Implemented basic text formatting package using a modfied version of the marked js library. Supports *bold*, _italics_, and code

Этот коммит содержится в:
Reed Garmsen
2015-07-08 11:34:34 -07:00
родитель 95e6626f9f
Коммит d293bc0b79
21 изменённых файлов: 2387 добавлений и 42 удалений

Просмотреть файл

@@ -156,7 +156,7 @@ module.exports = React.createClass({
}
var channel = this.state.channel;
var description = utils.textToJsx(channel.description, {singleline: true, noMentionHighlight: true});
var description = utils.textToJsx(channel.description, {singleline: true, noMentionHighlight: true, noTextFormatting: true});
var popoverContent = React.renderToString(<MessageWrapper message={channel.description}/>);
var channelTitle = channel.display_name;
var currentId = UserStore.getCurrentId();

Просмотреть файл

@@ -184,6 +184,7 @@ module.exports = React.createClass({
</div>
);
}
var allowTextFormatting = config.AllowTextFormatting;
var postError = null;
if (this.state.postError) {
@@ -204,6 +205,10 @@ module.exports = React.createClass({
if (postError) {
postFooterClassName += ' has-error';
}
var extraInfo = <MsgTyping channelId={this.props.channelId} parentId={this.props.rootId} />;
if (this.state.messageText.split(' ').length > 1 && allowTextFormatting) {
extraInfo = <span className='msg-format-help'>_<em>italics</em>_ *<strong>bold</strong>* `<code className='code-info'>code</code>`</span>;
}
return (
<form onSubmit={this.handleSubmit}>
@@ -224,7 +229,7 @@ module.exports = React.createClass({
onFileUpload={this.handleFileUploadComplete}
onUploadError={this.handleUploadError} />
</div>
<MsgTyping channelId={this.props.channelId} parentId={this.props.rootId} />
{extraInfo}
<div className={postFooterClassName}>
<input type='button' className='btn btn-primary comment-btn pull-right' value='Add Comment' onClick={this.handleSubmit} />
{postError}

Просмотреть файл

@@ -224,6 +224,7 @@ module.exports = React.createClass({
</div>
);
}
var allowTextFormatting = config.AllowTextFormatting;
var postError = null;
if (this.state.postError) {
@@ -244,6 +245,10 @@ module.exports = React.createClass({
if (postError) {
postFooterClassName += ' has-error';
}
var extraInfo = <MsgTyping channelId={this.state.channel_id} parentId='' />;
if (this.state.messageText.split(' ').length > 1 && allowTextFormatting) {
extraInfo = <span className='msg-typing'>_<em>italics</em>_ *<strong>bold</strong>* `<code className='code-info'>code</code>`</span>;
}
return (
<form id='create_post' ref='topDiv' role='form' onSubmit={this.handleSubmit}>
@@ -268,7 +273,7 @@ module.exports = React.createClass({
{postError}
{serverError}
{preview}
<MsgTyping channelId={this.state.channelId} parentId=''/>
{extraInfo}
</div>
</div>
</form>

Просмотреть файл

@@ -4,6 +4,7 @@
var FileAttachmentList = require('./file_attachment_list.jsx');
var UserStore = require('../stores/user_store.jsx');
var utils = require('../utils/utils.jsx');
var formatText = require('../../static/js/marked/lib/marked.js');
module.exports = React.createClass({
componentWillReceiveProps: function(nextProps) {
@@ -19,6 +20,7 @@ module.exports = React.createClass({
var filenames = this.props.post.filenames;
var parentPost = this.props.parentPost;
var inner = utils.textToJsx(this.state.message);
var allowTextFormatting = config.AllowTextFormatting;
var comment = "";
var reply = "";
@@ -50,11 +52,20 @@ module.exports = React.createClass({
}
}
comment = (
<p className="post-link">
<span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{message}</a></span>
</p>
);
if (allowTextFormatting) {
message = formatText(message, {sanitize: true, mangle: false, gfm: true, breaks: true, tables: false, smartypants: true, renderer: utils.customMarkedRenderer({disable: true})});
comment = (
<p className="post-link">
<span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick} dangerouslySetInnerHTML={{__html: message}} /></span>
</p>
);
} else {
comment = (
<p className="post-link">
<span>Commented on {name}{apostrophe} message: <a className="theme" onClick={this.props.handleCommentClick}>{message}</a></span>
</p>
);
}
postClass += " post-comment";
}
@@ -67,7 +78,11 @@ module.exports = React.createClass({
return (
<div className="post-body">
{ comment }
{allowTextFormatting ?
<div key={post.id+"_message"} className={postClass}><span>{inner}</span></div>
:
<p key={post.id+"_message"} className={postClass}><span>{inner}</span></p>
}
{ filenames && filenames.length > 0 ?
<FileAttachmentList
filenames={filenames}

Просмотреть файл

@@ -197,7 +197,10 @@ module.exports = React.createClass({
var post = post_list.posts[msg.props.post_id];
post.message = msg.props.message;
post.lastEditDate = Date.now();
post_list.posts[post.id] = post;
this.setState({ post_list: post_list });
PostStore.storePosts(msg.channel_id, post_list);
@@ -430,8 +433,12 @@ module.exports = React.createClass({
// it is the last comment if it is last post in the channel or the next post has a different root post
var isLastComment = utils.isComment(post) && (i === 0 || posts[order[i-1]].root_id != post.root_id);
var postKey = post.id;
if (post.lastEditDate != undefined)
postKey += post.lastEditDate;
var postCtl = (
<Post ref={post.id} sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={post.id}
<Post ref={post.id} sameUser={sameUser} sameRoot={sameRoot} post={post} parentPost={parentPost} key={postKey}
posts={posts} hideProfilePic={hideProfilePic} isLastComment={isLastComment}
/>
);

Просмотреть файл

@@ -56,6 +56,7 @@ RhsHeaderPost = React.createClass({
RootPost = React.createClass({
render: function() {
var allowTextFormatting = config.AllowTextFormatting;
var post = this.props.post;
var message = utils.textToJsx(post.message);
var isOwner = UserStore.getCurrentId() == post.user_id;
@@ -76,6 +77,11 @@ RootPost = React.createClass({
channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
}
var messageHolder = <p>{message}</p>;
if (allowTextFormatting) {
messageHolder = <div>{message}</div>;
}
return (
<div className={"post post--root " + currentUserCss}>
<div className="post-right-channel__name">{ channelName }</div>
@@ -101,7 +107,7 @@ RootPost = React.createClass({
</li>
</ul>
<div className="post-body">
<p>{message}</p>
{messageHolder}
{ post.filenames && post.filenames.length > 0 ?
<FileAttachmentList
filenames={post.filenames}
@@ -119,6 +125,7 @@ RootPost = React.createClass({
CommentPost = React.createClass({
render: function() {
var allowTextFormatting = config.AllowTextFormatting;
var post = this.props.post;
var commentClass = "post";
@@ -138,6 +145,11 @@ CommentPost = React.createClass({
var message = utils.textToJsx(post.message);
var timestamp = UserStore.getCurrentUser().update_at;
var messageHolder = <p>{message}</p>;
if (allowTextFormatting) {
messageHolder = <div>{message}</div>;
}
return (
<div className={commentClass + " " + currentUserCss}>
<div className="post-profile-img__container">
@@ -160,7 +172,7 @@ CommentPost = React.createClass({
</li>
</ul>
<div className="post-body">
<p>{message}</p>
{messageHolder}
{ post.filenames && post.filenames.length > 0 ?
<FileAttachmentList
filenames={post.filenames}
@@ -273,11 +285,20 @@ module.exports = React.createClass({
root_post = post_list.posts[selected_post.root_id];
}
var rootPostKey = root_post.id
if (root_post.lastEditDate != undefined)
rootPostKey += root_post.lastEditDate;
var posts_array = [];
for (var postId in post_list.posts) {
var cpost = post_list.posts[postId];
if (cpost.root_id == root_post.id) {
var cpostKey = cpost.id
if (cpost.lastEditDate != undefined)
cpostKey += cpost.lastEditDate;
cpost.cpostKey = cpostKey;
posts_array.push(cpost);
}
}
@@ -300,10 +321,10 @@ module.exports = React.createClass({
<div className="sidebar-right__body">
<RhsHeaderPost fromSearch={this.props.fromSearch} isMentionSearch={this.props.isMentionSearch} />
<div className="post-right__scroll">
<RootPost post={root_post} commentCount={posts_array.length}/>
<RootPost key={rootPostKey} post={root_post} commentCount={posts_array.length}/>
<div className="post-right-comments-container">
{ posts_array.map(function(cpost) {
return <CommentPost ref={cpost.id} key={cpost.id} post={cpost} selected={ (cpost.id == selected_post.id) } />
return <CommentPost ref={cpost.id} key={cpost.cpostKey} post={cpost} selected={ (cpost.id == selected_post.id) } />
})}
</div>
<div className="post-create__container">

Просмотреть файл

@@ -84,6 +84,8 @@ var SearchItem = React.createClass({
channelName = (channel.type === 'D') ? "Private Message" : channel.display_name;
}
var searchItemKey = Date.now().toString();
return (
<div className="search-item-container post" onClick={this.handleClick}>
<div className="search-channel__name">{ channelName }</div>
@@ -99,7 +101,7 @@ var SearchItem = React.createClass({
</time>
</li>
</ul>
<div className="search-item-snippet"><span>{message}</span></div>
<div key={this.props.key + searchItemKey} className="search-item-snippet"><span>{message}</span></div>
</div>
</div>
);
@@ -131,6 +133,7 @@ module.exports = React.createClass({
if (this.isMounted()) {
var newState = getStateFromStores();
if (!utils.areStatesEqual(newState, this.state)) {
newState.last_edit_time = Date.now();
this.setState(newState);
}
}
@@ -152,6 +155,11 @@ module.exports = React.createClass({
var noResults = (!results || !results.order || !results.order.length);
var searchTerm = PostStore.getSearchTerm();
var searchItemKey = "";
if (this.state.last_edit_time) {
searchItemKey += this.state.last_edit_time.toString();
}
return (
<div className="sidebar--right__content">
<div className="search-bar__container sidebar--right__search-header">{searchForm}</div>
@@ -162,7 +170,7 @@ module.exports = React.createClass({
{ noResults ? <div className="sidebar--right__subheader">No results</div>
: results.order.map(function(id) {
var post = results.posts[id];
return <SearchItem key={post.id} post={post} term={searchTerm} isMentionSearch={this.props.isMentionSearch} />
return <SearchItem key={searchItemKey + post.id} post={post} term={searchTerm} isMentionSearch={this.props.isMentionSearch} />
}, this)
}

Просмотреть файл

@@ -11,6 +11,7 @@ var BrowserStore = require('../stores/browser_store.jsx');
var utils = require('../utils/utils.jsx');
var SidebarHeader = require('./sidebar_header.jsx');
var SearchBox = require('./search_bar.jsx');
var formatText = require('../../static/js/marked/lib/marked.js');
var Constants = require('../utils/constants.jsx');
var ActionTypes = Constants.ActionTypes;
@@ -207,6 +208,11 @@ module.exports = React.createClass({
utils.notifyMe(title, username + ' did something new', channel);
}
} else {
var allowTextFormatting = config.AllowTextFormatting;
if (allowTextFormatting) {
notifyText = formatText(notifyText, {sanitize: false, mangle: false, gfm: true, breaks: true, tables: false, smartypants: true, renderer: utils.customMarkedRenderer({disable: true})});
}
notifyText = utils.replaceHtmlEntities(notifyText);
utils.notifyMe(title, username + ' wrote: ' + notifyText, channel);
}
if (!user.notify_props || user.notify_props.desktop_sound === 'true') {

Просмотреть файл

@@ -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();