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" ],
imageUrlBase: 'iri/img/',
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>'
+ _.range(3).map(function(i) {
return '<pattern id="pattern_'
+ i
+ '" x="0" y="0" width="3" height="3" patternUnits="userSpaceOnUse"><image x="0" y="0" width="3" height="3" xlink:href="'
+ config.imageUrlBase
+ 'pattern-'
+ i
+ '.png" /></pattern>';
}).join("")
+ '</defs></svg>';
$("body").append(xml);
function resizeTimer() {
var w = Math.max(80, $(window).width() - 158),
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,
width: vsWidth
});
$('.posters').css('width', vsWidth);
var timeAtPause = 0, paused = true, timeStart;
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 = Date.now() - timeAtPause;
paused = false;
$(".play-pause").removeClass("play").addClass("pause").attr("title", "Pause");
}
function pause() {
if (paused) {
return;
}
timeAtPause = currentVizTime();
paused = true;
$(".play-pause").removeClass("pause").addClass("play").attr("title", "Lecture");
}
$(".play-pause").click(function() {
if (paused) {
play();
} else {
pause();
}
});
function agoify(timestamp) {
var h = "Il y a ",
deltaT = Date.now() - 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";
}
}
return h;
}
var opinionTemplate = _.template(
'<div class="comment-content clearfix action-<%- actiontype %>">'
+ '<div class="avatar"><img src="<%- avatar %>" alt="Avatar" /></div>'
+ '<div class="comment-right">'
+ '<div class="comment-metadata">'
+ '<span class="username"><%- username %> </span>'
+ ' <span class="date"> <%- ago %></span>'
+ '<ul class="stars-rating rate-<%- ratevalue %>">'
+ '<li></li><li></li><li></li><li></li><li></li>'
+ '</ul>'
+ '</div>'
+ '<p class="review-content"><%- content %></p>'
+ '</div>'
+ '</div>'
+ '<div class="comment-arrow"></div>'
);
var barChart = undefined;
function startViz() {
$("#comment").hide();
console.log("Start Viz");
if (typeof barChart !== "undefined") {
console.log("Bar Chart exists");
barChart.selectAll("state",1).forEach(function(t) {
t.flocculate();
});
}
paused = true;
timeAtPause = 0;
opinions.sort(function(a,b) {
return a._timestamp > b._timestamp;
});
$("#data-viz").html("");
var lastShownToken = null,
maxData = Math.max.apply(Math, movies.map(function(m) { return m.opinions.length; })),
aggrHeight = vsHeight - 80,
aggrScale = aggrHeight / maxData,
firstStrataCall = true;
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() {
var flocculated = (typeof barChart === "undefined" ? [] : barChart.selectAll("state",2) );
if (firstStrataCall) {
_.defer(function() {
_.range(3).map(function(colorId) {
$(".layer_"+colorId).css({
fill: "url(#pattern_" +colorId + ")",
stroke: config.borderColors[colorId]
});
});
});
firstStrataCall = false;
}
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");
commentDiv.html(opinionTemplate({
actiontype: token.attr("actiontype"),
avatar: token.attr("avatar"),
username: token.attr("username"),
ago: agoify(token.attr("timestamp")),
ratevalue: token.attr("actionvalue"),
content: token.attr("content")
})).show();
lastShownToken = token;
}
}
function onTokenMouseout(token) {
lastShownToken = null;
setTimeout(function() {
if (!lastShownToken) {
$("body").css("cursor", "");
commentDiv.hide();
}
}, 200);
}
//console.log(barChartSettings);
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 tokensToAdd = opinions.filter(function(o) {
return o._viztime > lastTimestamp && o._viztime <= vizT
});
lastTimestamp = vizT;
tokensToAdd.forEach(function(opinion) {
barChart.addToken({
category: opinion._column,
datetime: opinion.datetime,
timestamp: opinion._timestamp,
viztime: opinion._viztime,
movie: opinion._movie,
colorId: opinion._colorId,
//fillStyle: config.colors[opinion._colorId],
avatar: opinion._avatar,
username: opinion.actor.user.userName,
actiontype: opinion.action.type,
actionvalue: opinion.action.value,
content: opinion._content.replace(/(^.{320,340})[\s].+$/,'$1…'),
//size: 30,
strokeStyle: config.borderColors[opinion._colorId],
texture: {
src: config.imageUrlBase + 'pattern-' + opinion._colorId + '.png'
},
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._viztime = originalTimeToVizTime(a._timestamp);
a._movie = movie.id;
a._column = movie.column;
a._avatar = ((typeof a.actor.user.avatar === "undefined") ? "http://fr.web.img6.acsta.net/r_50_x/commons/emptymedia/AvatarAllocineMr.gif" : resizeAcPicture( a.actor.user.avatar, 50, "x" ));
a._content = ( a.action.type === "userreview" ? a.content.$ : (a.action.type === "wanttosee" ? "veut voir ce film." : "" ) ).replace(/[\n\r\s]+/mg,' ');
});
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 h3").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 h3").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, i) {
return '<li class="poster" style=" margin: 0 '
+ Math.floor(config.columnSpacing / 2)
+ 'px" data-movie-index="'
+ i
+ '"><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")
.click(function() {
$("#comment-modal").modal({
containerCss: {
height: $(window).height()-200,
width: Math.min($(window).width()-200, 640)
},
overlayClose: true
});
var vizT = currentVizTime(),
movie = movies[$(this).attr("data-movie-index")],
opsToShow = movie.opinions.filter(function(o) {
return o._viztime <= vizT;
});
$(".comment-count").text(opsToShow.length);
$(".comment-subject").text(movie.title);
$(".comment-start").text(new Date(originalStart).toTimeString().substr(0,5));
$(".comment-end").text(new Date(vizTimeToOriginalTime(vizT)).toTimeString().substr(0,5));
$(".comment-list").html(opsToShow.map(function(o) {
return opinionTemplate({
actiontype: o.action.type,
avatar: o._avatar,
username: o.actor.user.userName,
ago: agoify(o._timestamp),
ratevalue: o.action.value,
content: o._content
});
}).join(""))
.mCustomScrollbar({
autoDraggerLength:false,
scrollButtons:{
enable:false
}
});
return false;
})
.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
})
.removeClass("left right").addClass(e.pageX < $(window).width() / 2 ? "left" : "right");
}, 50));
});