mirror of
https://github.com/twisterarmy/twister-html.git
synced 2025-01-27 15:14:40 +00:00
cc1c5e5d11
still a lot to do: using the "from" field from group messages to display who sent each message, options to invite, display members, change group description, leave etc.
371 lines
14 KiB
JavaScript
371 lines
14 KiB
JavaScript
// 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');
|
|
elem.addClass(kind);
|
|
elem.attr('data-time', t);
|
|
|
|
if( post["isNew"] )
|
|
elem.addClass('new');
|
|
|
|
var postData = elem.find(".post-data");
|
|
postData.addClass(kind);
|
|
postData.attr('data-userpost', postJson);
|
|
postData.attr('data-content_to_rt', content_to_rt);
|
|
postData.attr('data-content_to_sigrt', content_to_sigrt);
|
|
postData.attr('data-screen-name', n);
|
|
postData.attr('data-id', k);
|
|
postData.attr('data-lastk', userpost["lastk"]);
|
|
postData.attr('data-text', msg);
|
|
if( "reply" in userpost ) {
|
|
postData.attr('data-replied-to-screen-name', userpost["reply"]["n"]);
|
|
postData.attr('data-replied-to-id', userpost["reply"]["k"]);
|
|
|
|
postData.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"]);
|
|
postData.attr('data-replied-to-id', userpost["rt"]["reply"]["k"]);
|
|
|
|
postData.find('.post-expand').text(polyglot.t("Show conversation"));
|
|
}
|
|
|
|
var postInfoName = elem.find(".post-info-name");
|
|
postInfoName.attr('href',$.MAL.userUrl(n));
|
|
postInfoName.text(n);
|
|
getFullname( n, postInfoName );
|
|
elem.find(".post-info-tag").text = "@" + n;
|
|
getAvatar( n, elem.find(".avatar") );
|
|
elem.find(".post-info-time").text(timeGmtToText(t));
|
|
elem.find(".post-info-time").attr("title",timeSincePost(t));
|
|
|
|
var mentions = [];
|
|
htmlFormatMsg( msg, elem.find(".post-text"), 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] + " ";
|
|
}
|
|
}
|
|
if(!defaultScreenName)
|
|
{
|
|
elem.find(".post-area-new textarea").attr("placeholder", polyglot.t("You have to log in to post replies."));
|
|
}
|
|
else
|
|
{
|
|
elem.find(".post-area-new textarea").attr("placeholder", polyglot.t("reply_to", { fullname: replyTo })+ "...");
|
|
}
|
|
elem.find(".post-area-new textarea").attr("data-reply-to",replyTo);
|
|
postData.attr("data-reply-to",replyTo);
|
|
|
|
if( retweeted_by != undefined ) {
|
|
elem.find(".post-context").show();
|
|
var retweetedByElem = elem.find(".post-retransmited-by");
|
|
retweetedByElem.attr("href", $.MAL.userUrl(retweeted_by));
|
|
retweetedByElem.text('@'+retweeted_by);
|
|
}
|
|
|
|
if (typeof(promoted) !== 'undefined' && promoted) {
|
|
elem.find('.post-propagate').remove();
|
|
} else {
|
|
if ($.Options.getFilterLangOpt() !== 'disable' && $.Options.getFilterLangSimulateOpt()) {
|
|
// 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>');
|
|
elem.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;
|
|
}
|
|
|
|
// 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));
|
|
dmItem.find(".post-info-time").attr("title",timeSincePost(dmData.time));
|
|
|
|
return dmItem;
|
|
}
|
|
|
|
// format dmdata (returned by getdirectmsgs) to display in conversation thread
|
|
function dmDataToConversationItem(dmData, localUser, remoteUser) {
|
|
var classDm = dmData.fromMe ? "sent" : "received";
|
|
var dmItem = $("#dm-chat-template").clone(true);
|
|
dmItem.removeAttr('id');
|
|
dmItem.addClass(classDm);
|
|
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").attr("title",timeSincePost(dmData.time));
|
|
var mentions = [];
|
|
htmlFormatMsg( dmData.text, dmItem.find(".post-text"), mentions);
|
|
return dmItem;
|
|
}
|
|
|
|
// convert message text to html, featuring @users and links formating.
|
|
// todo: hashtags
|
|
function htmlFormatMsg( msg, output, mentions ) {
|
|
var tmp;
|
|
var match = null;
|
|
var index;
|
|
var strUrlRegexp = "http[s]?://";
|
|
var strEmailRegexp = "\\S+@\\S+\\.\\S+";
|
|
var strSplitCounterR = "\\(\\d{1,2}\\/\\d{1,2}\\)$";
|
|
var reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + "|" + strSplitCounterR + ")");
|
|
var reHttp = new RegExp(strUrlRegexp);
|
|
var reEmail = new RegExp(strEmailRegexp);
|
|
var reSplitCounter = new RegExp(strSplitCounterR);
|
|
|
|
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]) ) {
|
|
output.append(_formatText(msg.substr(0, index)));
|
|
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])) {
|
|
output.append(_formatText(msg.substr(0, index)));
|
|
tmp = msg.substring(index);
|
|
if( tmp.length ) {
|
|
var splitCounter = $('<span class="splited-post-counter"></span>');
|
|
splitCounter.text(tmp);
|
|
output.append(splitCounter);
|
|
msg = "";
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
output.append(_formatText(msg));
|
|
msg = "";
|
|
}
|
|
}
|
|
|
|
// internal function for htmlFormatMsg
|
|
function _formatText(msg)
|
|
{
|
|
// TODO: add options for emotions and linefeeds
|
|
//msg = $.emotions(msg);
|
|
if( $.Options.getLineFeedsOpt() == "enable" )
|
|
msg = msg.replace(/\n/g, '<br />');
|
|
return msg;
|
|
}
|
|
|
|
function _extractUsername(s) {
|
|
var username = "";
|
|
for( var i = 0; i < s.length; i++ ) {
|
|
var c = s.charCodeAt(i);
|
|
if( (c >= 'a'.charCodeAt(0) && c <= 'z'.charCodeAt(0)) ||
|
|
(c >= 'A'.charCodeAt(0) && c <= 'Z'.charCodeAt(0)) ||
|
|
(c >= '0'.charCodeAt(0) && c <= '9'.charCodeAt(0)) ||
|
|
c == '_'.charCodeAt(0) ) {
|
|
username += s[i];
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return username.toLowerCase();
|
|
}
|
|
|
|
// internal function for htmlFormatMsg
|
|
function _extractHashtag(s) {
|
|
var hashtag = "";
|
|
s = reverseHtmlEntities(s);
|
|
for( var i = 0; i < s.length; i++ ) {
|
|
if( " \n\t.,:/?!;'\"()[]{}*#".indexOf(s[i]) < 0 ) {
|
|
hashtag += s[i];
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return hashtag;
|
|
}
|
|
|
|
function escapeHtmlEntities(str) {
|
|
return str
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function reverseHtmlEntities(str) {
|
|
return str
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, "'")
|
|
.replace(/&/g, '&');
|
|
}
|
|
|