Browse Source

fix of post formatting, moving from regexps to achieve more flexibility

master
Simon Grim 9 years ago
parent
commit
8b925986ed
  1. 172
      js/twister_formatpost.js

172
js/twister_formatpost.js

@ -198,74 +198,160 @@ 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 htmlMention(str) { function getStrStart(str, i, stopChars) {
str = str.slice(1, str.length).toLowerCase(); for (; i > -1; i--) {
if (stopChars.indexOf(str[i]) > -1)
return i + 1;
}
mentions.push(str); // FIXME feel the pain of the scope chain return 0;
}
// FIXME we're trying to not interact with DOM, coz' we want to run really fast [to hell of RegExps] function getStrEnd(str, i, stopChars) {
// FIXME actually we should avoid it by dropping a template idea and construct html right here for (; i < str.length; i++) {
html.push($('#msg-user-link-template')[0].outerHTML if (stopChars.indexOf(str[i]) > -1)
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') return i - 1;
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') }
.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)
);
return '>' + (html.length - 1).toString() + '<'; return str.length;
} }
function htmlHashtag(str) { function extractStr(str, startPoint, endPoint, stopChars) {
str = str.slice(1, str.length); for (var i = endPoint; i > startPoint - 1; i--) {
if (stopChars.indexOf(str[i]) === -1) {
endPoint = i;
break;
}
}
html.push($('#hashtag-link-template')[0].outerHTML return str.slice(startPoint, endPoint + 1);
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') }
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
.replace(/<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))/ig, '<a href="' + $.MAL.hashtagUrl(str.toLowerCase()) + '" ') // $().closest('a.open-profile-modal').attr('href', $.MAL.hashtagUrl(hashtag)) function htmlSplitCounter(str) {
.replace(/(<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))[^]*?>)[^]*?(<\/a>)/ig, '$1#' + str + '$2') // $().closest('a.open-profile-modal').text('#'+hashtag) html.push('<span class="splited-post-counter">' + str + '</span>');
);
return '>' + (html.length - 1).toString() + '<'; return '>' + (html.length - 1).toString() + '<';
} }
function htmlHttp(str) { var mentionsChars = 'abcdefghijklmnopqrstuvwxyz_0123456789';
var stopCharsTrailing = '/\\.,:;?!*%\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011';
var stopCharsTrailingUrl = stopCharsTrailing.slice(1);
var whiteSpaces = ' \f\n\r\t\v\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000';
var stopCharsRight = '>' + whiteSpaces;
var stopCharsLeft = '<' + whiteSpaces;
var j, str, strEncoded;
var html = [];
msg = escapeHtmlEntities(msg);
for (var i = 0; i < msg.length - 7; i++) {
if (msg.slice(i, i + 4).toLowerCase() === 'http') {
if (msg.slice(i + 4, i + 7) === '://' && stopCharsRight.indexOf(msg[i + 7]) === -1) {
str = extractStr(msg, i, getStrEnd(msg, i + 7, stopCharsRight), stopCharsTrailingUrl);
// FIXME we're trying to not interact with DOM, coz' we want to run really fast [to hell of RegExps]
// FIXME actually we should avoid it by dropping a template idea and construct html right here
html.push($('#external-page-link-template')[0].outerHTML html.push($('#external-page-link-template')[0].outerHTML
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id')
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
.replace(/<a\s+/ig, '<a href="' + proxyURL(str) + '" ') // $().closest('a').attr('href', proxyURL(url)) .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) .replace(/(<a\s+[^]*?>)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url)
); );
strEncoded = '>' + (html.length - 1).toString() + '<';
return '>' + (html.length - 1).toString() + '<'; 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) {
str = extractStr(msg, i, getStrEnd(msg, i + 8, stopCharsRight), stopCharsTrailingUrl);
html.push($('#external-page-link-template')[0].outerHTML
.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() + '<';
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length);
i = i + strEncoded.length - 1;
}
}
} }
function htmlEmail(str) { for (var i = 1; i < msg.length - 1; i++) {
if (msg[i] === '@' && stopCharsLeft.indexOf(msg[i - 1]) === -1
&& stopCharsTrailing.indexOf(msg[i - 1]) === -1 && stopCharsRight.indexOf(msg[i + 1]) === -1) {
j = getStrStart(msg, i, stopCharsLeft);
str = extractStr(msg, j, getStrEnd(msg, i + 1, stopCharsRight), stopCharsTrailing);
html.push($('#external-page-link-template')[0].outerHTML html.push($('#external-page-link-template')[0].outerHTML
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id')
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
.replace(/<a\s+/ig, '<a href="mailto:' + str.toLowerCase() + '" ') // $().closest('a').attr('href', 'mailto:'+url) .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) .replace(/(<a\s+[^]*?>)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url)
); );
strEncoded = '>' + (html.length - 1).toString() + '<';
msg = msg.slice(0, j) + strEncoded + msg.slice(j + str.length);
i = j + strEncoded.length - 1;
}
}
for (var i = 0; i < msg.length - 1; i++) {
if (msg[i] === '@' && mentionsChars.indexOf(msg[i + 1].toLowerCase()) > -1) {
for (j = i + 2; j < msg.length; j++) {
if (mentionsChars.indexOf(msg[j].toLowerCase()) === -1)
break;
}
str = msg.slice(i + 1, j).toLowerCase();
mentions.push(str); // FIXME
html.push($('#msg-user-link-template')[0].outerHTML
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id')
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
.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() + '<';
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length + 1);
i = i + strEncoded.length - 1;
}
}
return '>' + (html.length - 1).toString() + '<'; for (var i = 0; i < msg.length - 1; i++) {
j = i + 1;
if (msg[i] === '#' && msg[j] !== '#' && stopCharsRight.indexOf(msg[j]) === -1) {
str = extractStr(msg, j, getStrEnd(msg, j, '/' + stopCharsRight), stopCharsTrailing);
html.push($('#hashtag-link-template')[0].outerHTML
.replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id')
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
.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() + '<';
msg = msg.slice(0, i) + strEncoded + msg.slice(i + str.length + 1);
i = i + strEncoded.length - 1;
}
} }
function htmlSplitCounter(str) { msg = msg
html.push('<span class="splited-post-counter">' + str + '</span>'); .replace(/\(\d{1,2}\/\d{1,2}\)$/, htmlSplitCounter)
.replace(/&(?!lt;|gt;|quot;|apos;)/g, '&amp;') // FIXME in many cases there is no need to escape ampersand in HTML 5
;
return '>' + (html.length - 1).toString() + '<'; for (var i = 0; i < msg.length - 2; i++) {
if (msg[i] === '>') {
j = getStrEnd(msg, i + 2, '<');
str = html[parseInt(msg.slice(i + 1, j + 1))];
msg = msg.slice(0, i) + str + msg.slice(j + 2);
i = i + str.length - 1;
}
} }
var html = []; if ($.Options.displayLineFeeds.val === 'enable')
msg = msg.replace(/\n/g, '<br />');
return _formatText(escapeHtmlEntities(msg) // TODO: add options for emotions; msg = $.emotions(msg);
.replace(/\bhttps?:\/\/[^>\s]+/ig, htmlHttp) // TODO: add at least basic markdown (optional) with syntax like this:
.replace(/[^<\s]+@\S+\.[^>\s]+/g, htmlEmail) // should be pretty slow monstrous RegExp here to handle it in accordance with RFC but twisters just gonna avoid text_without_spaces@text_with_._but_without spaces to not get it like email address // *text* -> bold text
.replace(/@\w+\b/g, htmlMention) // ~text~ -> italic text
.replace(/#[^#\\\/\.,:;\?\!\*\[\]\(\)\{\}\^\|%'"\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011>\s]+/g, htmlHashtag) // unicode escaped stuff is '“”…—一。,:?!【】' for our chinese friends // _text_ -> underlined text
.replace(/\(\d{1,2}\/\d{1,2}\)$/, htmlSplitCounter) // -text- -> strikethrough text
.replace(/>(\d+)</g, function(candy, core) {return html[core]})
); return msg;
} }
function proxyURL(url) { function proxyURL(url) {
@ -282,19 +368,9 @@ function proxyURL(url) {
return url; return url;
} }
// internal function for htmlFormatMsg
// TODO: add options for emotions; msg = $.emotions(msg);
// TODO: add at least basic markdown (optional) like *text* -> bold text and _text_ -> italic text
function _formatText(msg) {
if ($.Options.displayLineFeeds.val === 'enable')
msg = msg.replace(/\n/g, '<br />');
return msg;
}
function escapeHtmlEntities(str) { function escapeHtmlEntities(str) {
return str return str
.replace(/&/g, '&amp;') //.replace(/&/g, '&amp;') we do it not here
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;') .replace(/>/g, '&gt;')
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')

Loading…
Cancel
Save