@ -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 ) {
html . push ( $ ( '#hashtag-link-template' ) [ 0 ] . outerHTML
endPoint = i ;
. replace ( /\bid\s*=\s*"[^]*?"+/ig , '' ) // $().removeAttr('id')
break ;
//.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))
}
. replace ( /(<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))[^]*?>)[^]*?(<\/a>)/ig , '$1#' + str + '$2' ) // $().closest('a.open-profile-modal').text('#'+hashtag)
) ;
return '>' + ( html . length - 1 ) . toString ( ) + '<' ;
return str . slice ( startPoint , endPoint + 1 ) ;
}
}
function htmlHttp ( str ) {
function htmlSplitCounter ( str ) {
html . push ( $ ( '#external-page-link-template' ) [ 0 ] . outerHTML
html . push ( '<span class="splited-post-counter">' + str + '</span>' ) ;
. 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)
) ;
return '>' + ( html . length - 1 ) . toString ( ) + '<' ;
return '>' + ( html . length - 1 ) . toString ( ) + '<' ;
}
}
function htmlEmail ( str ) {
var mentionsChars = 'abcdefghijklmnopqrstuvwxyz_0123456789' ;
html . push ( $ ( '#external-page-link-template' ) [ 0 ] . outerHTML
var stopCharsTrailing = '/\\.,:;?!*%\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011' ;
. replace ( /\bid\s*=\s*"[^]*?"+/ig , '' ) // $().removeAttr('id')
var stopCharsTrailingUrl = stopCharsTrailing . slice ( 1 ) ;
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
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' ;
. replace ( /<a\s+/ig , '<a href="mailto:' + str . toLowerCase ( ) + '" ' ) // $().closest('a').attr('href', 'mailto:'+url)
var stopCharsRight = '>' + whiteSpaces ;
. replace ( /(<a\s+[^]*?>)[^]*?(<\/a>)/ig , '$1' + str + '$2' ) // $().closest('a').text(url)
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\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 ;
} 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 htmlSplitCounter ( str ) {
for ( var i = 1 ; i < msg . length - 1 ; i ++ ) {
html . push ( '<span class="splited-post-counter">' + str + '</span>' ) ;
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\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 ( ) + '<' ;
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 ( /<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 ;
}
}
}
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 ( /<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 ( /&/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 )
msg = 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
. replace ( /\(\d{1,2}\/\d{1,2}\)$/ , htmlSplitCounter )
. replace ( /\(\d{1,2}\/\d{1,2}\)$/ , htmlSplitCounter )
. replace ( />(\d+)</g , function ( candy , core ) { return html [ core ] } )
. replace ( /&(?!lt;|gt;|quot;|apos;)/g , '&' ) // FIXME in many cases there is no need to escape ampersand in HTML 5
) ;
;
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 ;
}
}
if ( $ . Options . displayLineFeeds . val === 'enable' )
msg = msg . replace ( /\n/g , '<br />' ) ;
// 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 ) {
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 , '&' )
//.replace(/&/g, '&') we do it not here
. replace ( /</g , '<' )
. replace ( /</g , '<' )
. replace ( />/g , '>' )
. replace ( />/g , '>' )
. replace ( /"/g , '"' )
. replace ( /"/g , '"' )