Browse Source

fix of post formatting: optimizing

master
Simon Grim 9 years ago
parent
commit
fb67d7a04d
  1. 330
      js/twister_formatpost.js

330
js/twister_formatpost.js

@ -3,6 +3,19 @@
// //
// Format JSON posts and DMs to HTML. // Format JSON posts and DMs to HTML.
var _htmlFormatMsgLinkTemplateExternal;
var _htmlFormatMsgLinkTemplateUser;
var _htmlFormatMsgLinkTemplateHashtag;
$(document).ready(function() {
// we're setting it here for perfomance improvement purpose // to not search and prepare it for for every post
_htmlFormatMsgLinkTemplateExternal = $('#external-page-link-template')[0].cloneNode();
_htmlFormatMsgLinkTemplateExternal.removeAttribute('id');
_htmlFormatMsgLinkTemplateUser = $('#msg-user-link-template')[0].cloneNode();
_htmlFormatMsgLinkTemplateUser.removeAttribute('id');
_htmlFormatMsgLinkTemplateHashtag = $('#hashtag-link-template')[0].cloneNode();
_htmlFormatMsgLinkTemplateHashtag.removeAttribute('id');
});
// format "userpost" to html element // format "userpost" to html element
// kind = "original"/"ancestor"/"descendant" // kind = "original"/"ancestor"/"descendant"
@ -198,7 +211,10 @@ function dmDataToConversationItem(dmData, localUser, remoteUser) {
// convert message text to html, featuring @users and links formating. // convert message text to html, featuring @users and links formating.
function htmlFormatMsg(msg, mentions) { function htmlFormatMsg(msg, mentions) {
function getStrStart(str, startPoint, stopChars, isStopCharMustExist, stopCharsTrailing) { // TODO: add options for emotions; msg = $.emotions(msg);
// TODO make markup optionally mutable ?
function getSubStrStart(str, startPoint, stopChars, isStopCharMustExist, stopCharsTrailing) {
for (var i = startPoint; i > -1; i--) { for (var i = startPoint; i > -1; i--) {
if (stopChars.indexOf(str[i]) > -1) if (stopChars.indexOf(str[i]) > -1)
break; break;
@ -215,7 +231,7 @@ function htmlFormatMsg(msg, mentions) {
return i; return i;
} }
function getStrEnd(str, startPoint, stopChars, isStopCharMustExist, stopCharsTrailing) { function getSubStrEnd(str, startPoint, stopChars, isStopCharMustExist, stopCharsTrailing) {
for (var i = startPoint; i < str.length; i++) { for (var i = startPoint; i < str.length; i++) {
if (stopChars.indexOf(str[i]) > -1) if (stopChars.indexOf(str[i]) > -1)
break; break;
@ -232,8 +248,8 @@ function htmlFormatMsg(msg, mentions) {
return i; return i;
} }
function markdown(str, chr, tag) { function markout(msg, chr, tag) {
function whiteSpace(i, j) { function isWhiteSpacesBetween(i, j) {
j++; j++;
for (i += 1; i < j; i++) { for (i += 1; i < j; i++) {
if (p[i].w) if (p[i].w)
@ -243,50 +259,50 @@ function htmlFormatMsg(msg, mentions) {
return false; return false;
} }
var i, j, t, l, r, strEncoded; var i, j, t, l, r, htmlEntityEncoded;
var w = false; var w = false;
var p = []; var p = [];
// collecting chars position data // collecting chars position data
for (i = 0; i < str.length; i++) { for (i = 0; i < msg.str.length; i++) {
if (str[i] === chr) { if (msg.str[i] === chr) {
for (j = i + 1; j < str.length; j++) { for (j = i + 1; j < msg.str.length; j++) {
if (str[j] !== chr) if (msg.str[j] !== chr)
break; break;
} }
if (i === 0) { if (i === 0) {
p.push({i: i, k: j - i, t: -1, w: w, a: -1, p: -1}); p.push({i: i, k: j - i, t: -1, w: w, a: -1, p: -1});
w = false; w = false;
} else if (j === str.length) { } else if (j === msg.str.length) {
p.push({i: i, k: j - i, t: 1, w: w, a: -1, p: -1}); p.push({i: i, k: j - i, t: 1, w: w, a: -1, p: -1});
w = false; w = false;
} else { } else {
if (stopCharsMarkDown.indexOf(str[i - 1]) > -1) { if (stopCharsMarkout.indexOf(msg.str[i - 1]) > -1) {
l = 1; l = 1;
for (t = i - 2; t > -1; t--) { for (t = i - 2; t > -1; t--) {
if (str[t] === chr) { if (msg.str[t] === chr) {
l = -1; l = -1;
break; break;
} else if (stopCharsMarkDown.indexOf(str[t]) === -1) { } else if (stopCharsMarkout.indexOf(msg.str[t]) === -1) {
l = whiteSpaces.indexOf(str[t]); l = whiteSpaces.indexOf(msg.str[t]);
break; break;
} }
} }
} else } else
l = whiteSpaces.indexOf(str[i - 1]); l = whiteSpaces.indexOf(msg.str[i - 1]);
if (stopCharsMarkDown.indexOf(str[j]) > -1) { if (stopCharsMarkout.indexOf(msg.str[j]) > -1) {
r = 1; r = 1;
for (t = j + 1; t < str.length; t++) { for (t = j + 1; t < msg.str.length; t++) {
if (str[t] === chr) { if (msg.str[t] === chr) {
r = -1; r = -1;
break; break;
} else if (stopCharsMarkDown.indexOf(str[t]) === -1) { } else if (stopCharsMarkout.indexOf(msg.str[t]) === -1) {
r = whiteSpaces.indexOf(str[t]); r = whiteSpaces.indexOf(msg.str[t]);
break; break;
} }
} }
} else } else
r = whiteSpaces.indexOf(str[j]); r = whiteSpaces.indexOf(msg.str[j]);
if (l > -1) { if (l > -1) {
if (r > -1) { if (r > -1) {
if (j - i > 2) { if (j - i > 2) {
@ -307,7 +323,7 @@ function htmlFormatMsg(msg, mentions) {
w = false; w = false;
} }
i = j - 1; i = j - 1;
} else if (!w && whiteSpaces.indexOf(str[i]) > -1) { } else if (!w && whiteSpaces.indexOf(msg.str[i]) > -1) {
w = true; w = true;
} }
} }
@ -317,7 +333,7 @@ function htmlFormatMsg(msg, mentions) {
if (p[i].t < 1 && p[i].a === -1) { if (p[i].t < 1 && p[i].a === -1) {
t = i; t = i;
for (j = i + 1; j < p.length; j++) { for (j = i + 1; j < p.length; j++) {
if (p[i].t === 0 && whiteSpace(i, j)) { if (p[i].t === 0 && isWhiteSpacesBetween(i, j)) {
i = j - 1; i = j - 1;
break; break;
} else if (p[j].t < 1 && p[j].a === -1) { } else if (p[j].t < 1 && p[j].a === -1) {
@ -335,11 +351,11 @@ function htmlFormatMsg(msg, mentions) {
for (i = 0; i < p.length; i++) { for (i = 0; i < p.length; i++) {
if (p[i].t === -1 && p[i].a === -1) { if (p[i].t === -1 && p[i].a === -1) {
for (j = p[i].p; j > -1; j = p[j].p) { for (j = p[i].p; j > -1; j = p[j].p) {
if (whiteSpace(i, j)) { if (isWhiteSpacesBetween(i, j)) {
i = j - 1; i = j - 1;
break; break;
} else if (p[j].t === 0 } else if (p[j].t === 0
&& !(p[j].p > -1 && p[p[j].p].t === 0 && !whiteSpace(j, p[j].p))) { && !(p[j].p > -1 && p[p[j].p].t === 0 && !isWhiteSpacesBetween(j, p[j].p))) {
p[j].a = i; p[j].a = i;
p[i].a = j; p[i].a = j;
i = j; i = j;
@ -359,15 +375,15 @@ function htmlFormatMsg(msg, mentions) {
else else
t = '<' + tag + '>'; t = '<' + tag + '>';
j = p[i].a; j = p[i].a;
t = t + str.slice(p[i].i + p[i].k, p[j].i); t = t + msg.str.slice(p[i].i + p[i].k, p[j].i);
if (p[j].k > 1) if (p[j].k > 1)
t = t + Array(p[i].k).join(chr) + '</' + tag + '>'; t = t + Array(p[i].k).join(chr) + '</' + tag + '>';
else else
t = t + '</' + tag + '>'; t = t + '</' + tag + '>';
html.push(t.replace(/&(?!lt;|gt;)/g, '&amp;')); msg.htmlEntities.push(t.replace(/&(?!lt;|gt;)/g, '&amp;'));
strEncoded = '>' + (html.length - 1).toString() + '<'; htmlEntityEncoded = '>' + (msg.htmlEntities.length - 1).toString() + '<';
str = str.slice(0, p[i].i) + strEncoded + str.slice(p[j].i + p[j].k); msg.str = msg.str.slice(0, p[i].i) + htmlEntityEncoded + msg.str.slice(p[j].i + p[j].k);
l = strEncoded.length - p[j].i - p[j].k + p[i].i; l = htmlEntityEncoded.length - p[j].i - p[j].k + p[i].i;
i = j; i = j;
for (j += 1; j < p.length; j++) for (j += 1; j < p.length; j++)
p[j].i += l; p[j].i += l;
@ -379,49 +395,59 @@ function htmlFormatMsg(msg, mentions) {
if (p[i].a > -1) { if (p[i].a > -1) {
if (p[i].t === -1 || (p[i].t === 0 && p[i].a > i)) { if (p[i].t === -1 || (p[i].t === 0 && p[i].a > i)) {
if (p[i].k > 1) if (p[i].k > 1)
html.push('<' + tag + '>' + Array(p[i].k).join(chr)); msg.htmlEntities.push('<' + tag + '>' + Array(p[i].k).join(chr));
else else
html.push('<' + tag + '>'); msg.htmlEntities.push('<' + tag + '>');
} else if (p[i].t === 1 || (p[i].t === 0 && p[i].a < i)) { } else if (p[i].t === 1 || (p[i].t === 0 && p[i].a < i)) {
if (p[i].k > 1) if (p[i].k > 1)
html.push(Array(p[i].k).join(chr) + '</' + tag + '>'); msg.htmlEntities.push(Array(p[i].k).join(chr) + '</' + tag + '>');
else else
html.push('</' + tag + '>'); msg.htmlEntities.push('</' + tag + '>');
} }
strEncoded = '>' + (html.length - 1).toString() + '<'; htmlEntityEncoded = '>' + (msg.htmlEntities.length - 1).toString() + '<';
str = str.slice(0, p[i].i) + strEncoded + str.slice(p[i].i + p[i].k); msg.str = msg.str.slice(0, p[i].i) + htmlEntityEncoded + msg.str.slice(p[i].i + p[i].k);
l = strEncoded.length - p[i].k; l = htmlEntityEncoded.length - p[i].k;
for (j = i + 1; j < p.length; j++) for (j = i + 1; j < p.length; j++)
p[j].i += l; p[j].i += l;
} }
} }
} }
return str; return msg;
}
function newHtmlEntityLink(template, urlTarget, urlName) {
template.href = urlTarget;
template.innerHTML = urlName; // .innerHTML instead .text to allow markup inside [] of [url name](target)
return template.outerHTML;
} }
function htmlSplitCounter(str) { function msgAddHtmlEntity(msg, strSliceLeft, strSliceRigth, htmlEntity) {
html.push('<span class="splited-post-counter">' + str + '</span>'); msg.htmlEntities.push(htmlEntity);
var htmlEntityEncoded = '>' + (msg.htmlEntities.length - 1).toString() + '<';
msg.str = msg.str.slice(0, strSliceLeft) + htmlEntityEncoded + msg.str.slice(strSliceRigth);
msg.i = strSliceLeft + htmlEntityEncoded.length - 1;
return '>' + (html.length - 1).toString() + '<'; return msg;
} }
function unpackHtml(str) { function applyHtml(msg) {
var t; var t;
for (var i = 0; i < str.length - 2; i++) { for (var i = 0; i < msg.str.length - 2; i++) {
if (str[i] === '>') { if (msg.str[i] === '>') {
for (var j = i + 2; j < str.length; j++) { for (var j = i + 2; j < msg.str.length; j++) {
if (str[j] === '<') if (msg.str[j] === '<')
break; break;
} }
t = html[parseInt(str.slice(i + 1, j))]; t = msg.htmlEntities[parseInt(msg.str.slice(i + 1, j))];
str = str.slice(0, i) + t + str.slice(j + 1); msg.str = msg.str.slice(0, i) + t + msg.str.slice(j + 1);
i = i + t.length - 1; i = i + t.length - 1;
} }
} }
return str; return msg.str;
} }
var mentionsChars = 'abcdefghijklmnopqrstuvwxyz_0123456789'; var mentionsChars = 'abcdefghijklmnopqrstuvwxyz_0123456789';
@ -433,178 +459,164 @@ function htmlFormatMsg(msg, mentions) {
var stopCharsRight = '>' + whiteSpaces; var stopCharsRight = '>' + whiteSpaces;
var stopCharsRightHashtags = '>/\\.,:;?!%\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011\u2047\u2048\u2049' // same as stopCharsTrailing but without '*~_-`' plus '>' var stopCharsRightHashtags = '>/\\.,:;?!%\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011\u2047\u2048\u2049' // same as stopCharsTrailing but without '*~_-`' plus '>'
+ whiteSpaces; + whiteSpaces;
var stopCharsMarkDown = '/\\*~_-`.,:;?!%+=&\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011\u2047\u2048\u2049'; var stopCharsMarkout = '/\\*~_-`.,:;?!%+=&\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011\u2047\u2048\u2049';
var i, j, k, str, strEncoded; var i, j, k, str;
var html = [];
msg = {str: escapeHtmlEntities(msg), htmlEntities: []};
msg = markdown(escapeHtmlEntities(msg), msg = markout(msg, '`', 'samp'); // <samp> tag is kind of monospace, here sequence of chars inside it will be escaped from markup
'`', 'samp'); // kind of monospace, sequence of chars inside will be escaped from markup
for (i = 0; i < msg.length - 7; i++) { // handling links
if (msg.slice(i, i + 2) === '](') { for (i = 0; i < msg.str.length - 7; i++) {
// FIXME there can be text with [] inside [] or links with () wee need to handle it too if (msg.str.slice(i, i + 2) === '](') {
j = getStrStart(msg, i - 2, '[', true, ''); // FIXME there can be text with [] inside [] or links with () we need to handle it too
j = getSubStrStart(msg.str, i - 2, '[', true, '');
if (j < i - 1) { if (j < i - 1) {
k = getStrEnd(msg, i + 3, ')', true, whiteSpaces); k = getSubStrEnd(msg.str, i + 3, ')', true, whiteSpaces);
if (k > i + 2) { if (k > i + 2) {
strEncoded = msg.slice(j, i); // just temporary storage for name of possiible link var linkName = msg.str.slice(j, i); // name of possiible link
for (i += 2; i < k; i++) { for (i += 2; i < k; i++) {
if (whiteSpacesUrl.indexOf(msg[i]) === -1) // drop whitespaces and ' and " // apostrophes and quotes to prevent injection of js events if (whiteSpacesUrl.indexOf(msg.str[i]) === -1) // drop whitespaces and ' and " // apostrophes and quotes to prevent injection of js events
break; break;
} }
if (i < k) { if (i < k) {
// check for injections like [href=(' or ")]('#' or 'javascript' or 'data')[:] // () and [] here just to explain somehow an idea, it's not about markup syntax var x = getSubStrEnd(msg.str, i, ':', false, '') + 1;
if (msg[i] === '#' || msg.slice(i, i + 10).toLowerCase() === 'javascript' // following check is NOT for real protection (we have blocking CSP rule instead), it's just to aware people
|| msg.slice(i, i + 4).toLowerCase() === 'data') { if (x > i && x < k && (msg.str.slice(x - 6, x).toLowerCase() === 'script' // other things would be added when W3C and all the people invent it
html.push('…<br><b><i>' + polyglot.t('busted_oh') + '</i> ' || msg.str.slice(x - 4, x).toLowerCase() === 'data')) {
msg = msgAddHtmlEntity(msg, j - 1, getSubStrEnd(msg.str, k + 1, ')', true, '') + 2,
'…<br><b><i>' + polyglot.t('busted_oh') + '</i> '
+ polyglot.t('busted_avowal') + ':</b><br><samp>' + polyglot.t('busted_avowal') + ':</b><br><samp>'
+ msg.slice(i, k + 1) + msg.str.slice(i, k + 1)
.replace(/&(?!lt;|gt;)/g, '&amp;') .replace(/&(?!lt;|gt;)/g, '&amp;')
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')
.replace(/'/g, '&apos;') .replace(/'/g, '&apos;')
+ '</samp><br>' + '</samp><br>'
); );
} else { } else {
if (getStrEnd(msg, i + 1, whiteSpacesUrl, false, '') < k) // use only first word as href target, others drop silently if (getSubStrEnd(msg.str, i + 1, whiteSpacesUrl, false, '') < k) // use only first word as href target, others drop silently
k = getStrEnd(msg, i + 1, whiteSpacesUrl, false, ''); k = getSubStrEnd(msg.str, i + 1, whiteSpacesUrl, false, '');
html.push($('#external-page-link-template')[0].outerHTML msg = msgAddHtmlEntity(msg, j - 1, getSubStrEnd(msg.str, k + 1, ')', true, '') + 2,
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') newHtmlEntityLink(_htmlFormatMsgLinkTemplateExternal,
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') proxyURL(msg.str.slice(i, k + 1)),
.replace(/<a\s+/ig, '<a href="' + proxyURL(msg.slice(i, k + 1)) + '" ') // $().closest('a').attr('href', proxyURL(url)) applyHtml( // we're trying markup inside [] of []()
.replace(/(<a\s+[^]*?>)[^]*?(<\/a>)/ig, '$1' markout(markout(markout(markout(
+ unpackHtml( {str: linkName, htmlEntities: msg.htmlEntities},
markdown(markdown(markdown(markdown(strEncoded, // name of link, see above '*', 'b'), // bold
'*', 'b'), // bold '~', 'i'), // italic
'~', 'i'), // italic '_', 'u'), // underlined
'_', 'u'), // underlined '-', 's') // striketrough
'-', 's') // striketrough
.replace(/&(?!lt;|gt;)/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;')
) )
+ '$2') // $().closest('a').text(url) .replace(/&(?!lt;|gt;)/g, '&amp;')
)
); );
} }
strEncoded = '>' + (html.length - 1).toString() + '<'; i = msg.i + 1;
msg = msg.slice(0, j - 1) + strEncoded + msg.slice(getStrEnd(msg, k + 1, ')', true, '') + 2); }
i = j + strEncoded.length - 1;
} // else
// i = just no
} }
} }
} else if (msg.slice(i, i + 4).toLowerCase() === 'http') { } else if (msg.str.slice(i, i + 4).toLowerCase() === 'http') {
if (msg.slice(i + 4, i + 7) === '://' && stopCharsRight.indexOf(msg[i + 7]) === -1) { if (msg.str.slice(i + 4, i + 7) === '://' && stopCharsRight.indexOf(msg.str[i + 7]) === -1) {
j = getStrEnd(msg, i + 7, stopCharsRight, false, stopCharsTrailingUrl); j = getSubStrEnd(msg.str, i + 7, stopCharsRight, false, stopCharsTrailingUrl);
if (j > i + 6) { if (j > i + 6) {
str = msg.slice(i, j + 1); str = msg.str.slice(i, j + 1);
// FIXME we're trying to not interact with DOM, coz' we want to run really fast [to hell of RegExps] msg = msgAddHtmlEntity(msg, i, i + str.length,
// FIXME actually we should avoid it by dropping a template idea and construct html right here newHtmlEntityLink(_htmlFormatMsgLinkTemplateExternal,
html.push($('#external-page-link-template')[0].outerHTML proxyURL(str), str)
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id')
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
.replace(/<a\s+/ig, '<a href="' + proxyURL(str) + '" ') // $().closest('a').attr('href', proxyURL(url))
.replace(/(<a\s+[^]*?>)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url)
); );
strEncoded = '>' + (html.length - 1).toString() + '<'; i = msg.i;
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length);
i = i + strEncoded.length - 1;
} }
} else if (msg.slice(i + 4, i + 8).toLowerCase() === 's://' && stopCharsRight.indexOf(msg[i + 8]) === -1) { } else if (msg.str.slice(i + 4, i + 8).toLowerCase() === 's://' && stopCharsRight.indexOf(msg.str[i + 8]) === -1) {
j = getStrEnd(msg, i + 8, stopCharsRight, false, stopCharsTrailingUrl); j = getSubStrEnd(msg.str, i + 8, stopCharsRight, false, stopCharsTrailingUrl);
if (j > i + 7) { if (j > i + 7) {
str = msg.slice(i, j + 1); str = msg.str.slice(i, j + 1);
html.push($('#external-page-link-template')[0].outerHTML msg = msgAddHtmlEntity(msg, i, i + str.length,
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') newHtmlEntityLink(_htmlFormatMsgLinkTemplateExternal,
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') proxyURL(str), str)
.replace(/<a\s+/ig, '<a href="' + proxyURL(str) + '" ') // $().closest('a').attr('href', proxyURL(url))
.replace(/(<a\s+[^]*?>)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url)
); );
strEncoded = '>' + (html.length - 1).toString() + '<'; i = msg.i;
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length);
i = i + strEncoded.length - 1;
} }
} }
} }
} }
for (i = 1; i < msg.length - 1; i++) { // handling mails
if (msg[i] === '@' && stopCharsLeft.indexOf(msg[i - 1]) === -1 for (i = 1; i < msg.str.length - 1; i++) {
&& stopCharsTrailing.indexOf(msg[i - 1]) === -1 && stopCharsRight.indexOf(msg[i + 1]) === -1) { if (msg.str[i] === '@' && stopCharsLeft.indexOf(msg.str[i - 1]) === -1
j = getStrStart(msg, i - 1, stopCharsLeft, false, stopCharsTrailing); && stopCharsTrailing.indexOf(msg.str[i - 1]) === -1 && stopCharsRight.indexOf(msg.str[i + 1]) === -1) {
j = getSubStrStart(msg.str, i - 1, stopCharsLeft, false, stopCharsTrailing);
if (j < i) { if (j < i) {
k = getStrEnd(msg, i + 1, stopCharsRight, false, stopCharsTrailing); k = getSubStrEnd(msg.str, i + 1, stopCharsRight, false, stopCharsTrailing);
if (k > i) { if (k > i) {
str = msg.slice(j, k + 1); str = msg.str.slice(j, k + 1);
html.push($('#external-page-link-template')[0].outerHTML msg = msgAddHtmlEntity(msg, j, j + str.length,
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') newHtmlEntityLink(_htmlFormatMsgLinkTemplateExternal,
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') 'mailto:' + str.toLowerCase(), str)
.replace(/<a\s+/ig, '<a href="mailto:' + str.toLowerCase() + '" ') // $().closest('a').attr('href', 'mailto:'+url)
.replace(/(<a\s+[^]*?>)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url)
); );
strEncoded = '>' + (html.length - 1).toString() + '<'; i = msg.i;
msg = msg.slice(0, j) + strEncoded + msg.slice(j + str.length);
i = j + strEncoded.length - 1;
} }
} }
} }
} }
for (i = 0; i < msg.length - 1; i++) { // handling mentions
if (msg[i] === '@' && mentionsChars.indexOf(msg[i + 1].toLowerCase()) > -1) { for (i = 0; i < msg.str.length - 1; i++) {
for (j = i + 2; j < msg.length; j++) { if (msg.str[i] === '@' && mentionsChars.indexOf(msg.str[i + 1].toLowerCase()) > -1) {
if (mentionsChars.indexOf(msg[j].toLowerCase()) === -1) for (j = i + 2; j < msg.str.length; j++) {
if (mentionsChars.indexOf(msg.str[j].toLowerCase()) === -1)
break; break;
} }
str = msg.slice(i + 1, j).toLowerCase(); str = msg.str.slice(i + 1, j).toLowerCase();
mentions.push(str); // FIXME mentions.push(str); // FIXME
html.push($('#msg-user-link-template')[0].outerHTML msg = msgAddHtmlEntity(msg, i, i + str.length + 1,
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') newHtmlEntityLink(_htmlFormatMsgLinkTemplateUser,
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') $.MAL.userUrl(str), '@' + str)
.replace(/<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))/ig, '<a href="' + $.MAL.userUrl(str) + '" ') // $().closest('a.open-profile-modal').attr('href', $.MAL.userUrl(username))
.replace(/(<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))[^]*?>)[^]*?(<\/a>)/ig, '$1@' + str + '$2') // $().closest('a.open-profile-modal').text('@'+username)
); );
strEncoded = '>' + (html.length - 1).toString() + '<'; i = msg.i;
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length + 1);
i = i + strEncoded.length - 1;
} }
} }
for (i = 0; i < msg.length - 1; i++) { // handling hashtags
if (msg[i] === '#' && msg[i + 1] !== '#' && stopCharsRight.indexOf(msg[i + 1]) === -1) { for (i = 0; i < msg.str.length - 1; i++) {
j = getStrEnd(msg, i + 1, stopCharsRightHashtags, false, stopCharsTrailing); if (msg.str[i] === '#' && msg.str[i + 1] !== '#' && stopCharsRight.indexOf(msg.str[i + 1]) === -1) {
j = getSubStrEnd(msg.str, i + 1, stopCharsRightHashtags, false, stopCharsTrailing);
if (j > i) { if (j > i) {
str = msg.slice(i + 1, j + 1); str = msg.str.slice(i + 1, j + 1);
html.push($('#hashtag-link-template')[0].outerHTML msg = msgAddHtmlEntity(msg, i, i + str.length + 1,
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') newHtmlEntityLink(_htmlFormatMsgLinkTemplateHashtag,
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') $.MAL.hashtagUrl(encodeURIComponent(str.toLowerCase())), '#' + str.replace(/&amp;/g, '&'))
.replace(/<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))/ig, '<a href="' + $.MAL.hashtagUrl(encodeURIComponent(str.toLowerCase())) + '" ') // $().closest('a.open-profile-modal').attr('href', $.MAL.hashtagUrl(hashtag))
.replace(/(<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))[^]*?>)[^]*?(<\/a>)/ig, '$1#' + str.replace(/&amp;/g, '&') + '$2') // $().closest('a.open-profile-modal').text('#'+hashtag)
); );
strEncoded = '>' + (html.length - 1).toString() + '<'; i = msg.i;
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length + 1);
i = i + strEncoded.length - 1;
} }
} }
} }
msg = unpackHtml( // handling text style markup
markdown(markdown(markdown(markdown(msg, msg = markout(markout(markout(markout(msg,
'*', 'b'), // bold '*', 'b'), // bold
'~', 'i'), // italic '~', 'i'), // italic
'_', 'u'), // underlined '_', 'u'), // underlined
'-', 's') // striketrough '-', 's') // striketrough
.replace(/\(\d{1,2}\/\d{1,2}\)$/, htmlSplitCounter) ;
// handling splitted posts numbering and escaping ampersands, qoutes and apostrophes
msg.str = msg.str
.replace(/\(\d{1,2}\/\d{1,2}\)$/, function (str) {
msg.htmlEntities.push('<span class="splited-post-counter">' + str + '</span>'); // FIXME
return '>' + (msg.htmlEntities.length - 1).toString() + '<';
})
.replace(/&(?!lt;|gt;)/g, '&amp;') // FIXME in many cases there is no need to escape ampersand in HTML 5 .replace(/&(?!lt;|gt;)/g, '&amp;') // FIXME in many cases there is no need to escape ampersand in HTML 5
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')
.replace(/'/g, '&apos;') .replace(/'/g, '&apos;')
);
// applying html entities to msg.str and converting msg to string back
msg = applyHtml(msg);
// handling linebreaks
if ($.Options.displayLineFeeds.val === 'enable') if ($.Options.displayLineFeeds.val === 'enable')
msg = msg.replace(/\n/g, '<br />'); msg = msg.replace(/\n/g, '<br />');
// TODO: add options for emotions; msg = $.emotions(msg);
// TODO make markdown optionally mutable ?
return msg; return msg;
} }

Loading…
Cancel
Save