// 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 ) {
/*
"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);
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-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"]);
}
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] + " ";
}
}
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);
}
//hed//media preview
var previewContainer = elem.find('.preview-container'), postText = elem.find(".post-text");
var postLink = postText.find("a[rel='nofollow']")[0] ? postText.find("a[rel='nofollow']")[0].href : '';
var ytRegExp = /(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?/i;
var vimeoRegExp = /http:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/i;
if (postLink && localStorage['imagesPreview'] == 'enable' && (/(\.jpg)|(\.gif)|(\.png)|(\.jpeg)|(\.jpe)/i.test(postLink) || /https:\/\/img.bi/i.test(postLink))){
previewContainer.show();
previewContainer.append(imagePreview(postLink));
}else if(postLink && ytRegExp.test(postLink) && localStorage['youtubePreview'] === 'enable'){
var ytid = postLink.match(ytRegExp) ? RegExp.$1 : false;
previewContainer.show();
previewContainer.attr('data-youtube-id', ytid);
previewContainer.append(getYoutubePreview(postLink, ytid));
}else if(postLink && vimeoRegExp.test(postLink) && localStorage['vimeoPreview'] === 'enable'){
var vimid = postLink.match(vimeoRegExp) ? RegExp.$2 : false;
previewContainer.show();
previewContainer.attr('data-vimeo-id', vimid);
previewContainer.append(getVimeoPreview(postLink, vimid));
}
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") );
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 reAll = new RegExp("(?:^|[ \\n\\t.,:\\/?!])(#|@|" + strUrlRegexp + "|" + strEmailRegexp + ")");
var reHttp = new RegExp(strUrlRegexp);
var reEmail = new RegExp(strEmailRegexp);
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 = url.replace('&', '&');
var extLinkTemplate = $("#external-page-link-template").clone(true);
extLinkTemplate.removeAttr("id");
extLinkTemplate.attr("href",url);
extLinkTemplate.html(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.html(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;
}
}
output.append(_formatText(msg));
msg = "";
}
}
// internal function for htmlFormatMsg
function _formatText(msg)
{
msg = $.emotions(msg);
msg = msg.replace(/\n/g, '
');
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 = "";
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('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''');
}
function imagePreview(link) {
var linkAnon = 'https://ssl-proxy.my-addr.org/myaddrproxy.php/http/';
if (link && /https:\/\/img.bi/i.test(link)){
return '
';
//imgBiJS();
}else{
var cleanLink = link.replace(/^http[s]?:\/\//i, '');
if(/\.gif\b/i.test(cleanLink) && localStorage['imagesPreviewGif'] == 'false') return;
return '
';
}
}
function getYoutubePreview(link, ytid) {
var vidPreviewTmpl = $('#vidPreviewTmpl').clone(true).removeAttr('style').removeAttr('class').addClass("youtube");
var ytDataStorage = localStorage['ytData'] ? JSON.parse(localStorage['ytData']) : {};
if (ytDataStorage[ytid]) {
vidPreviewTmpl.find('img').attr('src', ytDataStorage[ytid].thumbnail);
vidPreviewTmpl.find('a').text(ytDataStorage[ytid].title).attr('href', link).attr('target', '_blank');
if (ytDataStorage[ytid].description) vidPreviewTmpl.find('p').html(ytDataStorage[ytid].description+'…');
return vidPreviewTmpl;
}else{
$.ajax({
url: "http://gdata.youtube.com/feeds/api/videos/"+ytid+"?v=2&alt=jsonc",
dataType: 'jsonp',
success: function(data) {
ytDataStorage[ytid] = {
title: data.data.title,
description: data.data.description.substring(0, 400),
thumbnail: data.data.thumbnail.hqDefault,
link: 'http://youtu.be/'+ytid,
time: Date.now()
};
localStorage['ytData'] = JSON.stringify(ytDataStorage);
vidPreviewTmpl.find('img').attr('src', ytDataStorage[ytid].thumbnail);
vidPreviewTmpl.find('a').text(ytDataStorage[ytid].title).attr('href', link).attr('target', '_blank');
if (ytDataStorage[ytid].description) vidPreviewTmpl.find('p').html(ytDataStorage[ytid].description+'…');
$('[data-youtube-id='+ytid+']').append(vidPreviewTmpl);
}
});
}
}
function getVimeoPreview (link, vimid) {
var vidPreviewTmpl = $('#vidPreviewTmpl').clone(true).removeAttr('style').removeAttr('class').addClass("vimeo");
var vimDataStorage = localStorage['vimData'] ? JSON.parse(localStorage['vimData']) : {};
if (vimDataStorage[vimid]) {
vidPreviewTmpl.find('img').attr('src', vimDataStorage[vimid].thumbnail);
vidPreviewTmpl.find('a').text(vimDataStorage[vimid].title).attr('href', link).attr('target', '_blank');
if (vimDataStorage[vimid].description) vidPreviewTmpl.find('p').html(vimDataStorage[vimid].description+'…');
return vidPreviewTmpl;
}else{
$.ajax({
url: "http://vimeo.com/api/v2/video/"+vimid+".json",
dataType: 'json',
success: function(data) {
vimDataStorage[vimid] = {
title: data[0].title,
description: data[0].description.substring(0, 400),
thumbnail: data[0].thumbnail_large,
link: data[0].url,
time: Date.now()
};
localStorage['vimData'] = JSON.stringify(vimDataStorage);
vidPreviewTmpl.find('img').attr('src', vimDataStorage[vimid].thumbnail);
vidPreviewTmpl.find('a').text(vimDataStorage[vimid].title).attr('href', link).attr('target', '_blank');
if (vimDataStorage[vimid].description) vidPreviewTmpl.find('p').html(vimDataStorage[vimid].description+'…');
$('[data-vimeo-id='+vimid+']').append(vidPreviewTmpl);
}
});
}
}