/**
* Creation of an object for streamgraph and treemap connected
*/
/**
* Parameters is the object containing basics initialization parameters
*/
function StreamMap(json, parametres) {
this.parametres = {
hauteurStream: 100,
largeurStream: 900,
selectorStream: "#streamgraph",
hauteurTree: 900,
largeurTree: 900,
selectorTree: "#treemap",
duree: 50,
nbClusters: 10,
nbArticles: 1000,
couleurs: null,
mx: 0,
my: 0,
onFocus: null,
onBlur: null,
contenu: function(cellule, article) {
var html;
var img = article.image;
var wi, hi, wc, hc, w, h, r;
wi = img.largeur;
hi = img.hauteur;
wc = cellule.dx - 1;
hc = cellule.dy - 1;
r = Math.min(hi/hc, wi/wc);
w = wi/r;
h = hi/r;
var posx, posy;
posx = -(w-wc)/2;
posy = -(h-hc)/2;
html = "<img class = 'img' src = \"" + img.url + "\" style=\" width:"+ w + "px; height:" +h + "px; margin-top:" + posy + "px; margin-left:" + posx + "px;\" />";
html += "<p class = 'title' style = \" width:" + wc + "px;\" >" + article.titre + "</p>";
return html;
}
};
this.etendre(parametres);
var object = this;
this.treemap = null;
this.streamgraph = null;
this.dataTreemap = null;
this.articles = null;
this.dataStreamgraph = null;
this.data = null;
this.relations = [];
this.treemapHtml = null;
this.streamgraphHtml = null;
this.area = d3.svg.area()
.x(function(d) {
return d.x * object.parametres.largeurStream / object.parametres.mx;
})
.y0(function(d) {
return object.parametres.hauteurStream
- d.y0 * object.parametres.hauteurStream / object.parametres.my;
})
.y1(function(d) {
return object.parametres.hauteurStream
- (d.y + d.y0) * object.parametres.hauteurStream
/ object.parametres.my;
});
// Consctructeur des deux graphes
object.dataJSON = json;
/* -- Generate arrays for each graph -- */
object.dataTreemap = object.getDataTreemap(json);
object.dataStreamgraph = object.getDataStreamgraph(json);
object.articles = object.dataJSON.articles[0];
// console.log(object.dataTreemap);
// console.log(object.dataStreamgraph);
/* -- Create the streamgraph -- */
if (null==object.parametres.couleurs) {
object.parametres.couleurs = d3.scale.category20c();
}
object.parametres.mx = object.dataStreamgraph[0].length - 1;
object.parametres.my = d3.max(object.dataStreamgraph, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
});
object.streamgraphHtml = d3.select(object.parametres.selectorStream)
.style("width", object.parametres.largeurStream + "px")
.style("height", object.parametres.hauteurStream + "px")
.append("svg:svg");
object.streamgraphHtml.selectAll("path")
.data(object.dataStreamgraph).enter().append("svg:path")
.style("fill", function() {
return object.parametres.couleurs(Math.random());
})
.attr("d", object.area)
.each(function(datum, index) {
var item = d3.select(this).attr("id", index);
object.relations.push({
name: "cluster_" + index,
stream: item,
map: null,
color: item.style("fill")
});
});
/* -- Create the treemap -- */
object.treemap = d3.layout.treemap()
.size([object.parametres.largeurTree, object.parametres.hauteurTree])
.sticky(true)
.value(function(d) {
return object.comptage(d.timeline);
});
object.treemapHtml = d3.select("#treemap").append('div')
.style('position', 'relative')
.style('width', object.parametres.largeurTree + 'px')
.style('height', object.parametres.hauteurTree + 'px')
.style('background-color', '#32375F');
object.treemapHtml.data(object.dataTreemap).selectAll("div")
.data(object.treemap.nodes).enter().append("div")
.attr('class', 'cell')
.call(object.sizeCell)
.html(function(d) {
return d.children? null: object.getContent(d);
})
/* .style('background', function(d) {
return !d.children? null: object.parametres.couleurs(d.name);
})*/
.each(function(datum) {
var item = d3.select(this);
for (var i=0; i<object.parametres.nbClusters; i++) {
if (datum.name==object.relations[i].name) {
object.relations[i].map = item;
// item.style("background", object.relations[i].color);
}
}
});
/* -- Bind the maps with streams -- */
for (var i=0, _end=object.parametres.nbClusters; i<_end; i++) {
object.bindMapToStream(
object.relations[i].stream, object.relations[i].map);
object.bindStreamToMap(
object.relations[i].map, object.relations[i].stream);
}
};
StreamMap.prototype = {
etendre: function(parametres) {
if (null==parametres || "object"!=typeof(parametres)) {
return;
}
for (var cle in parametres) {
this.parametres[cle] = parametres[cle];
}
},
// calcule la taille d'un cluster (somme de tout les articles)
comptage : function(timeline, start, end) {
var compteursize = 0, idMax = 0, poidsMax = 0;
var tStart = (start==null || start<0)? 0: start;
var tEnd = (end==null || end>timeline.length)?
timeline.length: end;
var index, endIndex, articleId;
for (t=tStart; t<tEnd; t++) { //boucle sur les t de la timeline
compteursize+= timeline[t].length;
}
return compteursize;
},
getContent: function(datumCell, start, end) {
var compteursize = 0, idMax = 0, poidsMax = 0;
var articles = this.articles;
var tStart = (start==null || start<0)? 0: start;
var tEnd = (end==null || end>datumCell.timeline.length)?
datumCell.timeline.length: end;
var index, endIndex, articleId;
for (t=tStart; t<tEnd; t++) { //boucle sur les t de la timeline
for (index = 0, endIndex = datumCell.timeline[t].length; index<endIndex; ++index) {
articleId = datumCell.timeline[t][index];
if (articles[articleId].poids > poidsMax) {
poidsMax = articles[articleId].poids;
idMax = articleId;
}
}
}
if (null!=this.parametres.contenu) {
return this.parametres.contenu(datumCell, articles[idMax]);
}
else {
return articles[idMax].titre;
}
},
getDataTreemap : function(json, start, end) {
var data = { name : "data", children : []};
var clusters = json.clusters;
for (var i=0, _end=clusters.length; i<_end; i++) { // parcours les differents clusters
//var size = comptage(clusters[i], start, end);
var cluster = { name : "cluster_" + clusters[i].numero,
timeline : clusters[i].timeline };
data.children.push(cluster);
}
return [data];
},
getDataStreamgraph : function(json) {
var data = new Array(),
clusters = json.clusters,
duree = clusters[0].timeline.length;
var dataCluster, timeline;
for (var i=0, end=clusters.length; i<end; i++) {
timeline = clusters[i].timeline;
dataCluster = [];
for (var t=0; t<duree; t++) {
dataCluster.push({ x: t, y: timeline[t].length });
}
data.push(dataCluster);
}
// Smooth the data
return d3.layout.stack().offset("wiggle")(data);
},
sizeCell: function() {
this.style("left", function(d) { return d.x + "px"; })
.style("top", function(d) { return d.y + "px"; })
.style("width", function(d) { return d.dx - 1 + "px"; })
.style("height", function(d) { return d.dy - 1 + "px"; });
},
bindMapToStream: function(stream, map) {
var object = this;
if (null!=this.parametres.onFocus && null!=this.parametres.onBlur) {
stream.on("mouseover", function() {
object.parametres.onFocus(stream, map);
});
stream.on("mouseout", function() {
object.parametres.onBlur(stream, map);
});
}
},
bindStreamToMap: function(map, stream) {
var object = this;
if (null!=this.parametres.onFocus && null!=this.parametres.onBlur) {
map.on("mouseover", function() {
object.parametres.onFocus(stream, map);
});
map.on("mouseout", function() {
object.parametres.onBlur(stream, map);
});
}
}
};
/* -- Draggable window -- */
var selectBars = {
focus: function(stream, map) {
stream.style("stroke", "black")
.transition()
.duration(300)
.delay(50)
.style("opacity", 0.5);
map.classed("selected", true)
.transition()
.duration(150)
.delay(50)
.style("opacity", 0.5);
},
unfocus: function(stream, map) {
stream.style("stroke", "none")
.transition()
.duration(300)
.delay(75)
.style("opacity", 1);
map.classed("selected", false)
.transition()
.duration(100)
.delay(50)
.style("opacity", 1);
},
calculerIndx: function() { //recalcul des valeurs
indx[0] = Math.round(parseInt($('#gauche').css("left"))
/(streamMap.parametres.largeurStream/streamMap.parametres.duree));
indx[1] = Math.round((parseInt($('#droite').css('left'))
+ parseInt($('#droite').css('width')))
/(streamMap.parametres.largeurStream/streamMap.parametres.duree));
},
followcache: function() { //déplacement des caches avec le drag'n'drop
d3.select('.cache.gauche').style("width", function() {
return $('#gauche').css("left")
});
d3.select('.cache.droite').style("left", function() {
var a;
a = parseInt($('#droite').css("left")) + 10;
a = a + "px";
return a;
});
d3.select('.cache.droite').style("width", function() {
var a;
a = 900 - parseInt($('.cache.droite').css('left'));
a = a + "px";
return a;
});
},
contain: function(a) {
var ar = [];
var lim;
//limite pour gauche
var lim = parseInt($('.selecteur.droite').css("left"));
ar = [0, 0, lim, 300];
$('.selecteur.gauche').draggable("option", "containment", ar);
//limite pour droite
lim = parseInt($('.selecteur.gauche').css("left"))+20;
ar = [lim, 0, 900, 300];
$('.selecteur.droite').draggable("option", "containment", ar);
},
transitionTree: function(start, end) {
streamMap.treemapHtml.selectAll("div")
.data(streamMap.treemap.value(function(d) {
return streamMap.comptage(d.timeline, start, end);
}))
.html(function(d) { return d.children? null: streamMap.getContent(d, start, end); })
.transition()
.duration(1500)
.call(streamMap.sizeCell);
}
};
var parametres = {
hauteurStream: 300,
hauteurTree: 300,
onFocus: selectBars.focus,
onBlur: selectBars.unfocus
};
var streamMap = new StreamMap(json, parametres);
var indx = [0, streamMap.parametres.duree];
/*l'objet indx contient les valeurs d'indexs de la zone correspondant au tree map.
Il se réactulise lorsqu'on lâche les barres.
*/
//méthode draggable de jquery pour drag'n'drop
$('.selecteur').draggable({
axis : 'x',
grid: [streamMap.parametres.largeurStream/streamMap.parametres.duree, 1],
containment: [60,0,
streamMap.parametres.largeurStream,streamMap.parametres.hauteurStream],
drag: selectBars.followcache,
stop: function(e, ui){
selectBars.calculerIndx();
selectBars.followcache();
selectBars.contain(ui);
selectBars.transitionTree(indx[0], indx[1]);
}
});