// twister_formatpost.js
// 2013 Miguel Freitas
//
// Format JSON posts and DMs to HTML.
// format "userpost" to html element
// kind = "original"/"ancestor"/"descendant"
function postToElem ( post , kind , promoted ) {
/ *
"userpost" :
{
"n" : username ,
"k" : seq number ,
"t" : "post" / "dm" / "rt"
"msg" : message ( post / rt )
"time" : unix utc
"height" : best height at user
"dm" : encrypted message ( dm ) - opt
"rt" : original userpost - opt
"sig_rt" : sig of rt - opt
"reply" : - opt
{
"n" : reference username
"k" : reference k
}
}
"sig_userpost" : signature by userpost . n
* /
// Obtain data from userpost
var postJson = $ . toJSON ( post ) ;
var userpost = post [ "userpost" ] ;
if ( "rt" in userpost ) {
var rt = userpost [ "rt" ] ;
var n = rt [ "n" ] ;
var k = rt [ "k" ] ;
var t = rt [ "time" ] ;
var msg = rt [ "msg" ] ;
var content _to _rt = $ . toJSON ( rt ) ;
var content _to _sigrt = userpost [ "sig_rt" ] ;
var retweeted _by = userpost [ "n" ] ;
} else {
var n = userpost [ "n" ] ;
var k = userpost [ "k" ] ;
var t = userpost [ "time" ] ;
var msg = userpost [ "msg" ]
var content _to _rt = $ . toJSON ( userpost ) ;
var content _to _sigrt = post [ "sig_userpost" ] ;
var retweeted _by = undefined ;
}
// Now create the html elements
var elem = $ . MAL . getPostTemplate ( ) . clone ( true ) ;
elem . removeAttr ( 'id' )
. addClass ( kind )
. attr ( 'data-time' , t )
;
if ( post [ 'isNew' ] )
elem . addClass ( 'new' ) ;
var postData = elem . find ( '.post-data' ) ;
postData . addClass ( kind )
. attr ( 'data-userpost' , postJson )
. attr ( 'data-content_to_rt' , content _to _rt )
. attr ( 'data-content_to_sigrt' , content _to _sigrt )
. attr ( 'data-screen-name' , n )
. attr ( 'data-id' , k )
. attr ( 'data-lastk' , userpost [ "lastk" ] )
. attr ( 'data-text' , msg )
;
if ( 'reply' in userpost ) {
postData . attr ( 'data-replied-to-screen-name' , userpost . reply . n )
. attr ( 'data-replied-to-id' , userpost . reply . k )
. find ( '.post-expand' ) . text ( polyglot . t ( 'Show conversation' ) )
;
} else if ( 'rt' in userpost && 'reply' in userpost . rt ) {
postData . attr ( 'data-replied-to-screen-name' , userpost . rt . reply . n )
. attr ( 'data-replied-to-id' , userpost . rt . reply . k )
. find ( '.post-expand' ) . text ( polyglot . t ( 'Show conversation' ) )
;
}
var postInfoName = elem . find ( '.post-info-name' ) ;
postInfoName . text ( n ) . attr ( 'href' , $ . MAL . userUrl ( n ) ) ;
getFullname ( n , postInfoName ) ;
//elem.find('.post-info-tag').text("@" + n);
setPostInfoSent ( n , k , elem . find ( '.post-info-sent' ) ) ;
getAvatar ( n , elem . find ( '.avatar' ) ) ;
elem . find ( '.post-info-time' ) . text ( timeGmtToText ( t ) ) . attr ( 'title' , timeSincePost ( t ) ) ;
var mentions = [ ] ;
elem . find ( '.post-text' ) . html ( htmlFormatMsg ( msg , mentions ) ) ;
postData . attr ( 'data-text-mentions' , mentions ) ;
var replyTo = '' ;
if ( n !== defaultScreenName )
replyTo += '@' + n + ' ' ;
for ( var i = 0 ; i < mentions . length ; i ++ ) {
if ( mentions [ i ] !== n && mentions [ i ] !== defaultScreenName )
replyTo += '@' + mentions [ i ] + ' ' ;
}
var postTextArea = elem . find ( '.post-area-new textarea' ) ;
postTextArea . attr ( 'data-reply-to' , replyTo ) ;
if ( ! defaultScreenName )
postTextArea . attr ( 'placeholder' , polyglot . t ( 'You have to log in to post replies.' ) ) ;
else
postTextArea . attr ( 'placeholder' , polyglot . t ( 'reply_to' , { fullname : replyTo } ) + '...' ) ;
postData . attr ( 'data-reply-to' , replyTo ) ;
if ( retweeted _by != undefined ) {
elem . find ( '.post-context' ) . show ( ) ;
elem . find ( '.post-retransmited-by' )
. attr ( 'href' , $ . MAL . userUrl ( retweeted _by ) )
. text ( '@' + retweeted _by )
;
}
if ( typeof ( promoted ) !== 'undefined' && promoted ) {
elem . find ( '.post-propagate' ) . remove ( ) ;
} else {
if ( $ . Options . filterLang . val !== 'disable' && $ . Options . filterLangSimulate . val ) {
// FIXME it's must be stuff from template actually
if ( typeof ( post [ 'langFilter' ] ) !== 'undefined' ) {
if ( typeof ( post [ 'langFilter' ] [ 'prob' ] [ 0 ] ) !== 'undefined' )
var mlm = ' // ' + polyglot . t ( 'Most possible language: this' , { 'this' : '<em>' + post [ 'langFilter' ] [ 'prob' ] [ 0 ] . toString ( ) + '</em>' } ) ;
else
var mlm = '' ;
elem . append ( '<div class="langFilterSimData">' + polyglot . t ( 'This post is treated by language filter' , { 'treated' : '<em>' + ( ( post [ 'langFilter' ] [ 'pass' ] ) ? polyglot . t ( 'passed' ) : polyglot . t ( 'blocked' ) ) + '</em>' } ) + '</div>' )
. append ( '<div class="langFilterSimData">' + polyglot . t ( 'Reason: this' , { 'this' : '<em>' + post [ 'langFilter' ] [ 'reason' ] + '</em>' } ) + mlm + '</div>' )
;
} else {
elem . append ( '<div class="langFilterSimData">' + polyglot . t ( 'This post is treated by language filter' , { 'treated' : '<em>' + polyglot . t ( 'not analyzed' ) + '</em>' } ) + '</div>' ) ;
}
}
}
return elem ;
}
function setPostInfoSent ( n , k , item ) {
if ( n === defaultScreenName && k >= 0 ) {
getPostMaxAvailability ( n , k ,
function ( args , count ) {
if ( count >= 3 ) { // assume 3 peers (me + 2) is enough for "sent"
args . item . text ( "\u2713" ) ; // check mark
} else {
args . item . text ( "\u231B" ) ; // hour glass
setTimeout ( setPostInfoSent , 2000 , n , k , item ) ;
}
} , { n : n , k : k , item : item } ) ;
}
}
// format dmdata (returned by getdirectmsgs) to display in "snippet" per user list
function dmDataToSnippetItem ( dmData , remoteUser ) {
var dmItem = $ ( "#dm-snippet-template" ) . clone ( true ) ;
dmItem . removeAttr ( 'id' ) ;
dmItem . attr ( "data-dm-screen-name" , remoteUser ) ;
dmItem . attr ( "data-last_id" , dmData . id ) ;
dmItem . attr ( "data-time" , dmData . time ) ;
dmItem . find ( ".post-info-tag" ) . text ( "@" + remoteUser ) ;
dmItem . find ( "a.post-info-name" ) . attr ( "href" , $ . MAL . userUrl ( remoteUser ) ) ;
dmItem . find ( "a.dm-chat-link" ) . attr ( "href" , $ . MAL . dmchatUrl ( remoteUser ) ) ;
getAvatar ( remoteUser , dmItem . find ( ".post-photo" ) . find ( "img" ) ) ;
if ( remoteUser . length && remoteUser [ 0 ] === '*' )
getGroupChatName ( remoteUser , dmItem . find ( "a.post-info-name" ) ) ;
else
getFullname ( remoteUser , dmItem . find ( "a.post-info-name" ) ) ;
dmItem . find ( ".post-text" ) . html ( escapeHtmlEntities ( dmData . text ) ) ;
dmItem . find ( ".post-info-time" ) . text ( timeGmtToText ( dmData . time ) ) . attr ( "title" , timeSincePost ( dmData . time ) ) ;
return dmItem ;
}
// format dmdata (returned by getdirectmsgs) to display in conversation thread
function dmDataToConversationItem ( dmData , localUser , remoteUser ) {
var from = ( dmData . from && dmData . from . length && dmData . from . charCodeAt ( 0 ) )
? dmData . from
: ( dmData . fromMe ? localUser : remoteUser ) ;
var classDm = dmData . fromMe ? "sent" : "received" ;
var dmItem = $ ( "#dm-chat-template" ) . clone ( true ) ;
dmItem . removeAttr ( 'id' ) ;
dmItem . addClass ( classDm ) ;
getAvatar ( from , dmItem . find ( ".post-photo" ) . find ( "img" ) ) ;
dmItem . find ( ".post-info-time" ) . text ( timeGmtToText ( dmData . time ) ) . attr ( "title" , timeSincePost ( dmData . time ) ) ;
setPostInfoSent ( from , dmData . k , dmItem . find ( '.post-info-sent' ) ) ;
var mentions = [ ] ;
dmItem . find ( '.post-text' ) . html ( htmlFormatMsg ( dmData . text , mentions ) ) ;
return dmItem ;
}
// convert message text to html, featuring @users and links formating.
function htmlFormatMsg ( msg , mentions ) {
function getStrStart ( str , startPoint , stopChars , stopCharsTrailing ) {
for ( var i = startPoint ; i > - 1 ; i -- ) {
if ( stopChars . indexOf ( str [ i ] ) > - 1 )
break ;
}
for ( i += 1 ; i < startPoint + 1 ; i ++ ) {
if ( stopCharsTrailing . indexOf ( str [ i ] ) === - 1 )
break ;
}
return i ;
}
function getStrEnd ( str , startPoint , stopChars , stopCharsTrailing ) {
for ( var i = startPoint ; i < str . length ; i ++ ) {
if ( stopChars . indexOf ( str [ i ] ) > - 1 )
break ;
}
for ( i -= 1 ; i > startPoint - 1 ; i -- ) {
if ( stopCharsTrailing . indexOf ( str [ i ] ) === - 1 )
break ;
}
return i ;
}
function markdown ( str , chr , tag ) {
function whiteSpace ( i , j ) {
j ++ ;
for ( i += 1 ; i < j ; i ++ ) {
if ( p [ i ] . w )
return true ;
}
return false ;
}
var i , j , t , l , r , strEncoded ;
var w = false ;
var p = [ ] ;
// collecting chars position data
for ( i = 0 ; i < str . length ; i ++ ) {
if ( str [ i ] === chr ) {
for ( j = i + 1 ; j < str . length ; j ++ ) {
if ( str [ j ] !== chr )
break ;
}
if ( i === 0 ) {
p . push ( { i : i , k : j - i , t : - 1 , w : w , a : - 1 , p : - 1 } ) ;
w = false ;
} else if ( j === str . length ) {
p . push ( { i : i , k : j - i , t : 1 , w : w , a : - 1 , p : - 1 } ) ;
w = false ;
} else {
if ( stopCharsMarkDown . indexOf ( str [ i - 1 ] ) > - 1 ) {
l = 1 ;
for ( t = i - 2 ; t > - 1 ; t -- ) {
if ( str [ t ] === chr ) {
l = - 1 ;
break ;
} else if ( stopCharsMarkDown . indexOf ( str [ t ] ) === - 1 ) {
l = whiteSpacesMarkDown . indexOf ( str [ t ] ) ;
break ;
}
}
} else
l = whiteSpacesMarkDown . indexOf ( str [ i - 1 ] ) ;
if ( stopCharsMarkDown . indexOf ( str [ j ] ) > - 1 ) {
r = 1 ;
for ( t = j + 1 ; t < str . length ; t ++ ) {
if ( str [ t ] === chr ) {
r = - 1 ;
break ;
} else if ( stopCharsMarkDown . indexOf ( str [ t ] ) === - 1 ) {
r = whiteSpacesMarkDown . indexOf ( str [ t ] ) ;
break ;
}
}
} else
r = whiteSpacesMarkDown . indexOf ( str [ j ] ) ;
if ( l > - 1 ) {
if ( r > - 1 ) {
if ( j - i > 2 ) {
l = p . push ( { i : i , k : j - i - 1 , t : - 1 , w : w , a : - 1 , p : - 1 } ) - 1 ;
p [ l ] . a = p . push ( { i : j - 1 , k : 1 , t : 1 , w : false , a : l , p : - 1 } ) - 1 ;
}
t = 10 ;
} else
t = - 1 ;
} else {
if ( r > - 1 )
t = 1 ;
else
t = 0 ;
}
if ( t !== 10 )
p . push ( { i : i , k : j - i , t : t , w : w , a : - 1 , p : - 1 } ) ;
w = false ;
}
i = j - 1 ;
} else if ( ! w && whiteSpaces . indexOf ( str [ i ] ) > - 1 ) {
w = true ;
}
}
// calculating dependencies
for ( i = 0 ; i < p . length ; i ++ ) {
if ( p [ i ] . t < 1 && p [ i ] . a === - 1 ) {
t = i ;
for ( j = i + 1 ; j < p . length ; j ++ ) {
if ( p [ i ] . t === 0 && whiteSpace ( i , j ) ) {
i = j - 1 ;
break ;
} else if ( p [ j ] . t < 1 && p [ j ] . a === - 1 ) {
p [ t ] . p = j ;
t = j ;
} else if ( p [ j ] . t === 1 && p [ j ] . a === - 1 ) {
p [ i ] . a = j ;
p [ j ] . a = i ;
i = j ;
break ;
}
}
}
}
for ( i = 0 ; i < p . length ; i ++ ) {
if ( p [ i ] . t === - 1 && p [ i ] . a === - 1 ) {
for ( j = p [ i ] . p ; j > - 1 ; j = p [ j ] . p ) {
if ( whiteSpace ( i , j ) ) {
i = j - 1 ;
break ;
} else if ( p [ j ] . t === 0
&& ! ( p [ j ] . p > - 1 && p [ p [ j ] . p ] . t === 0 && ! whiteSpace ( j , p [ j ] . p ) ) ) {
p [ j ] . a = i ;
p [ i ] . a = j ;
i = j ;
break ;
}
}
}
}
// changing the string
if ( chr === '`' ) {
for ( i = 0 ; i < p . length ; i ++ ) {
if ( p [ i ] . a > - 1 ) {
if ( p [ i ] . t === - 1 || ( p [ i ] . t === 0 && p [ i ] . a > i ) ) {
if ( p [ i ] . k > 1 )
t = '<' + tag + '>' + Array ( p [ i ] . k ) . join ( chr ) ;
else
t = '<' + tag + '>' ;
j = p [ i ] . a ;
t = t + str . slice ( p [ i ] . i + p [ i ] . k , p [ j ] . i ) ;
if ( p [ j ] . k > 1 )
t = t + Array ( p [ i ] . k ) . join ( chr ) + '</' + tag + '>' ;
else
t = t + '</' + tag + '>' ;
html . push ( t . replace ( /&(?!lt;|gt;)/g , '&' ) ) ;
strEncoded = '>' + ( html . length - 1 ) . toString ( ) + '<' ;
str = str . slice ( 0 , p [ i ] . i ) + strEncoded + str . slice ( p [ j ] . i + p [ j ] . k ) ;
l = strEncoded . length - p [ j ] . i - p [ j ] . k + p [ i ] . i ;
i = j ;
for ( j += 1 ; j < p . length ; j ++ )
p [ j ] . i += l ;
}
}
}
} else {
for ( i = 0 ; i < p . length ; i ++ ) {
if ( p [ i ] . a > - 1 ) {
if ( p [ i ] . t === - 1 || ( p [ i ] . t === 0 && p [ i ] . a > i ) ) {
if ( p [ i ] . k > 1 )
html . push ( '<' + tag + '>' + Array ( p [ i ] . k ) . join ( chr ) ) ;
else
html . push ( '<' + tag + '>' ) ;
} else if ( p [ i ] . t === 1 || ( p [ i ] . t === 0 && p [ i ] . a < i ) ) {
if ( p [ i ] . k > 1 )
html . push ( Array ( p [ i ] . k ) . join ( chr ) + '</' + tag + '>' ) ;
else
html . push ( '</' + tag + '>' ) ;
}
strEncoded = '>' + ( html . length - 1 ) . toString ( ) + '<' ;
str = str . slice ( 0 , p [ i ] . i ) + strEncoded + str . slice ( p [ i ] . i + p [ i ] . k ) ;
l = strEncoded . length - p [ i ] . k ;
for ( j = i + 1 ; j < p . length ; j ++ )
p [ j ] . i += l ;
}
}
}
return str ;
}
function htmlSplitCounter ( str ) {
html . push ( '<span class="splited-post-counter">' + str + '</span>' ) ;
return '>' + ( html . length - 1 ) . toString ( ) + '<' ;
}
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 stopCharsLeft = '<' + whiteSpaces ;
var stopCharsRight = '>' + whiteSpaces ;
var stopCharsRightHashtags = '>/\\.,:;?!%\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011' // same as stopCharsTrailing but without '*~_-`' plus '>'
+ whiteSpaces ;
var whiteSpacesMarkDown = whiteSpaces + '+=&/\\.,:;?!%\'"[](){}^|«»…\u201C\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3010\u3011' ;
var stopCharsMarkDown = '*~_-`' ;
var i , j , k , str , strEncoded ;
var html = [ ] ;
msg = markdown ( escapeHtmlEntities ( msg ) ,
'`' , 'samp' ) ; // kind of monospace, sequence of chars inside will be escaped from markup
for ( i = 0 ; i < msg . length - 7 ; i ++ ) {
if ( msg . slice ( i , i + 2 ) === '](' ) {
// FIXME there can be text with [] inside [] or links with () wee need to handle it too
j = getStrStart ( msg , i - 1 , '[' , '' ) ;
if ( j < i ) {
k = getStrEnd ( msg , i + 2 , ')' , '' ) ;
if ( k > i + 1 ) {
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 ( msg . slice ( i + 2 , k + 1 ) ) + '" ' ) // $().closest('a').attr('href', proxyURL(url))
. replace ( /(<a\s+[^]*?>)[^]*?(<\/a>)/ig , '$1' + msg . slice ( j , i ) + '$2' ) // $().closest('a').text(url)
) ;
strEncoded = '>' + ( html . length - 1 ) . toString ( ) + '<' ;
msg = msg . slice ( 0 , j - 1 ) + strEncoded + msg . slice ( k + 2 ) ;
i = j + strEncoded . length - 1 ;
}
}
} else if ( msg . slice ( i , i + 4 ) . toLowerCase ( ) === 'http' ) {
if ( msg . slice ( i + 4 , i + 7 ) === '://' && stopCharsRight . indexOf ( msg [ i + 7 ] ) === - 1 ) {
j = getStrEnd ( msg , i + 7 , stopCharsRight , stopCharsTrailingUrl ) ;
if ( j > i + 6 ) {
str = msg . slice ( i , j + 1 ) ;
// 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 ) {
j = getStrEnd ( msg , i + 8 , stopCharsRight , stopCharsTrailingUrl ) ;
if ( j > i + 7 ) {
str = msg . slice ( i , j + 1 ) ;
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 ;
}
}
}
}
for ( 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 - 1 , stopCharsLeft , stopCharsTrailing ) ;
if ( j < i ) {
k = getStrEnd ( msg , i + 1 , stopCharsRight , stopCharsTrailing ) ;
if ( k > i ) {
str = msg . slice ( j , k + 1 ) ;
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 ;
}
}
}
}
for ( 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 ;
}
}
for ( i = 0 ; i < msg . length - 1 ; i ++ ) {
if ( msg [ i ] === '#' && msg [ i + 1 ] !== '#' && stopCharsRight . indexOf ( msg [ i + 1 ] ) === - 1 ) {
j = getStrEnd ( msg , i + 1 , stopCharsRightHashtags , stopCharsTrailing ) ;
if ( j > i ) {
str = msg . slice ( i + 1 , j + 1 ) ;
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 ;
}
}
}
msg = markdown ( markdown ( markdown ( markdown ( msg ,
'*' , 'b' ) , // bold
'~' , 'i' ) , // italic
'_' , 'u' ) , // underlined
'-' , 's' ) // striketrough
. replace ( /\(\d{1,2}\/\d{1,2}\)$/ , htmlSplitCounter )
. replace ( /&(?!lt;|gt;)/g , '&' ) // FIXME in many cases there is no need to escape ampersand in HTML 5
. replace ( /"/g , '"' )
. replace ( /'/g , ''' )
;
for ( i = 0 ; i < msg . length - 2 ; i ++ ) {
if ( msg [ i ] === '>' ) {
for ( j = i + 2 ; j < msg . length ; j ++ ) {
if ( msg [ j ] === '<' )
break ;
}
str = html [ parseInt ( msg . slice ( i + 1 , j ) ) ] ;
msg = msg . slice ( 0 , i ) + str + msg . slice ( j + 1 ) ;
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 make markdown optionally mutable ?
return msg ;
}
function proxyURL ( url ) {
var proxyOpt = $ . Options . useProxy . val ;
if ( proxyOpt !== 'disable' && ! $ . Options . useProxyForImgOnly . val ) {
// proxy alternatives may be added to options page FIXME currently not; and we need more fresh proxies
if ( proxyOpt === 'ssl-proxy-my-addr' ) {
url = 'https://ssl-proxy.my-addr.org/myaddrproxy.php/' +
url . substring ( 0 , url . indexOf ( ':' ) ) + url . substr ( url . indexOf ( '/' ) + 1 ) ;
} else if ( proxyOpt === 'anonymouse' )
url = 'http://anonymouse.org/cgi-bin/anon-www.cgi/' + url ;
}
return url ;
}
function escapeHtmlEntities ( str ) {
return str
//.replace(/&/g, '&') we do it not here
. replace ( /</g , '<' )
. replace ( />/g , '>' )
//.replace(/"/g, '"')
//.replace(/'/g, ''')
;
}
function reverseHtmlEntities ( str ) {
return str
. replace ( /</g , '<' )
. replace ( />/g , '>' )
. replace ( /"/g , '"' )
. replace ( /'/g , "'" )
. replace ( /&/g , '&' ) ;
}