From 8b925986ed27bf08a5c8d01aba4f57cddfe83479 Mon Sep 17 00:00:00 2001 From: Simon Grim Date: Fri, 10 Jul 2015 01:31:54 +0500 Subject: [PATCH] fix of post formatting, moving from regexps to achieve more flexibility --- js/twister_formatpost.js | 196 +++++++++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 60 deletions(-) diff --git a/js/twister_formatpost.js b/js/twister_formatpost.js index 84108c7..009488f 100644 --- a/js/twister_formatpost.js +++ b/js/twister_formatpost.js @@ -144,7 +144,7 @@ function postToElem( post, kind, promoted ) { function setPostInfoSent(n, k, item) { if( n === defaultScreenName && k >= 0 ) { - getPostMaxAvailability(n,k, + getPostMaxAvailability(n,k, function(args,count) { if( count >= 3 ) { // assume 3 peers (me + 2) is enough for "sent" args.item.text("\u2713"); // check mark @@ -198,74 +198,160 @@ function dmDataToConversationItem(dmData, localUser, remoteUser) { // convert message text to html, featuring @users and links formating. function htmlFormatMsg(msg, mentions) { - function htmlMention(str) { - str = str.slice(1, str.length).toLowerCase(); + function getStrStart(str, i, stopChars) { + 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] - // FIXME actually we should avoid it by dropping a template idea and construct html right here - html.push($('#msg-user-link-template')[0].outerHTML - .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') - //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') - .replace(/]*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))/ig, ']*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))[^]*?>)[^]*?(<\/a>)/ig, '$1@' + str + '$2') // $().closest('a.open-profile-modal').text('@'+username) - ); + function getStrEnd(str, i, stopChars) { + for (; i < str.length; i++) { + if (stopChars.indexOf(str[i]) > -1) + return i - 1; + } - return '>' + (html.length - 1).toString() + '<'; + return str.length; } - function htmlHashtag(str) { - str = str.slice(1, str.length); - - html.push($('#hashtag-link-template')[0].outerHTML - .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') - //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') - .replace(/]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))/ig, ']*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))[^]*?>)[^]*?(<\/a>)/ig, '$1#' + str + '$2') // $().closest('a.open-profile-modal').text('#'+hashtag) - ); + function extractStr(str, startPoint, endPoint, stopChars) { + for (var i = endPoint; i > startPoint - 1; i--) { + if (stopChars.indexOf(str[i]) === -1) { + endPoint = i; + break; + } + } - return '>' + (html.length - 1).toString() + '<'; + return str.slice(startPoint, endPoint + 1); } - function htmlHttp(str) { - html.push($('#external-page-link-template')[0].outerHTML - .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') - //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') - .replace(/)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url) - ); + function htmlSplitCounter(str) { + html.push('' + str + ''); return '>' + (html.length - 1).toString() + '<'; } - function htmlEmail(str) { - html.push($('#external-page-link-template')[0].outerHTML - .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') - //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') - .replace(/)[^]*?(<\/a>)/ig, '$1' + str + '$2') // $().closest('a').text(url) - ); + 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 = []; - return '>' + (html.length - 1).toString() + '<'; + 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 + .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') + //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') + .replace(/)[^]*?(<\/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; + } 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>)/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 htmlSplitCounter(str) { - html.push('' + 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 + .replace(/\bid\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('id') + //.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href') + .replace(/)[^]*?(<\/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; + } + } - return '>' + (html.length - 1).toString() + '<'; + 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(/]*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))/ig, ']*?\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; + } } - var html = []; + 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(/]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))/ig, ']*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))[^]*?>)[^]*?(<\/a>)/ig, '$1#' + str.replace(/&/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; + } + } - return _formatText(escapeHtmlEntities(msg) - .replace(/\bhttps?:\/\/[^>\s]+/ig, htmlHttp) - .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 - .replace(/@\w+\b/g, htmlMention) - .replace(/#[^#\\\/\.,:;\?\!\*\[\]\(\)\{\}\^\|%'"\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011>\s]+/g, htmlHashtag) // unicode escaped stuff is '“”…—一。,:?!【】' for our chinese friends + msg = msg .replace(/\(\d{1,2}\/\d{1,2}\)$/, htmlSplitCounter) - .replace(/>(\d+)') { + 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; + } + } + + if ($.Options.displayLineFeeds.val === 'enable') + msg = msg.replace(/\n/g, '
'); + + // TODO: add options for emotions; msg = $.emotions(msg); + // TODO: add at least basic markdown (optional) with syntax like this: + // *text* -> bold text + // ~text~ -> italic text + // _text_ -> underlined text + // -text- -> strikethrough text + + return msg; } function proxyURL(url) { @@ -282,19 +368,9 @@ function proxyURL(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, '
'); - - return msg; -} - function escapeHtmlEntities(str) { return str - .replace(/&/g, '&') + //.replace(/&/g, '&') we do it not here .replace(//g, '>') .replace(/"/g, '"')