@ -52,71 +52,71 @@ function postToElem( post, kind, promoted ) {
// Now create the html elements
// Now create the html elements
var elem = $ . MAL . getPostTemplate ( ) . clone ( true ) ;
var elem = $ . MAL . getPostTemplate ( ) . clone ( true ) ;
elem . removeAttr ( 'id' ) ;
elem . removeAttr ( 'id' )
elem . addClass ( kind ) ;
. addClass ( kind )
elem . attr ( 'data-time' , t ) ;
. attr ( 'data-time' , t )
;
if ( post [ "isNew" ] )
if ( post [ 'isNew' ] )
elem . addClass ( 'new' ) ;
elem . addClass ( 'new' ) ;
var postData = elem . find ( ".post-data" ) ;
var postData = elem . find ( '.post-data' ) ;
postData . addClass ( kind ) ;
postData . addClass ( kind )
postData . attr ( 'data-userpost' , postJson ) ;
. attr ( 'data-userpost' , postJson )
postData . attr ( 'data-content_to_rt' , content _to _rt ) ;
. attr ( 'data-content_to_rt' , content _to _rt )
postData . attr ( 'data-content_to_sigrt' , content _to _sigrt ) ;
. attr ( 'data-content_to_sigrt' , content _to _sigrt )
postData . attr ( 'data-screen-name' , n ) ;
. attr ( 'data-screen-name' , n )
postData . attr ( 'data-id' , k ) ;
. attr ( 'data-id' , k )
postData . attr ( 'data-lastk' , userpost [ "lastk" ] ) ;
. attr ( 'data-lastk' , userpost [ "lastk" ] )
postData . attr ( 'data-text' , msg ) ;
. attr ( 'data-text' , msg )
if ( "reply" in userpost ) {
;
postData . attr ( 'data-replied-to-screen-name' , userpost [ "reply" ] [ "n" ] ) ;
if ( 'reply' in userpost ) {
postData . attr ( 'data-replied-to-id' , userpost [ "reply" ] [ "k" ] ) ;
postData . attr ( 'data-replied-to-screen-name' , userpost . reply . n )
. attr ( 'data-replied-to-id' , userpost . reply . k )
postData . find ( '.post-expand' ) . text ( polyglot . t ( "Show conversation" ) ) ;
. 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" ] ) ;
} else if ( 'rt' in userpost && 'reply' in userpost . rt ) {
postData . attr ( 'data-replied-to-id' , userpost [ "rt" ] [ "reply" ] [ "k" ] ) ;
postData . attr ( 'data-replied-to-screen-name' , userpost . rt . reply . n )
. attr ( 'data-replied-to-id' , userpost . rt . reply . k )
postData . find ( '.post-expand' ) . text ( polyglot . t ( "Show conversation" ) ) ;
. find ( '.post-expand' ) . text ( polyglot . t ( 'Show conversation' ) )
;
}
}
var postInfoName = elem . find ( ".post-info-name" ) ;
var postInfoName = elem . find ( '.post-info-name' ) ;
postInfoName . attr ( 'href' , $ . MAL . userUrl ( n ) ) ;
postInfoName . text ( n ) . attr ( 'href' , $ . MAL . userUrl ( n ) ) ;
postInfoName . text ( n ) ;
getFullname ( n , postInfoName ) ;
getFullname ( n , postInfoName ) ;
elem . find ( ".post-info-tag" ) . text = "@" + n ;
//elem.find('.post-info-tag').text("@" + n);
getAvatar ( n , elem . find ( ".avatar" ) ) ;
getAvatar ( n , elem . find ( '.avatar' ) ) ;
elem . find ( ".post-info-time" ) . text ( timeGmtToText ( t ) ) ;
elem . find ( '.post-info-time' ) . text ( timeGmtToText ( t ) ) . attr ( 'title' , timeSincePost ( t ) ) ;
elem . find ( ".post-info-time" ) . attr ( "title" , timeSincePost ( t ) ) ;
var mentions = [ ] ;
var mentions = [ ] ;
htmlFormatMsg ( msg , elem . find ( ".post-text" ) , mentions ) ;
elem . find ( '.post-text' ) . html ( htmlFormatMsg ( msg , mentions ) ) ;
postData . attr ( 'data-text-mentions' , mentions ) ;
postData . attr ( 'data-text-mentions' , mentions ) ;
var replyTo = "" ;
var replyTo = [ ] ;
if ( n != defaultScreenName )
if ( n !== defaultScreenName )
replyTo += "@" + n + " " ;
replyTo . push ( [ '@' , n , ' ' ] . join ( '' ) ) ;
for ( var i = 0 ; i < mentions . length ; i ++ ) {
for ( var i = 0 ; i < mentions . length ; i ++ ) {
if ( mentions [ i ] != n && mentions [ i ] != defaultScreenName ) {
if ( mentions [ i ] !== n && mentions [ i ] !== defaultScreenName )
replyTo += "@" + mentions [ i ] + " " ;
replyTo . push ( [ '@' , mentions [ i ] , ' ' ] . join ( '' ) ) ;
}
}
if ( ! defaultScreenName )
{
elem . find ( ".post-area-new textarea" ) . attr ( "placeholder" , polyglot . t ( "You have to log in to post replies." ) ) ;
}
}
replyTo = replyTo . join ( '' ) ;
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
else
{
postTextArea . attr ( 'placeholder' , polyglot . t ( 'reply_to' , { fullname : replyTo } ) + '...' ) ;
elem . find ( ".post-area-new textarea" ) . attr ( "placeholder" , polyglot . t ( "reply_to" , { fullname : replyTo } ) + "..." ) ;
}
postData . attr ( 'data-reply-to' , replyTo ) ;
elem . find ( ".post-area-new textarea" ) . attr ( "data-reply-to" , replyTo ) ;
postData . attr ( "data-reply-to" , replyTo ) ;
if ( retweeted _by != undefined ) {
if ( retweeted _by != undefined ) {
elem . find ( ".post-context" ) . show ( ) ;
elem . find ( '.post-context' ) . show ( ) ;
var retweetedByElem = elem . find ( ".post-retransmited-by" ) ;
elem . find ( '.post-retransmited-by' )
retweetedByElem . attr ( "href" , $ . MAL . userUrl ( retweeted _by ) ) ;
. attr ( 'href' , $ . MAL . userUrl ( retweeted _by ) )
retweetedByElem . text ( '@' + retweeted _by ) ;
. text ( '@' + retweeted _by )
;
}
}
if ( typeof ( promoted ) !== 'undefined' && promoted ) {
if ( typeof ( promoted ) !== 'undefined' && promoted ) {
@ -130,8 +130,9 @@ function postToElem( post, kind, promoted ) {
else
else
var mlm = '' ;
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>' ) ;
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>' )
elem . append ( '<div class="langFilterSimData">' + polyglot . t ( 'Reason: this' , { 'this' : '<em>' + post [ 'langFilter' ] [ 'reason' ] + '</em>' } ) + mlm + '</div>' ) ;
. append ( '<div class="langFilterSimData">' + polyglot . t ( 'Reason: this' , { 'this' : '<em>' + post [ 'langFilter' ] [ 'reason' ] + '</em>' } ) + mlm + '</div>' )
;
} else {
} else {
elem . append ( '<div class="langFilterSimData">' + polyglot . t ( 'This post is treated by language filter' , { 'treated' : '<em>' + polyglot . t ( 'not analyzed' ) + '</em>' } ) + '</div>' ) ;
elem . append ( '<div class="langFilterSimData">' + polyglot . t ( 'This post is treated by language filter' , { 'treated' : '<em>' + polyglot . t ( 'not analyzed' ) + '</em>' } ) + '</div>' ) ;
}
}
@ -158,8 +159,7 @@ function dmDataToSnippetItem(dmData, remoteUser) {
else
else
getFullname ( remoteUser , dmItem . find ( "a.post-info-name" ) ) ;
getFullname ( remoteUser , dmItem . find ( "a.post-info-name" ) ) ;
dmItem . find ( ".post-text" ) . html ( escapeHtmlEntities ( dmData . text ) ) ;
dmItem . find ( ".post-text" ) . html ( escapeHtmlEntities ( dmData . text ) ) ;
dmItem . find ( ".post-info-time" ) . text ( timeGmtToText ( dmData . time ) ) ;
dmItem . find ( ".post-info-time" ) . text ( timeGmtToText ( dmData . time ) ) . attr ( "title" , timeSincePost ( dmData . time ) ) ;
dmItem . find ( ".post-info-time" ) . attr ( "title" , timeSincePost ( dmData . time ) ) ;
return dmItem ;
return dmItem ;
}
}
@ -171,183 +171,96 @@ function dmDataToConversationItem(dmData, localUser, remoteUser) {
dmItem . removeAttr ( 'id' ) ;
dmItem . removeAttr ( 'id' ) ;
dmItem . addClass ( classDm ) ;
dmItem . addClass ( classDm ) ;
getAvatar ( dmData . fromMe ? localUser : remoteUser , dmItem . find ( ".post-photo" ) . find ( "img" ) ) ;
getAvatar ( dmData . fromMe ? localUser : remoteUser , dmItem . find ( ".post-photo" ) . find ( "img" ) ) ;
dmItem . find ( ".post-info-time" ) . text ( timeGmtToText ( dmData . time ) ) ;
dmItem . find ( ".post-info-time" ) . text ( timeGmtToText ( dmData . time ) ) . attr ( "title" , timeSincePost ( dmData . time ) ) ;
dmItem . find ( ".post-info-time" ) . attr ( "title" , timeSincePost ( dmData . time ) ) ;
var mentions = [ ] ;
var mentions = [ ] ;
htmlFormatMsg ( dmData . text , dmItem . find ( ".post-text" ) , mentions ) ;
dmItem . find ( '.post-text' ) . html ( htmlFormatMsg ( dmData . text , mentions ) ) ;
return dmItem ;
return dmItem ;
}
}
// convert message text to html, featuring @users and links formating.
// convert message text to html, featuring @users and links formating.
// todo: hashtags
function htmlFormatMsg ( msg , mentions ) {
function htmlFormatMsg ( msg , output , mentions ) {
function htmlMention ( str , pre ) {
var tmp ;
str = str . replace ( new RegExp ( [ '^' , pre , '@' ] . join ( '' ) ) , '' ) . toLowerCase ( ) ;
var match = null ;
var index ;
mentions . push ( str ) ; // FIXME feel the scope
var strUrlRegexp = "http[s]?://" ;
var strEmailRegexp = "\\S+@\\S+\\.\\S+" ;
// FIXME we're trying to not interact with DOM, coz' we want to run really fast [to hell of RegExps]
var strSplitCounterR = "\\(\\d{1,2}\\/\\d{1,2}\\)$" ;
// FIXME actually we should avoid it by dropping a template idea and construct html right here
var reAll = new RegExp ( "(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + "|" + strSplitCounterR + ")" ) ;
return $ ( '#msg-user-link-template' ) [ 0 ] . outerHTML
var reHttp = new RegExp ( strUrlRegexp ) ;
. replace ( /\bid\s*=\s*"[^]*?"+/ig , '' ) // $().removeAttr('id')
var reEmail = new RegExp ( strEmailRegexp ) ;
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
var reSplitCounter = new RegExp ( strSplitCounterR ) ;
. replace ( /<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))/ig , [ pre , '<a href="' , $ . MAL . userUrl ( str ) , '" ' ] . join ( '' ) ) // $().closest('a.open-profile-modal').attr('href', $.MAL.userUrl(username))
. replace ( /(<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-profile-modal\b))[^]*?>)[^]*?(<\/a>)/ig , [ pre , '$1@' , str , '$2' ] . join ( '' ) ) // $().closest('a.open-profile-modal').text('@'+username)
msg = escapeHtmlEntities ( msg ) ;
;
}
while ( msg != undefined && msg . length ) {
match = reAll . exec ( msg ) ;
if ( match ) {
index = ( match [ 0 ] === match [ 1 ] ) ? match . index : match . index + 1 ;
if ( match [ 1 ] == "@" ) {
output . append ( _formatText ( msg . substr ( 0 , index ) ) ) ;
tmp = msg . substr ( index + 1 ) ;
var username = _extractUsername ( tmp ) ;
if ( username . length ) {
if ( mentions . indexOf ( username ) < 0 )
mentions . push ( username ) ;
var userLinkTemplate = $ ( "#msg-user-link-template" ) . clone ( true ) ;
userLinkTemplate . removeAttr ( "id" ) ;
userLinkTemplate . attr ( "href" , $ . MAL . userUrl ( username ) ) ;
userLinkTemplate . text ( "@" + username ) ;
output . append ( userLinkTemplate ) ;
msg = tmp . substr ( String ( username ) . length ) ;
continue ;
}
output . append ( '@' ) ;
msg = tmp ;
continue ;
}
if ( reHttp . exec ( match [ 1 ] ) ) {
output . append ( _formatText ( msg . substr ( 0 , index ) ) ) ;
tmp = msg . substring ( index ) ;
var space = tmp . search ( /[ \n\t]/ ) ;
var url ;
if ( space != - 1 ) url = tmp . substring ( 0 , space ) ; else url = tmp ;
if ( url . length ) {
msg = tmp . substr ( String ( url ) . length ) ;
url = reverseHtmlEntities ( url ) ;
var extLinkTemplate = $ ( "#external-page-link-template" ) . clone ( true ) ;
extLinkTemplate . removeAttr ( "id" ) ;
if ( $ . Options . getUseProxyOpt ( ) !== 'disable' && ! $ . Options . getUseProxyForImgOnlyOpt ( ) ) {
//proxy alternatives may be added to options page...
if ( $ . Options . getUseProxyOpt ( ) === 'ssl-proxy-my-addr' ) {
extLinkTemplate . attr ( 'href' , 'https://ssl-proxy.my-addr.org/myaddrproxy.php/' +
url . substring ( 0 , url . indexOf ( ':' ) ) +
url . substr ( url . indexOf ( '/' ) + 1 ) ) ;
} else if ( $ . Options . getUseProxyOpt ( ) === 'anonymouse' ) {
extLinkTemplate . attr ( 'href' , 'http://anonymouse.org/cgi-bin/anon-www.cgi/' + url ) ;
}
} else {
extLinkTemplate . attr ( "href" , url ) ;
}
extLinkTemplate . text ( url ) ;
extLinkTemplate . attr ( "title" , url ) ;
output . append ( extLinkTemplate ) ;
continue ;
}
}
if ( reEmail . exec ( match [ 1 ] ) ) {
function htmlHashtag ( str , pre ) {
output . append ( _formatText ( msg . substr ( 0 , index ) ) ) ;
str = str . replace ( new RegExp ( [ '^' , pre , '#' ] . join ( '' ) ) , '' ) ;
tmp = msg . substring ( index ) ;
var space = tmp . search ( /[ \n\t]/ ) ;
var email ;
if ( space != - 1 ) email = tmp . substring ( 0 , space ) ; else email = tmp ;
if ( email . length ) {
var extLinkTemplate = $ ( "#external-page-link-template" ) . clone ( true ) ;
extLinkTemplate . removeAttr ( "id" ) ;
extLinkTemplate . attr ( "href" , "mailto:" + email ) ;
extLinkTemplate . text ( email ) ;
extLinkTemplate . attr ( "title" , email ) ;
output . append ( extLinkTemplate ) ;
msg = tmp . substr ( String ( email ) . length ) ;
continue ;
}
}
if ( match [ 1 ] == "#" ) {
output . append ( _formatText ( msg . substr ( 0 , index ) ) ) ;
tmp = msg . substr ( index + 1 ) ;
var hashtag = _extractHashtag ( tmp ) ;
if ( hashtag . length ) {
var hashtag _lc = '' ;
for ( var i = 0 ; i < hashtag . length ; i ++ ) {
var c = hashtag [ i ] ;
hashtag _lc += ( c >= 'A' && c <= 'Z' ) ? c . toLowerCase ( ) : c ;
}
var hashtagLinkTemplate = $ ( "#hashtag-link-template" ) . clone ( true ) ;
hashtagLinkTemplate . removeAttr ( "id" ) ;
hashtagLinkTemplate . attr ( "href" , $ . MAL . hashtagUrl ( hashtag _lc ) ) ;
hashtagLinkTemplate . text ( "#" + hashtag ) ;
output . append ( hashtagLinkTemplate ) ;
msg = tmp . substr ( String ( hashtag ) . length ) ;
continue ;
}
output . append ( '#' ) ;
msg = tmp ;
continue ;
}
if ( reSplitCounter . exec ( match [ 1 ] ) ) {
return $ ( '#hashtag-link-template' ) [ 0 ] . outerHTML
output . append ( _formatText ( msg . substr ( 0 , index ) ) ) ;
. replace ( /\bid\s*=\s*"[^]*?"+/ig , '' ) // $().removeAttr('id')
tmp = msg . substring ( index ) ;
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
if ( tmp . length ) {
. replace ( /<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))/ig , [ '<a href="' , $ . MAL . hashtagUrl ( str . toLowerCase ( ) ) , '" ' ] . join ( '' ) ) // $().closest('a.open-profile-modal').attr('href', $.MAL.hashtagUrl(hashtag))
var splitCounter = $ ( '<span class="splited-post-counter"></span>' ) ;
. replace ( /(<a\s+(?=[^>]*?\bclass\s*=\s*"(?=[^"]*?\bopen-hashtag-modal\b))[^]*?>)[^]*?(<\/a>)/ig , [ pre , '$1#' , str , '$2' ] . join ( '' ) ) // $().closest('a.open-profile-modal').text('#'+hashtag)
splitCounter . text ( tmp ) ;
;
output . append ( splitCounter ) ;
}
msg = "" ;
continue ;
}
}
}
output . append ( _formatText ( msg ) ) ;
function htmlHttp ( str ) {
msg = "" ;
return $ ( '#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 ) , '" ' ] . join ( '' ) ) // $().closest('a').attr('href', proxyURL(url))
. replace ( /(<a\s+[^]*?>)[^]*?(<\/a>)/ig , [ '$1' , str , '$2' ] . join ( '' ) ) // $().closest('a').text(url)
;
}
}
}
// internal function for htmlFormatMsg
function htmlEmail ( str ) {
function _formatText ( msg )
return $ ( '#external-page-link-template' ) [ 0 ] . outerHTML
{
. replace ( /\bid\s*=\s*"[^]*?"+/ig , '' ) // $().removeAttr('id')
// TODO: add options for emotions and linefeeds
//.replace(/\bhref\s*=\s*"[^]*?"+/ig, '') // $().removeAttr('href')
//msg = $.emotions(msg);
. replace ( /<a\s+/ig , [ '<a href="mailto:' , str . toLowerCase ( ) , '" ' ] . join ( '' ) ) // $().closest('a').attr('href', 'mailto:'+url)
if ( $ . Options . getLineFeedsOpt ( ) == "enable" )
. replace ( /(<a\s+[^]*?>)[^]*?(<\/a>)/ig , [ '$1' , str , '$2' ] . join ( '' ) ) // $().closest('a').text(url)
msg = msg . replace ( /\n/g , '<br />' ) ;
;
return msg ;
}
function htmlSplitCounter ( str ) {
return [ '<span class="splited-post-counter">' , str , '</span>' ] . join ( '' ) ;
}
msg = escapeHtmlEntities ( msg )
. replace ( /(^|\s|\w)@\S\w*/g , htmlMention )
. replace ( /(^|\s|\w)#\S\w*/g , htmlHashtag )
. replace ( /\bhttps?:\/\/\S+/ig , htmlHttp )
. replace ( /\S+@\S+\.\S+/g , htmlEmail )
. replace ( /\(\d{1,2}\/\d{1,2}\)$/ , htmlSplitCounter )
;
return _formatText ( msg ) ;
}
}
function _extractUsername ( s ) {
function proxyURL ( url ) {
var username = "" ;
var proxyOpt = $ . Options . getUseProxyOpt ( ) ;
for ( var i = 0 ; i < s . length ; i ++ ) {
if ( proxyOpt !== 'disable' && ! $ . Options . getUseProxyForImgOnlyOpt ( ) ) {
var c = s . charCodeAt ( i ) ;
// proxy alternatives may be added to options page
if ( ( c >= 'a' . charCodeAt ( 0 ) && c <= 'z' . charCodeAt ( 0 ) ) ||
if ( proxyOpt === 'ssl-proxy-my-addr' ) {
( c >= 'A' . charCodeAt ( 0 ) && c <= 'Z' . charCodeAt ( 0 ) ) ||
url = [ 'https://ssl-proxy.my-addr.org/myaddrproxy.php/' ,
( c >= '0' . charCodeAt ( 0 ) && c <= '9' . charCodeAt ( 0 ) ) ||
url . substring ( 0 , url . indexOf ( ':' ) ) , url . substr ( url . indexOf ( '/' ) + 1 ) ] . join ( '' ) ;
c == '_' . charCodeAt ( 0 ) ) {
} else if ( proxyOpt === 'anonymouse' )
username += s [ i ] ;
url = [ 'http://anonymouse.org/cgi-bin/anon-www.cgi/' , url ] . join ( '' ) ;
} else {
break ;
}
}
}
return username . toLowerCase ( ) ;
return url ;
}
}
// internal function for htmlFormatMsg
// internal function for htmlFormatMsg
function _extractHashtag ( s ) {
// TODO: add options for emotions; msg = $.emotions(msg);
var hashtag = "" ;
// TODO: add at least basic markdown (optional) like *text* -> bold text and _text_ -> italic text
s = reverseHtmlEntities ( s ) ;
function _formatText ( msg ) {
for ( var i = 0 ; i < s . length ; i ++ ) {
if ( $ . Options . getLineFeedsOpt ( ) === 'enable' )
if ( " \n\t.,:/?!;'\"()[]{}*#" . indexOf ( s [ i ] ) < 0 ) {
msg = msg . replace ( /\n/g , '<br />' ) ;
hashtag += s [ i ] ;
} else {
return msg ;
break ;
}
}
return hashtag ;
}
}
function escapeHtmlEntities ( str ) {
function escapeHtmlEntities ( str ) {