toolkit/javascript/nco/treemap.js
changeset 47 c0b4a8b5a012
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolkit/javascript/nco/treemap.js	Thu Apr 10 14:20:23 2014 +0200
@@ -0,0 +1,285 @@
+/**
+ * Creation of a treemap
+ */
+
+/**
+ * Parameters is the object containing basics initialization parameters
+ */
+function Treemap(json, parametres) {
+	this.parametres = {
+		name: null,
+		hauteur: 300,
+		largeur: 500,
+		selector: "#treemap",
+		
+		couleurs: null,
+		relations: null,
+		
+		contenu: null,
+		template: null,
+	
+		transitionDuration: 1000
+	};
+	
+	this.etendre(parametres);
+	
+	/* -- Gestion des valeurs par défaut -- */
+	if (null==this.parametres.name) {
+		this.parametres.name = this.parametres.selector;
+	}
+	if (null==this.parametres.contenu) {
+		if (null!=this.parametres.template) {
+			this.template = jQuery(this.parametres.template.selector);
+			this.template.remove();
+			this.parametres.contenu = this.renderContenu;
+			
+			// Check for forgotten values
+			this.setDefaultValue(this.parametres.template.maxTextSpace, 50);
+			this.setDefaultValue(this.parametres.template.minFontSize, 1);
+		}
+		else {
+			this.parametres.contenu = this.defaultContent;
+		}
+	}
+	
+	/* -- Initialisation d'attributs -- */
+	var object = this;
+	this.treemap = null;
+	this.dataTreemap = null;
+	this.articles = null;
+	this.treemapHtml = null;
+
+	this.dataJSON = json;	
+	this.dataTreemap = this.getDataTreemap(json);
+	this.articles = this.dataJSON.articles[0];
+	
+	this.duree = this.dataJSON.clusters[0].timeline.length;
+	this.nbClusters = this.dataJSON.clusters.length;
+	this.nbArticles = this.articles.length;
+	
+	/* -- Create the treemap -- */
+	
+	if (null==this.parametres.couleurs) {
+		this.parametres.couleurs = new Couleurs(this.nbClusters);
+	}
+	
+	this.treemap = d3.layout.treemap()
+		.size([this.parametres.largeur, this.parametres.hauteur])
+		.sticky(true)
+		.value(function(d) { 
+			return object.comptage(d); 
+		});
+		
+	this.treemapHtml = d3.select("#treemap").append('div')
+		.style('position', 'relative')
+		.style('width', this.parametres.largeur + 'px')
+		.style('height', this.parametres.hauteur + 'px')
+		.style('background-color', '#32375F');
+		
+	this.treemapHtml.data(this.dataTreemap).selectAll("div")
+		.data(this.treemap.nodes).enter().append("div")
+			.attr('class', 'cell')
+			.style('background-color', function(datum) {
+				return d.children? null: object.parametres.couleurs.get(datum.id);
+			})
+			.call(this.sizeCell)
+			.html(function(d) { 
+				return d.children? null: object.getContent(d); 
+			})
+			.attr("datetime", function(d) {
+				return d.children? 0: object.articles[d.idMax].date;
+			});
+	
+	/* -- Register items in relations -- */
+	
+	if (null!=this.parametres.relations) {
+		this.parametres.relations.add(this.parametres.name, this);
+		
+		this.treemapHtml.selectAll("div")
+			.each(function(datum) {
+				var item = d3.select(this);
+				datum["relationName"] = object.parametres.name +'.'+ datum.name;
+				object.parametres.relations.add(datum.relationName, item);
+			});
+	}
+};
+
+Treemap.prototype = {
+	constructor: Treemap,
+	
+	etendre: function(parametres) {
+		if (null==parametres || "object"!=typeof(parametres)) {
+			return;
+		}
+		
+		for (var cle in parametres) {
+			this.parametres[cle] = parametres[cle];
+		}
+	},
+	
+	setDefaultValue: function(attribute, value) {
+		if (undefined==attribute || null==attribute) {
+			attribute = value;
+		}
+	},
+	
+	// calcule la taille d'un cluster (somme de tout les articles)
+	// et l'élément de poids maximal
+	comptage : function(datumCell, start, end) {
+		var compteursize = 0, idMax = 0, poidsMax = 0;
+		var timeline = datumCell.timeline;
+		
+		var tStart = (start==null || start<0)? 0: start;
+		var tEnd = (end==null || end>timeline.length)? 
+				timeline.length: end;
+		var index, endIndex, articleId;
+		for (var t=tStart; t<tEnd; t++) { //boucle sur les t de la timeline
+			compteursize+= timeline[t].length;
+			for (index = 0, endIndex = timeline[t].length; 
+				index < endIndex; ++index) {
+				articleId = timeline[t][index];
+				if (this.articles[articleId].poids > poidsMax) {
+					poidsMax = this.articles[articleId].poids;
+					idMax = articleId;
+				}
+			}
+		}
+		
+		datumCell.poidsMax = poidsMax;
+		datumCell.idMax = idMax;
+		return compteursize;
+	},
+	
+	getContent: function(datumCell, start, end) {
+		if (null!=this.parametres.contenu) {
+			return this.parametres.contenu.call(
+				this, datumCell, this.articles[datumCell.idMax]);
+		}
+		else {
+			return this.articles[datumCell.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 cluster = { id: clusters[i].numero, name : "cluster." + clusters[i].numero, timeline : clusters[i].timeline };
+			data.children.push(cluster);
+		}
+		
+		return [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"; });
+	},
+	
+	focus: function(map) {
+		map.classed("selected", true)
+			.transition()
+			.duration(150)
+			.delay(50)
+			.style("opacity", 0.5);
+	},
+	
+	blur: function(map) {
+		map.classed("selected", false)
+			.transition()
+			.duration(100)
+			.delay(50)
+			.style("opacity", 1);
+	},
+	
+	transition: function(start, end) {
+		var object = this;
+		// To avoid conflicts while elements are hovered, lock the actions
+		
+		this.treemapHtml.selectAll("div")
+			.each(function(datum) {
+				object.parametres.relations.get(datum.relationName)
+						.lock();
+			})
+			.data(this.treemap.value(function(d) {
+				return object.comptage(d, start, end); 
+			}))
+			.attr("datetime", function(d) {
+				return d.children? 0: object.articles[d.idMax].date;
+			})
+			.html(function(d) { return d.children? null: object.getContent(d, start, end); })
+			.transition()
+				.duration(this.parametres.transitionDuration)
+				.call(this.sizeCell)
+				// Free the lock
+				.each("end", function(datum) {
+					object.parametres.relations.get(datum.relationName)
+						.unlock();
+				});
+	},
+	
+	defaultContent: 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;
+	},
+	
+	renderContenu: function(cellule, article) {
+		var image = article.image;
+		
+		var wi = image.largeur,
+			hi = image.hauteur,
+			wc = cellule.dx - 1,
+			hc = cellule.dy - 1,
+			r = Math.min(hi/hc, wi/wc),
+			w = wi/r,
+			h = hi/r,
+			posx = -(w-wc)/2,
+			posy = -(h-hc)/2;
+		
+		var data = {
+			'urlImage': image.url,
+			'widhtImage': w +"px",
+			'heightImage': h +"px",
+			'cssOffsetImage': "margin-top: "+ posy +"px; margin-left: "+posx +"px;",
+			'titreArticle': article.titre,
+			'dateArticle': article.date,
+			'widthArticle': wc +"px",
+			'heightArticle': hc +"px",
+			'cssWidthArticle': "width:" +wc +"px;",
+			'cssHeightArticle': "height:" +hc +"px;",
+			'cssDimensionsArticle': "width:" +wc +"px;height:" +hc +"px;",
+			'dateArticle': article.date
+		};
+		
+		return this.template
+			.render(data, this.parametres.template.directives).html();
+	},
+	
+	on: function(event, action) {
+		switch(event) {
+		default:
+			this.treemapHtml.on(event, action);
+		}
+	}
+};