").text(text).html();
}
// https://gist.github.com/442463
function linkify_entities(tweet) {
if (!(tweet.entities)) {
return escapeHTML(tweet.text)
}
// This is very naive, should find a better way to parse this
var index_map = {}
$.each(tweet.entities.urls, function(i,entry) {
index_map[entry.indices[0]] = [entry.indices[1], function(text) {return ""}]
})
$.each(tweet.entities.hashtags, function(i,entry) {
index_map[entry.indices[0]] = [entry.indices[1], function(text) {return ""}]
})
$.each(tweet.entities.user_mentions, function(i,entry) {
index_map[entry.indices[0]] = [entry.indices[1], function(text) {return ""}]
})
if (tweet.entities.media !== undefined) {
$.each(tweet.entities.media, function(i,entry) {
index_map[entry.indices[0]] = [entry.indices[1], function(text) {return ""}]
})
}
var result = ""
var last_i = 0
var i = 0
// iterate through the string looking for matches in the index_map
for (i=0; i < tweet.text.length; ++i) {
var ind = index_map[i]
if (ind) {
var end = ind[0]
var func = ind[1]
if (i > last_i) {
result += escapeHTML(tweet.text.substring(last_i, i))
}
result += func(tweet.text.substring(i, end))
i = end - 1
last_i = end
}
}
if (i > last_i) {
result += escapeHTML(tweet.text.substring(last_i, i))
}
return result
}
var birdsPossible = {
'randomBird1':120,
'randomBird2':65,
'randomBird3':100,
'randomBird4':78,
'randomBird5':97,
'randomBird6':103,
'randomBird7':120,
'randomBird8':100,
'randomBird9':100,
'randomBird10':110,
'randomBird11':110,
'_max':10
};
function drawRandomBird() {
if (Math.random() <= 0.95) {
tweetdisplay.birdimg.hide();
return;
}
// return positionnodesPossible.verycentercolor;
var idx = Math.round(Math.random()*(birdsPossible._max));
var i = 0;
for (var key in birdsPossible) {
if (i==idx) {
tweetdisplay.birdimg.attr("src", "img/"+key).css("top","-"+birdsPossible[key]+"px");
tweetdisplay.birdimg.show();
return;
}
i++;
}
if (DEBUG) {console.log("?????");}
return undefined;
}
function displayTweet(data) {
if (DEBUG > 1) {
console.log("displaying tweet: ");
console.log(data);
}
var pattern,
txt=linkify_entities(data);
// color our tags
for(var i in state.tag2color) {
pattern = new RegExp("("+i+")", 'gi');
txt = txt.replace(pattern,'
$1');
}
// msg.html(data.from_user_name+": "+txt+" ("+data.created_at+")");
var mediacontainercontent = "";
if (options.option_show_images && (data.entities.media != undefined)) {
$.each(data.entities.media, function(i,entry) {
if (entry.type == "photo") {
var size = undefined;
if (entry.sizes.thumb != undefined) { size = "thumb"; }
else if (entry.sizes.small != undefined) { size = "small"; }
else if (entry.sizes.orig != undefined) { size = "orig"; }
else if (entry.sizes.large != undefined) { size = "large"; }
if (size != "thumb")
console.log("size: "+size);
if (size != undefined)
mediacontainercontent+= "
";
}
});
}
// TOTO
tweetdisplay.content.html(txt);
tweetdisplay.authoravatar.src = "img/twitter-egg.jpg"; // first call; so if we are unable to load the true one, this will be obious
tweetdisplay.authoravatar.src = data.profile_image_url;
tweetdisplay.authorlinks.attr("href", "https://twitter.com/intent/user?user_id="+data.from_user_id_str);
tweetdisplay.authorname.html(data.from_user_name);
tweetdisplay.authorusername.html("@"+data.from_user);
tweetdisplay.timestamp.html("
"+toTwitterDate(data.created_at)+"");
tweetdisplay.actionReplyLink.attr("href", "https://twitter.com/intent/tweet?in_reply_to="+data.id_str);
tweetdisplay.actionRetweetLink.attr("href", "https://twitter.com/intent/retweet?tweet_id="+data.id_str);
tweetdisplay.actionFavoriteLink.attr("href", "https://twitter.com/intent/favorite?tweet_id="+data.id_str);
//tweetdisplay.actionFollow.attr("href", "https://twitter.com/"+data.from_user_name);
// twttr.widgets.load();
tweetdisplay.actionFollow.attr("src", "//platform.twitter.com/widgets/follow_button.html?show_count=false&lang=en&show_screen_name=false&screen_name="+data.from_user);
tweetdisplay.mediacontainer.html(mediacontainercontent);
//tweetdisplay.birdimg.attr("src", "img/randomBird1.png").css("top","-50px");
if (options.option_show_random_birds) { drawRandomBird(); }
showTweetDisplay();
// TOTO
// $('#messages').stop().fadeTo(100,1.0);
}
function showTweetDisplay() {
tweetdisplay.container.stop().fadeTo(100,1.0);
}
var positionnodesPossible = {
'randomp':0,
'verycenter':1,
'lineh':2,
'linexy':3,
'lineyx':4,
'bottom':5,
'bottomAndRight':6,
'right':7,
'left':8,
'circle':9,
'colorsBottom':10,
'colorsDiag':11,
'circleColor':12,
'circleoptionsColor2':13,
'linexyColor':14,
'linehColor':15,
'lineyxColor':16,
'bottomColor':17,
'verycentercolor':18,
'_max':18
};
//Object.freeze(positionnodesPossible);
function getRandomNodesPosition() {
// return positionnodesPossible.verycentercolor;
var idx = Math.round(Math.random()*(positionnodesPossible._max));
var i = 0;
for (var key in positionnodesPossible) {
if (i==idx) {
return positionnodesPossible[key];
}
i++;
}
if (DEBUG) {console.log("?????");}
return undefined;
}
var positionnodes = positionnodesPossible.randomp;
/**
* preload images
**/
function preload(imgSrc) {
if (DEBUG==2) { console.log("preloading: "+imgSrc); }
(new Image()).src = imgSrc;
}
function addNode(i, withdata) {
var nodecolor = computeNodeColor(withdata);
var xinit,yinit;
var rand = Math.random();
var rand2 = Math.random();
switch(positionnodes)
{ // select node position according to main position scheme
case positionnodesPossible.verycenter:
xinit = Math.floor((window.innerWidth)/2+(rand-0.5)*30);
yinit = Math.floor((window.innerHeight)/2+(rand2-0.5)*30);
break;
case positionnodesPossible.verycentercolor:
if (nodecolor == options.color1) {
xinit = Math.floor((window.innerWidth)/2+20+(rand)*15);
yinit = Math.floor((window.innerHeight)/2+20+(rand2)*15);
} else if (nodecolor == options.color2) {
xinit = Math.floor((window.innerWidth)/2-20-(rand)*15);
yinit = Math.floor((window.innerHeight)/2-20-(rand2)*15);
} else {
xinit = Math.floor((window.innerWidth)/2+(rand-0.5)*30);
yinit = Math.floor((window.innerHeight)/2+(rand2-0.5)*30);
}
break;
case positionnodesPossible.lineh:
xinit = Math.floor(rand*(window.innerWidth));
yinit = Math.floor((window.innerHeight)/2+(rand2-0.5)*5);
break;
case positionnodesPossible.linehColor:
xinit = Math.floor(rand*(window.innerWidth/2));
yinit = Math.floor((window.innerHeight)/2+(rand2-0.5)*5);
if (nodecolor == options.color1) {
} else if (nodecolor == options.color2) {
xinit = xinit+window.innerWidth/2;
} else {
xinit = xinit+window.innerWidth/4;
}
break;
case positionnodesPossible.linexy:
xinit = Math.floor(rand*(window.innerWidth)+(rand2-0.5)*5);
yinit = Math.floor(rand*(window.innerHeight)+(rand2-0.5)*5);
break;
case positionnodesPossible.linexyColor:
if (nodecolor == options.color1) {
rand = rand / 2;
} else if (nodecolor == options.color2) {
rand = 0.5 + rand/2;
}
xinit = Math.floor(rand*(window.innerWidth/2)+(rand2-0.5)*5);
yinit = Math.floor(rand*(window.innerHeight/2)+(rand2-0.5)*5);
break;
case positionnodesPossible.lineyx:
xinit = Math.floor(rand*(window.innerWidth)+(rand2-0.5)*5);
yinit = Math.floor((1-rand)*(window.innerHeight)+(rand2-0.5)*5);
break;
case positionnodesPossible.lineyxColor:
if (nodecolor == options.color1) {
rand = rand/2;
} else if (nodecolor == options.color2) {
rand = rand/2+0.5;
}
xinit = Math.floor(rand*(window.innerWidth)+(rand2-0.5)*5);
yinit = Math.floor((1-rand)*(window.innerHeight)+(rand2-0.5)*5);
break;
case positionnodesPossible.bottom:
xinit = Math.floor(rand*(window.innerWidth));
yinit = Math.floor(window.innerHeight*0.8+(rand2-0.5)*5);
break;
case positionnodesPossible.bottomColor:
if (nodecolor == options.color1) {
rand = rand/2;
} else if (nodecolor == options.color2) {
rand = rand/2+0.5;
}
xinit = Math.floor(rand*(window.innerWidth));
yinit = Math.floor(window.innerHeight*0.8+(rand2-0.5)*5);
break;
case positionnodesPossible.bottombottomAndRight:
if (Math.random() <= 0.5) {
xinit = Math.floor(rand*(window.innerWidth));
yinit = Math.floor(window.innerHeight*0.8+(rand2-0.5)*5);
} else {
xinit = Math.floor(0.8*(window.innerWidth)+(rand2-0.5)*5);
yinit = Math.floor(rand*window.innerHeight);
}
break;
case positionnodesPossible.right:
xinit = Math.floor(0.8*(window.innerWidth)+(rand2-0.5)*5);
yinit = Math.floor(rand*window.innerHeight);
break;
case positionnodesPossible.left:
xinit = Math.floor(0.15*(window.innerWidth)+(rand2-0.5)*5);
yinit = Math.floor(rand*window.innerHeight);
break;
case positionnodesPossible.circle:
var radius = window.innerHeight/3 + (rand2-0.5)*5;
var angle = rand*Math.PI*2;
xinit = Math.floor(Math.cos(angle)*radius + window.innerWidth/2);
yinit = Math.floor(Math.sin(angle)*radius + window.innerHeight/2);
break;
case positionnodesPossible.circleColor:
var radius;
var angle = rand*Math.PI*2;
if (nodecolor == options.color2) {
radius = window.innerHeight/5 + (rand2-0.5)*5;
} else if (nodecolor == options.color1) {
radius = window.innerHeight/2 + (rand2-0.5)*5;
} else {
radius = window.innerHeight/4 + (rand2-0.5)*5;
}
xinit = Math.floor(Math.cos(angle)*radius + window.innerWidth/2);
yinit = Math.floor(Math.sin(angle)*radius + window.innerHeight/2);
break;
case positionnodesPossible.circlecolor2:
var radius = window.innerHeight/2 + (rand2-0.5)*5;
var angle;
if (nodecolor == options.color1) {
angle = Math.PI*2/3+rand*Math.PI;
} else if (nodecolor == options.color2) {
angle = Math.PI*2/3-rand*Math.PI;
} else {
angle = rand*Math.PI*2;
}
xinit = Math.floor(Math.cos(angle)*radius + window.innerWidth/2);
yinit = Math.floor(Math.sin(angle)*radius + window.innerHeight/2);
break;
case positionnodesPossible.colorsBottom:
if (nodecolor == options.color1) {
xinit = Math.floor(window.innerWidth/4+(rand-0.5)*20);
} else if (nodecolor == options.color2) {
xinit = Math.floor(window.innerWidth*3/4+(rand-0.5)*20);
} else {
xinit = Math.floor(window.innerWidth/2+(rand-0.5)*20);
}
yinit = Math.floor(window.innerHeight*3/4+(rand2-0.5)*5);
break;
case positionnodesPossible.colorsDiag:
if (nodecolor == options.color1) {
xinit = Math.floor(window.innerWidth/4+(rand-0.5)*20);
yinit = Math.floor(window.innerHeight*3/4+(rand2-0.5)*5);
} else if (nodecolor == options.color2) {
xinit = Math.floor(window.innerWidth*3/4+(rand-0.5)*20);
yinit = Math.floor(window.innerHeight/4+(rand2-0.5)*5);
} else {
xinit = Math.floor(window.innerWidth/2+(rand-0.5)*20);
yinit = Math.floor(window.innerHeight/2+(rand2-0.5)*5);
}
break;
case positionnodesPossible.randomp:
default:
var random3 = Math.random();
var random4 = Math.random();
if (random3 <= 0.5) {
xinit = (random4 <= 0.5 ? Math.floor(rand*(window.innerWidth/3)) : Math.floor((rand+2)*(window.innerWidth/3)));
yinit = Math.floor(rand2*window.innerHeight);
} else {
xinit = Math.floor(rand*window.innerWidth);
yinit = (random4 <= 0.3 ? Math.floor(rand2*(window.innerHeight/4)) : Math.floor((rand2+3)*(window.innerHeight/4)));
}
break;
}
// console.log("x:"+xinit+",y:"+yinit);
// remove node(s) if required
if (nodes.length > options.maxnodes) {
removeNodes(nodes.length-options.maxnodes);
}
// add the node
var novelNode = nodes.push({x: xinit, y: yinit, name:i, data: withdata, radius: computeNodeRadius(withdata), color:nodecolor });
var thenode = nodes[novelNode-1];
state.id2node[''+i]=thenode;
restart();
// display ?
if (options.option_display_last && (state.nodeselected == undefined) && (state.nodeselectedtmp == undefined) && (state.stateTweetsAutoDisplay.timestamp_lasttweet_displayed == undefined || (Date.now() - state.stateTweetsAutoDisplay.timestamp_lasttweet_displayed) > 2500)) {
state.stateTweetsAutoDisplay.timestamp_lasttweet_displayed = Date.now();
if (state.nodeselectedAuto !== undefined) {
state.nodeselectedAuto.selected = false;
}
displayTweet(withdata);
thenode.selected = true;
state.nodeselectedAuto = thenode;
}
// preload data
if (options.option_preload_avatars) {
preload(withdata.profile_image_url);
};
if (options.option_preload_images && withdata.entities.media != undefined) {
$.each(withdata.entities.media, function(i,entry) {
var size = undefined;
if (entry.sizes.thumb != undefined) { size = "thumb"; }
else if (entry.sizes.small != undefined) { size = "small"; }
else if (entry.sizes.orig != undefined) { size = "orig" };
if (size != undefined)
preload(entry.media_url+":"+size);
});
}
}
function computeNodeRadius(withdata) {
return Math.round(Math.min(state.rationodesize,(4+withdata.text.length/(state.rationodesize+1))));
}
function computeNodeColor(withdata) {
var len = 0;
// TODO allow mixed colors (with stroke)
// try to determine the fill color from hashtags
if (withdata.entities.hashtags !== undefined) {
len = withdata.entities.hashtags.length;
for(var i=0; i
0) {
state.tag2color[content.toLowerCase()] = color;
}
}
function changedParamsManu() {
// mmm, unselect the preset, thanks
$("#preset").val([]);
changedParams();
}
function changedParams() {
// cancel previous tweet updates
stopLoadUpdate();
state.isInitialLoad = true;
// update the map of tag / color
state.tag2color = {};
maybeLoad("aA",options.color1); maybeLoad("aB",options.color1); maybeLoad("aC",options.color1);
maybeLoad("bA",options.color2); maybeLoad("bB",options.color2); maybeLoad("bC",options.color2);
// update the "searched" part of the url
searched = '';
state.currentUrlLink = '';
state.currentUrlLinkLeft = '';
state.currentUrlLinkRight = '';
for(var i in state.tag2color) {
if (searched.length > 0) {
searched = searched + " OR ";
}
if (i.indexOf(" ")!=-1) {
// if space, enclose into quotes
searched = searched + "\"" + i + "\"";
} else {
searched = searched + i;
}
if (state.tag2color[i]==options.color1) {
if (state.currentUrlLinkLeft.length > 0) {
state.currentUrlLinkLeft = state.currentUrlLinkLeft + ",";
}
state.currentUrlLinkLeft = state.currentUrlLinkLeft + i;
} else {
if (state.currentUrlLinkRight.length > 0) {
state.currentUrlLinkRight = state.currentUrlLinkRight + ",";
}
state.currentUrlLinkRight = state.currentUrlLinkRight + i;
}
}
state.currentUrlLink = window.location.protocol+"//"+window.location.host+window.location.pathname+"?query="+encodeURIComponent(state.currentUrlLinkLeft + "/" + state.currentUrlLinkRight);
// TODO update !
// addthis_share["url"] = state.currentUrlLink;
if (addthis !== undefined) { // if the script is not loaded, still continue our work
addthis.update('share','url', state.currentUrlLink);
}
searched = encodeURIComponent(searched);
lastRetrieved = 1;
// clear the current image which is no more relevant
clear();
restart();
// restart the update of tweets
updateTweets();
}
var retrieved;
/*
function displayMessage(idmessage) {
var msg = '';
switch (idmessage)
{
case 'loading':
msg = "contacting the twitter server...";
break;
case 'indirect':
msg = "Twitter is not accessible from your network, attempting to contact our relay...";
break;
case 'full':
msg = "Your screen is full, update stopped.";
break;
case 'noupdate':
msg = "Waiting for server info...";
break;
default:
return;
break;
}
$('#msgcentral').stop().html(msg).show();
//fadeTo(2000,0.8)
}
*/
function clearMessage() {
$('#msgcentral').stop().hide().html("");
}
function presedChoosed() {
loadFromShort(guiElements.formparams.elements["preset"].value);
}
function randompreset() {
var oldvalue = guiElements.formparams.elements["preset"].value;
while (guiElements.formparams.elements["preset"].value == oldvalue) { // avoid to randomly select the same option
guiElements.formparams.elements["preset"].options[Math.floor(guiElements.formparams.elements["preset"].options.length*Math.random())].selected = "1";
}
presedChoosed();
}
function loadFromShort(txt) {
var one = txt.split("/");
if (one.length != 2) {
return; // error !
}
var as = one[0].split(",");
var bs = one[1].split(",");
guiElements.formparams.elements["aA"].value = (as[0] === undefined ? "": as[0]);
guiElements.formparams.elements["aB"].value = (as[1] === undefined ? "": as[1]);
guiElements.formparams.elements["aC"].value = (as[2] === undefined ? "": as[2]);
guiElements.formparams.elements["bA"].value = (bs[0] === undefined ? "": bs[0]);
guiElements.formparams.elements["bB"].value = (bs[1] === undefined ? "": bs[1]);
guiElements.formparams.elements["bC"].value = (bs[2] === undefined ? "": bs[2]);
changedParams();
}
function planTweeterUpdate(delay) {
if (updateHolder !== undefined) {
clearTimeout(updateHolder);
}
if (DEBUG) {
console.log("planning next tweeter update for "+delay);
}
updateHolder = setTimeout('updateTweets()', delay);
}
function filterDisplayResult(item) {
var len = 0;
// basic filter: remove the tweets with too many hashtags (that's a kind of spam)
if (item.entities.hashtags !== undefined) {
len1 = item.entities.hashtags.length;
if (len1 >= 7) {
return false;
}
len += len1;
}
// basic filter: remove the tweets with too many user mentions
if (item.entities.user_mentions !== undefined) {
len2 = item.entities.user_mentions.length;
if (len2 >= 5) {
return false;
}
len += len2;
}
// total filter: too many references = spam
if (len >= 8) {
return false;
}
// color: if we are unable to color them, hide them
if (computeNodeColor(item) === options.colorDefault) {
return false;
}
return true;
}
function hideUpdate() {
$("#wait").hide();
}
var lastRetrievedNext;
function processResults(data) {
if (data == null || data.error !== undefined) {
if (DEBUG) { console.log("error detected: "+(data != null ? data.error:"(unknown)")+"; will try to refresh"); }
planTweeterUpdate(options.delayRetryError);
displayMessage('noupdate');
return;
}
//clearMessage();
hideUpdate();
retrieved = data;
lastRetrievedNext = data.max_id_str;
var date, diff, tmpholderl
var now = (new Date()).getTime();
// first nodes added using one random method
positionnodes = getRandomNodesPosition();
$.each(data.results, function (i, item) {
// filter spams
if (!filterDisplayResult(item)) {
return;
}
if (options.option_filter_withdataonly) {
if (item.entities.media == undefined || item.entities.media.length == 0)
return;
}
date = (new Date(item.created_at)).getTime();
diff = (date - diffHour - now + options.delay + 20000);
if (DEBUG>1) { console.log("diffHour:"+diffHour+",date:"+date+",now:"+now+"; "+diff); }
if (diff < 50) {
// mmm... too old, display it right now
addNodeIfNecessary(item.id, item);
} else {
// this is a recent tweet; replay how it appears !
if (DEBUG>1) { console.log("options.delayed by "+diff); };
var diff2 = diff;
tmpholder = setTimeout(function () {
if (DEBUG>1) { console.log("display after waiting "+diff2);}
addNodeIfNecessary(item.id, item);
}, diff+Math.floor(500*Math.random())
);
holders.push(tmpholder);
}
});
// later the nodes (not displayed now, but planned) will be added from random positions
positionnodes = positionnodesPossible.randomp;
if ((data.next_page !== undefined) && ( !state.isInitialLoad || (nodes.length == 0) ) ) {
// all the nodes will be displayed later !
// attempt to load more, in order to avoid displaying an empty screen for the next 2 minutes :-/
if (DEBUG) { console.log("LOADING NEXT PAGE !"); }
if (options.mode == 'direct') {
updateTweetsFromURL(options.URL_BASE_DIRECT + data.next_page+"&since_id="+lastRetrieved + "&callback=?");
} else {
updateTweetsFromURL(options.URL_BASE_INDIRECT + data.next_page+"&since_id="+lastRetrieved);
}
} else {
// we are not loading any other page... let's plan an update ?
// plan next update !
planTweeterUpdate(options.delay);
state.isInitialLoad = false;
lastRetrieved = lastRetrievedNext;
}
}
var err ;
function updateTweetsFromURL (url) {
if (DEBUG) { console.log("LOADING "+url); }
$.ajax({
url: url,
dataType: 'json',
data: {},
success: processResults,
timeout: options.timeoutGetTweets,
error: function(jqXHR, status, errorThrown){ //the status returned will be "timeout"
if (DEBUG) { console.log("error: "+errorThrown); }
//$("#wait").hide();
//displayMessage('indirect');
planTweeterUpdate(options.delayRetryError);
}
});
}
function updateTweets() {
// no more pending
updateHolder = undefined;
var url2 = "";
if (options.mode == 'direct') {
url2 = options.URL_BASE_DIRECT+'?callback=?&';
} else {
url2 = options.URL_BASE_INDIRECT + '?';
}
url2 = url2+'q='+searched+'&since_id='+lastRetrieved+"&rpp=100&result_type=mixed&include_entities=1"; // old &include_rts=1
url = url2;
//displayMessage('loading');
$("#wait").show();
if (DEBUG) { console.log("refresh from url: "+url); }
updateTweetsFromURL(url);
}
function initLoad() {
if (urlParams.preset !== undefined) {
var requiredIdx = parseInt(urlParams.preset);
if (requiredIdx != NaN) {
if (DEBUG) { console.log("loading preset with idx: "+requiredIdx); }
// attempts to use this parameter to set the value
guiElements.formparams.elements["preset"].options[urlParams.preset].selected = "1";
presedChoosed();
return;
}
}
if (urlParams.query !== undefined) {
if (DEBUG) { console.log("loading query : "+urlParams.query); }
loadFromShort(decodeURI(urlParams.query));
return;
}
// default
randompreset();
}
function refresh() {
changedParams();
}
function initEverything() {
$("#stop").click(function(){ stopLoadUpdate(); state.force.stop(); });
$("#refresh").click(function(){ refresh(); });
// don't hide the display when the user is willing to interact with it !
tweetdisplay.container.mouseover(function() {
showTweetDisplay();
});
window.onload = initLoad();
}
initEverything();