var SECONDS = 1000,
MINUTES = 60 * SECONDS,
HOURS = 60 * MINUTES;
var config = {
partnerCode: 'B00015838755',
originalDuration: 24 * HOURS,
vizDuration: 8 * MINUTES,
colorIds: {
"-2.0": 2,
"0.5": 0, "1.0": 0, "1.5": 0, "2.0": 0,
"2.5": 1, "3.0": 1, "3.5": 1,
"4.0": 2, "4.5": 2, "5.0": 2
},
movieCount: 10,
opinionsPerPage: 100,
colors: [ "#f62a43", "#f3c000", "#2d9252"],
borderColors: [ "#f7788e", "#fdde83", "#3bb767" ],
stripeColors: [ "#c92238", "#c79e03", "#237844" ],
refreshRate: .25 * SECONDS,
columnSpacing: 40,
columnWidth: 150,
tokenHeight: 16,
fallSpeed: 4,
disappearAfter: 4 * SECONDS
}
$(function() {
var originalStart = Date.now() - config.originalDuration,
timeScale = config.vizDuration / config.originalDuration,
movies,
moviesToLoad,
opinions = [],
loadTime = Date.now(),
clockInterval,
tzDelta = 0,
tzSuffix = 'Z';
// Add Patterns
var xml = '<svg style="position: absolute;" width="1" height="1"><defs>'
+ config.colors.map(function(color, i) {
return '<pattern id="pattern_'
+ i
+ '" x="0" y="0" width="3" height="3" patternUnits="userSpaceOnUse"><rect x="0" y="0" width="3" height="3" fill="'
+ color
+ '" />'
+ _.range(3).map(function(n) {
return '<rect x="'
+ n
+ '" y="'
+ n
+ '" width="1" height="1" fill="'
+ config.stripeColors[i]
+ '" />'
}).join("")
+ '</pattern>';
}).join("")
+ '</defs></svg>';
$("body").append(xml);
function resizeTimer() {
var w = Math.max(80, $(window).width() - 272),
n = Math.min(12, Math.floor(w / 80));
$('.timer-bar').width(w);
var html = '';
for (var k = 0; k < n + 1; k++) {
html += '<li style="left:'
+ Math.floor(k * w / n)
+ 'px;">'
+ new Date(originalStart + k * config.originalDuration / n).toTimeString().substr(0,5)
+ '</li>';
}
$('.hours').html(html);
}
resizeTimer();
$(window).resize(resizeTimer);
var vsHeight = $(window).height() - $('footer').height() - $('header').height() - 300,
vsWidth = config.movieCount * (config.columnWidth + config.columnSpacing);
$("#data-viz").css('height', vsHeight);
$('.posters').css('width', vsWidth);
var timeAtPause = 0, paused = true;
function currentVizTime() {
if (paused) {
return timeAtPause;
} else {
return new Date().valueOf() - timeStart;
}
}
function vizTimeToOriginalTime(t) {
return originalStart + t / timeScale;
}
function originalTimeToVizTime(t) {
return (t - originalStart) * timeScale;
}
function play() {
if (!paused) {
return;
}
timeStart = new Date().valueOf() - timeAtPause;
paused = false;
$(".play-pause").removeClass("play").addClass("pause").text("Pause");
}
function pause() {
if (paused) {
return;
}
timeAtPause = currentVizTime();
paused = true;
$(".play-pause").removeClass("pause").addClass("play").text("Lecture");
}
$(".play-pause").click(function() {
if (paused) {
play();
} else {
pause();
}
});
function startViz() {
timeAtPause = 0;
opinions.sort(function(a,b) {
return a._timestamp > b._timestamp;
});
$("#data-viz").html("").css("text-align","left");
var lastShownToken = null,
maxData = Math.max.apply(Math, movies.map(function(m) { return m.opinions.length; })),
aggrHeight = vsHeight - 80,
aggrScale = aggrHeight / maxData;
var barChartSettings = {
width : vsWidth,
height : vsHeight,
chart : {
spacer : config.columnSpacing / 2,
y: 0,
height: vsHeight
},
options : {
layout: false
},
data : {
model : movies.map(function(movie) {
return {
label: movie.title
};
}),
strata : function() {
if (typeof barChart === "undefined") {
var flocculated = [];
_.range(3).map(function(colorId) {
$(".layer_"+colorId).css({
fill: "url(#pattern_" +colorId + ")",
stroke: config.borderColors[colorId]
});
});
} else {
var flocculated = barChart.selectAll("state",2);
}
var res = movies.map(function(movie, i) {
var movieTokens = flocculated.filter(function(t) {
return t.attr("movie") === movie.id;
});
var movieHeight = aggrScale * movieTokens.length;
movie.poster$.find(".poster-overlay").css("top", -movieHeight);
return _.range(3).map(function(colorId) {
var stratumTokens = movieTokens.filter(function(t) {
return t.attr("colorId") === colorId;
}).length;
movie.poster$.find(".poster-" + colorId).text(stratumTokens);
return {
value: function() {
return stratumTokens
}
};
});
});
return res;
},
stream : {
provider : "direct"
}
},
sedimentation: {
token: {
size: {
original:12,
minimum:0
}
},
aggregation:{
height: aggrHeight,
maxData: maxData
},
suspension:{
decay:{
power:1.001
}
}
}
}
function onTokenMouseover(token) {
if (token !== lastShownToken) {
$("body").css("cursor", "pointer");
var h = "Il y a ",
deltaT = Date.now() - token.attr("timestamp"),
deltaH = Math.round(deltaT / HOURS);
if (deltaH) {
h += deltaH + " heure";
if (deltaH > 1) {
h += "s";
}
} else {
var deltaM = Math.round(deltaT / MINUTES);
h += deltaM + " minute";
if (deltaM > 1) {
h += "s";
}
}
commentDiv.find(".comment-content").removeClass("userreview wanttosee rate").addClass(token.attr("actiontype"));
commentDiv.find(".stars-rating").attr("className","stars-rating rate-"+token.attr("actionvalue"));
commentDiv.find(".review-content").text(token.attr("content"));
commentDiv.find(".date").text(h);
commentDiv.find(".avatar img").attr("src", token.attr("avatar"));
commentDiv.find(".username").text(token.attr("username"));
commentDiv.show();
lastShownToken = token;
}
}
function onTokenMouseout(token) {
lastShownToken = null;
setTimeout(function() {
if (!lastShownToken) {
$("body").css("cursor", "");
commentDiv.hide();
}
}, 200);
}
console.log(barChartSettings);
var barChart = $("#data-viz").vs(barChartSettings).data('visualSedimentation');
barChart.world.SetGravity(new barChart.phy.b2Vec2(0,10 * config.fallSpeed));
$(".timer-bar-value").width(0);
play();
clearInterval(clockInterval);
var lastTimestamp = 0;
clockInterval = setInterval(function() {
if (paused) {
return;
}
var vizT = currentVizTime();
var originalT = vizTimeToOriginalTime(vizT),
tokensToAdd = opinions.filter(function(o) {
return o._timestamp > lastTimestamp && o._timestamp <= originalT
});
lastTimestamp = originalT;
tokensToAdd.forEach(function(opinion) {
barChart.addToken({
category: opinion._column,
datetime: opinion.datetime,
timestamp: opinion._timestamp,
viztime: originalTimeToVizTime(opinion._timestamp),
movie: opinion._movie,
colorId: opinion._colorId,
fillStyle: config.colors[opinion._colorId],
avatar: (typeof opinion.actor.user.avatar === "undefined" ? "http://fr.web.img6.acsta.net/r_50_x/commons/emptymedia/AvatarAllocineMr.gif" : resizeAcPicture( opinion.actor.user.avatar, 50, "x" )),
username: opinion.actor.user.userName,
actiontype: opinion.action.type,
actionvalue: opinion.action.value,
content: ( opinion.action.type === "userreview" ? opinion.content.$ : (opinion.action.type === "wanttosee" ? "veut voir ce film." : "" ) ).replace(/[\n\r\s]+/mg,' ').replace(/(^.{320,340})[\s].+$/,'$1…'),
size: 30,
texture: {
src: "iri/img/logo-iri.png"
},
strokeStyle: config.borderColors[opinion._colorId],
shape:{
type:'box',
width: config.columnWidth / 2 - 1,
height: config.tokenHeight / 2
},
callback: {
mouseover: onTokenMouseover,
mouseout: onTokenMouseout
}
})
});
var deleteT = vizT - config.disappearAfter;
barChart.selectAll("state",1)
.filter(function(t) {
return t.attr("viztime") <= deleteT;
}).forEach(function(t) {
t.flocculate();
});
if (deleteT > config.vizDuration) {
pause();
}
barChart.strata.update(barChart);
var w = $('.timer-bar').width() * Math.min(1, vizT / config.vizDuration);
$(".timer-bar-value").width(w);
}, config.refreshRate);
}
function loadMovie(movie) {
$.getJSON(
"http://api.allocine.fr/rest/v3/opinionlist",
{
partner: config.partnerCode,
format: "json",
subject: "movie:" + movie.id,
count: config.opinionsPerPage,
refresh: refreshtoken(),
page: movie.opinionPage
},
function(d) {
if (d.feed && d.feed.activity) {
var tokens = d.feed.activity.filter(function(a) {
return a.action.type !== "notinterested" && a.action.value !== "-3.0";
});
} else {
var tokens = [];
}
tokens.forEach(function(a) {
a._colorId = config.colorIds[a.action.value];
a._timestamp = Date.parse(a.datetime + tzSuffix) - tzDelta;
a._movie = movie.id;
a._column = movie.column;
});
var tokcount = tokens.length;
tokens = tokens.filter(function(a) {
return a._timestamp >= originalStart;
});
movie.opinions = movie.opinions.concat(tokens);
opinions = opinions.concat(tokens);
$("#data-viz").append(".");
if (tokens.length === tokcount) {
console.log("Page " + movie.opinionPage + " of '" + movie.title + "' loaded");
movie.opinionPage ++;
loadMovie(movie);
} else {
console.log("Page " + movie.opinionPage + " of '" + movie.title + "' -- total : " + movie.opinions.length + " opinions loaded");
moviesToLoad--;
if (!moviesToLoad) {
console.log("**** Everything is loaded, in " + (Date.now() - loadTime) / 1000 + " seconds");
startViz();
}
}
}
);
}
var acimgserv = 0, acimgservcount = 6;
function resizeAcPicture(pic, w, h) {
var path = pic.path || pic.href || pic || "";
if (/^https?:\/\/[\w\d\.]+\.acsta\.net\//.test(path)) {
path = path.replace(/^https?:\/\/[^\/]+/,'');
}
if (path[0] === "/") {
return "http://fr.web.img"
+ ( 1 + (acimgserv++ % acimgservcount))
+ ".acsta.net/r_"
+ w
+ "_"
+ h
+ path;
}
return path;
}
$.getJSON(
"http://api.allocine.fr/rest/v3/movielist",
{
partner: config.partnerCode,
format: "json",
filter: "top:week",
count: config.movieCount,
refresh: refreshtoken()
},
function(d) {
$("#data-viz").html("Chargement des flux d'opinions<br />");
console.log("Movie List Loaded");
tzDelta = .5 * HOURS * Math.round((Date.parse(d.feed.updated) - loadTime) / (.5 * HOURS));
tzSuffix = d.feed.updated.replace(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/,'');
movies = [];
for (var i = 0; i < d.feed.movie.length; i++) {
var movie = d.feed.movie[i];
if (i % 2) {
movies.push(movie);
} else {
movies.splice(0,0,movie);
}
}
movies = movies.map(function(movie, i) {
return {
id: movie.code,
column: i,
title: movie.title,
poster: movie.poster.href,
opinions: [],
opinionPage: 1
};
});
moviesToLoad = movies.length;
movies.forEach(loadMovie);
$('.posters').html(movies.map(function(movie) {
return '<li class="poster" style=" margin: 0 '
+ Math.floor(config.columnSpacing / 2)
+ 'px"><img width="150" height="200" src="'
+ resizeAcPicture(movie.poster,150,200)
+ '" alt="'
+ movie.title
+ '" /><div class="poster-overlay"><div class="poster-datainfo">'
+ '<ul class="poster-distribution"><li class="poster-2">0</li><li class="poster-1">0</li><li class="poster-0">0</li>'
+ '</ul></div></div></li>';
}).join(""));
$(".poster")
.each(function(i) {
movies[i].poster$ = $(this);
});
}
);
function refreshtoken() {
return Math.floor(0x100000000 * Math.random()).toString(16) + "-" + (Date.now() % 0x100000000).toString(16);
}
$(".restart").click(function() {
startViz();
return false;
});
var commentDiv = $("#comment");
$("#data-viz").mousemove(_.throttle(function(e) {
commentDiv
.css({
left: e.pageX,
top: e.pageY + 2
})
.attr("className", (e.pageX < $(window).width() / 2 ? "left" : "right"))
}, 50));
});