alcatel/static/libraries/nco/cartographie.js
changeset 27 8ca7f2cea729
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/alcatel/static/libraries/nco/cartographie.js	Thu Jan 24 16:58:55 2013 +0100
@@ -0,0 +1,801 @@
+function Carte(parametres, articles, links) {
+
+	var object = this;
+	
+	this.parametres = {
+		// Module dimensions and caracteristics
+		width: 1440,
+		height: 800,
+		top: 50,
+		left: -240,
+		name: null,
+		boxID: "box",
+		selectorID: "carto",
+		canvasID: "canvas",
+		info: "infos",
+		
+		couleurs: null,
+		relations: null,
+		
+		// How to deal with the data
+		isPositions: false,
+		
+		charge: -30,
+		gravity: .13,
+		theta: 3,
+		
+		// Node rendering
+		nodeSizeInterval: [3, 15],
+		log: false,
+		exp: false,
+		layerStyle: {
+			opacity: 0.6
+		},
+		handle_len_rate: 2.2,
+		maxDistance: 35,
+		
+		scaleInterval: [0.5, 10],
+		
+		infobulleHtml: function(node) {
+			return node.titre + "<p>cluster: " + node.group +"</p>";
+		},
+		
+		activerZoom: true,
+		activerNodeZoom: true,
+		activerInfobulle: true,
+		zoomNode: false,
+		logZoomNode: false,
+		logZoomNodeParameter: 2
+	};
+	
+	this.beginDate = new Date(); 
+	this.etendre(parametres);
+	
+	if (null==this.parametres.name) {
+		this.parametres.name = this.parametres.selectorID;
+	}
+	
+	//création du contenant sur la page, si inexistant
+	if (null == (document.getElementById(this.parametres.boxID))) {
+		d3.select('.content').append('div')
+			.attr("id", this.parametres.boxID)
+			.classed("gallery", true)
+			.style("position", "absolute")
+			.style("border", "1 px dashed");
+	}
+	
+	d3.select('#' + this.parametres.boxID)
+		.style("width", this.parametres.width)
+		.style("height", this.parametres.height)
+		.style("position", "absolute")
+		.style("top", this.parametres.top)
+		.style("left", this.parametres.left)
+		.style("overflow", "hidden")
+		.style('z-index', 0);
+	
+	
+	if (null == (document.getElementById(this.parametres.selectorID))) {
+		d3.select('.gallery').append('div')
+			.attr("id", this.parametres.selectorID);
+	}
+	
+	d3.select('#' + this.parametres.selectorID)
+		.attr("width", this.parametres.width)
+		.attr("height", this.parametres.height)
+		.style("position", "absolute")
+		.style('z-index', 2);
+	
+	// Create drag interaction with the module
+	this.drag = d3.behavior.drag()
+	    .on("dragstart", function(d, i) {object.dragstart(d, i, object);})
+	    .on("drag", object.dragmove)
+	    .on("dragend", function(d, i) {object.dragend(d, i, object);});
+		   
+	this.static = false;
+    
+    if (this.parametres.activerNodeZoom) {
+    		this.nodeMouseover = function(node) { 
+    			var scale = object.getScale(object.rect);
+				object.grow(node, scale); 
+			};
+			
+			this.nodeMouseout = function(node) {
+				var radius = object.radius(node.datum("scaledPoids"));
+				object.shrink(radius, node); 
+			};
+    }
+    
+    if (this.parametres.activerInfobulle) {
+    	
+		this.nodeClick = function(d) { 
+			object.highlight(d); 
+		};
+		
+		this.infoClick = function(d) { 
+			object.blur(); 
+		};
+    }	
+	
+	//création de l'infobulle, si inexistante
+	if (null == (this.infoBulle = d3.select('#' + this.parametres.info)[0][0])) {
+		this.infoBulle = d3.select('#' + this.parametres.selectorID).append('div')
+			.attr("id", this.parametres.info)
+			.style("top", this.parametres.height)
+			.style("opacity", 0)
+			.style("z-index", 1);
+	}
+	
+	if (null!=this.parametres.relations) {
+		this.parametres.relations.add(
+				this.parametres.name + ".infobulle", this.infoBulle)
+			.click(this.infoClick);
+	}
+	else {
+		this.infoBulle.on("click", this.infoClick);
+	}
+	
+	
+	//création du canvas sur la page, si inexistant
+	if (null == (this.canvas = document.getElementById(this.parametres.canvasID))) {
+		d3.select('.gallery').insert('canvas', '#' + this.parametres.selectorID)
+			.attr("id", this.parametres.canvasID)
+			.attr("resize", false);
+		this.canvas = document.getElementById(this.parametres.canvasID);
+	}
+	d3.select('#' + this.parametres.canvasID)
+		.attr("width", d3.select('#' + this.parametres.selectorID).attr("width"))
+		.attr("height", d3.select('#' + this.parametres.selectorID).attr("height"))
+		.style("position", "absolute")
+		.style("z-index", 1);
+	
+	this.mouse = {};
+	this.isDown = false;
+	this.drag2 = false;
+	
+	this.svg = d3.select('#' + this.parametres.selectorID).append('svg')
+		.attr("width", this.parametres.width)
+		.attr("height", this.parametres.height)
+		.on("mousedown", function() {object.mouseDown(event);})
+		.on("mousemove", function() {object.mouseMove(event);})
+		.on("mouseup", function() {object.mouseUp(event);});
+		
+	paper.setup(this.canvas);
+	paper.view.viewSize = new paper.Size(object.parametres.width, object.parametres.height);
+	
+	this.clusters = articles.clusters;
+		
+	//génération d'une palette de couleurs
+	if (null == this.parametres.couleurs) {
+		//this.parametres.couleurs = this.selectColors(this.clusters.length);
+		this.parametres.couleurs = new Couleurs(this.clusters.length);
+	}
+	
+	console.log("Environnement OK.");
+		
+	this.data = {};
+	this.zones = [];	
+	
+	if (this.parametres.isPositions) {
+		// Use directly data
+		this.data.nodes = this.generateNodes(articles, this.parametres.isPositions);
+		this.length = articles.nbItems;
+		this.display();
+		
+	}
+	else {
+		//initialisation du Force Directed Graph
+		this.data.nodes = this.generateNodes(articles, this.parametres.isPositions);
+		this.data.links = this.generateLinks(links, this.data.nodes);
+	
+		this.force = d3.layout.force()
+			.charge(this.parametres.charge)    //-20  
+			.nodes(this.data.nodes)
+			.links(this.data.links)
+			.linkDistance(function(link) {return link.linkDistance;}) //20
+			.linkStrength(function(link) {return link.linkStrength;})
+			.size([this.parametres.width, this.parametres.height])
+			.gravity(this.parametres.gravity) //.1
+			.theta(this.parametres.theta)
+			.start();
+			
+		console.log("Graphe en cours de stabilisation . . .");
+		
+		this.force.on("tick", function() {
+			console.log("Stabilisation OK.");
+			object.display();
+		});
+	
+	}	
+};	
+
+
+		
+Carte.prototype = {
+	constructor: Carte,
+		
+	//gestion des paramètres
+	etendre: function(parametres) {
+		if (null==parametres || "object"!=typeof(parametres)) {
+			return;
+		}
+		
+		for (var cle in parametres) {
+			this.parametres[cle] = parametres[cle];
+		}
+	},
+	
+	display: function () {
+		
+		this.showNodes();
+		if (this.parametres.activerZoom) {
+			this.activateZoom();
+		}
+		
+		paper.setup(this.canvas);
+		paper.view.viewSize = new paper.Size(
+				this.parametres.width, this.parametres.height);
+		this.createBackground();
+	},
+	
+	activateZoom: function() {
+		this.rect = this.svg.selectAll("rect")
+		    .data([{
+		    	x: this.parametres.width*(1 - this.parametres.scaleInterval[0])/(this.parametres.scaleInterval[1] - this.parametres.scaleInterval[0]),
+		    	y: 0
+		    }])
+			.enter().append("rect")
+			.attr("x", function(d) { return d.x; })
+			.attr("y", function(d) { return d.y; })
+		    .attr("width", 15)
+		    .attr("height", 15)
+		    .style("stroke", "black")
+		    .style("fill", "#00f")
+		    .style("z-index", 2)
+			.call(this.drag);
+		
+		if (null!=this.parametres.relations) {
+			this.parametres.relations.add(
+					this.parametres.name + ".zoomButton", this.rect);
+		}
+	},
+	
+	//génération d'une palette de couleurs
+	selectColors: function(nbClusters) {
+		var colors = colorbrewer;
+		var sets = ["BrBG", "PiYG", "PRGn", "PuOr", "RdBu", "RdYlBu", "RdYlGn"];
+		var selectorID, finalColors = [];
+		
+		while (finalColors.length < nbClusters) {
+			selectorID = 	Math.floor(Math.random()*7);
+			selectorID2 = Math.floor(Math.random()*11);
+			color = new paper.RgbColor(colors[sets[selectorID]][11][selectorID2]);
+			
+			var available = true;
+			for (var i = 0; i < finalColors.length; i++) {
+				if (Math.floor(finalColors[i].hue) == Math.floor(color.hue)) {
+					available = false;
+				}
+			}
+			
+			if (color.brightness < 0.9 && available) {
+				finalColors.push(color.toCssString());
+			}
+		}
+		
+		return finalColors;
+	},
+	
+	//génération des noeuds √† partir du json
+	generateNodes: function(items, isPos) {
+		
+		var nodes = [];
+		var poidsMin = 100000, poidsMax = 0;
+		
+		// on crée les noeuds
+		for (var i = 0 ; i<items.nbItems; ++i) {
+			nodes.push({index: i, titre: "Titre numero " + i, weigth: 1});
+		}
+		
+		// on donne à chaque noeud son poids et couleurs
+		// on traite identiquement core et agregated
+		// pour les clusters
+		var nbClusters = items.clusters.length;
+		for (var i =0; i< nbClusters; ++i) {
+			var cluster = items.clusters[i], 
+				clusterId = cluster.id,
+				groups = [];
+			if (cluster.items) {
+				groups.push("items");
+			}
+			else {
+				groups.push("core");
+				groups.push("agregated");
+			}
+			for (var a in groups) {
+				var group = cluster[groups[a]];
+				for (var j =0; j<group.length; ++j) {
+					var item = group[j], poids = parseInt(item.value);
+					nodes[item.key]["poids"] = poids;
+					nodes[item.key]["scaledPoids"] = poids;
+					nodes[item.key]["group"] = clusterId;
+					nodes[item.key]["key"] = item.key;
+					if (isPos) {
+						nodes[item.key]["x"] = parseFloat(item.x);
+						nodes[item.key]["y"] = parseFloat(item.y);
+					}
+					else {
+						nodes[item.key]["x"] = this.parametres.width/2*(1 + Math.cos(i*Math.Pi/nbClusters));
+						nodes[item.key]["y"] = this.parametres.height/2*(1 + Math.sin(i*Math.Pi/nbClusters));
+					}
+					if (null != this.parametres.relations) {
+						this.parametres.relations.add(this.parametres.selectorID + ".node." + item.key, nodes[item.key]);
+					}
+					if (poids>poidsMax) {
+						poidsMax = poids;
+					}
+					else if (poids<poidsMin) {
+						poidsMin = poids;
+					}
+				}
+			}
+		}
+		
+		this.poidsMin = poidsMin;
+		this.poidsMax = poidsMax;
+		
+		// pour les unclustered
+		var unclusteredId = items.clusters.length;
+		var unclusteredItems = (items.unclustered.items)? 
+				items.unclustered.items: items.unclustered;
+		for (var i =0; i< unclusteredItems.length; ++i) {
+			var item = unclusteredItems[i];
+			nodes[item.key]["poids"] = poidsMin;
+			nodes[item.key]["scaledPoids"] = poidsMin;
+			nodes[item.key]["group"] = unclusteredId;
+			nodes[item.key]["key"] = item.key;
+			if (isPos) {
+				nodes[item.key]["x"] = parseFloat(item.x);
+				nodes[item.key]["y"] = parseFloat(item.y);
+			}
+			else {
+				nodes[item.key]["x"] = this.parametres.width/2;
+				nodes[item.key]["y"] = this.parametres.height/2;
+			}
+		}
+		
+		console.log("Data articles extraites.");
+		return nodes;
+	},
+	
+	//génération des liens à partir du json
+	generateLinks: function(liens, nodes) {
+		//var linkMax = 0, linkMin = 500;
+		var links = [];
+		
+		for (var i = 0; i< liens.images.length; ++i) {
+			var item = liens.images[i];
+			for (var j = 0; j < 20 && j < item.v.length; ++j) {
+				var voisin = item.v[j];
+				
+				var linkDistance = 200/Math.max(voisin.d, 4), linkStrength;
+				
+				if (nodes[item.id]["group"] == nodes[voisin.id]["group"]) {
+					linkStrength = 1;
+					linkDistance *= 0.5;
+				}
+				else {
+					linkStrength = 0.5;
+				}
+				
+				links.push({source: item.id, target: voisin.id, value: voisin.d, linkDistance: linkDistance, linkStrength: linkStrength});
+			}
+		}
+		
+		console.log("Data liens extraites.");
+		return links;
+	},
+
+	//calcul du rayon
+	radius: function(poids) {
+		
+		var diff = this.parametres.nodeSizeInterval[1] - this.parametres.nodeSizeInterval[0];
+		scale = this.getScale(this.rect);
+		
+		if (this.parametres.log) {
+			return this.parametres.nodeSizeInterval[0] + Math.log(poids/(this.poidsMin*scale))*diff/Math.log(this.poidsMax/this.poidsMin);
+			
+		}
+		else if (this.parametres.exp) {
+			return this.parametres.nodeSizeInterval[0]*Math.exp(Math.log(this.parametres.nodeSizeInterval[1]/this.parametres.nodeSizeInterval[0])*(poids-(this.poidsMin*scale))/(scale*(this.poidsMax - this.poidsMin)));
+		}
+		else {
+			return scale*((this.parametres.nodeSizeInterval[0] + diff*(poids - scale*this.poidsMin)/(scale*(this.poidsMax - this.poidsMin))));
+		}
+	},
+	
+	//affichages des noeuds
+	showNodes: function() {
+		var object = this;
+		node = this.svg.selectAll("circle.node")
+			 .data(this.data.nodes)
+			 .enter().append("circle")
+			 .attr("class", "node")
+			 .attr("cx", function(d) { return d.x; })
+			 .attr("cy", function(d) { return d.y; })
+			 .attr("z-index", 0)
+			 .attr("r", function(d) {return object.radius(d.poids);})
+			 .style("fill", function(d) { 
+				 return object.parametres.couleurs.get(d.group);
+			 });
+			 /*.on("mouseover", this.nodeMouseover)
+			 .on("mouseout", this.nodeMouseout)*/;
+		
+		// Register all nodes
+		if (null!=object.parametres.relations) {
+			 node.each(function(datum, index) {
+				 datum["registrationName"] = object.parametres.name + ".node." + index; 
+				 object.parametres.relations.add(
+						 datum.registrationName, d3.select(this))
+					.mouseover(object.nodeMouseover)
+					.mouseout(object.nodeMouseout);
+		 	});
+		 }
+	
+		node.append("p")
+			.text(function(d) { return d.titre; });
+		node.on('click', this.nodeClick);
+		console.log("Noeuds OK.");
+	},
+	
+	//génération du fond de la carte
+	createBackground: function() {
+		var circlePaths = [];
+		scale = this.getScale(this.rect);
+		object = this;
+		
+		this.svg.selectAll('circle.node').each(function(d) {
+			var node = d3.select(this);
+			var circle = new paper.Path.Circle(
+					new paper.Point(d.x, d.y), object.radius(d.scaledPoids)*2);
+			circle.fillColor = node.style("fill");
+			circlePaths.push(circle);
+		});
+		
+		console.log("Fond des noeuds OK.");
+		this.generateConnections(circlePaths);
+		paper.view.draw();
+		
+		/*var endDate = new Date();
+		var timer = endDate.getTime() - beginDate.getTime();
+		console.log(timer);*/
+	},
+	
+	//generate background connections between nodes of a same cluster
+	generateConnections: function(paths) {
+		for (var c=0; c<this.clusters.length; ++c) {
+			var cluster = this.clusters[c];
+			
+			// définir les données suivant le type passé
+			var group = cluster.items;
+			if (undefined==group) {
+				group = cluster.core.concat(cluster.agregated);
+			}
+				
+			var layer = new paper.Layer();
+			
+			// Donner un design au fond
+			for (var attr in this.parametres.layerStyle) {
+				if (layer[attr]) {
+					layer[attr] = this.parametres.layerStyle[attr];
+				}
+				else {
+					layer.style[attr] = this.parametres.layerStyle[attr];
+				}
+			}
+			
+			var maxDistance = this.scale(this.rect[0][0].getAttribute("x"))*this.parametres.maxDistance,
+				handle = this.parametres.handle_len_rate;
+			for (var i = 1; i<group.length; ++i) {
+				var circle = paths[group[i].key];
+				if ((circle.position.x + maxDistance > 0) && (circle.position.x < this.parametres.width + maxDistance) && (circle.position.y + maxDistance > 0) && (circle.position.y < this.parametres.height + maxDistance)) {
+					layer.appendTop(circle);
+					for (var j = 0; j<i; ++j) {
+						var path = this.connect(circle, paths[group[j].key], 0.5, handle, maxDistance);
+						if (null!=path) {
+							layer.appendTop(path);
+						}
+					}
+				}
+			}
+			
+			this.zones[c] = layer;
+			if (null != this.parametres.relations) {
+				this.parametres.relations.add(this.parametres.selectorID + ".layer." + c, layer);
+			}
+			
+		}
+		paper.project.layers[0].remove();
+		console.log("Connexions OK.");
+	},
+	
+	/*
+	 * Create a connection between two balls
+	 */
+	connect: function(ball1, ball2, v, handle_len_rate, maxDistance) {
+	    var center1 = ball1.position;
+	    var center2 = ball2.position;
+	    var radius1 = ball1.bounds.width / 2;
+	    var radius2 = ball2.bounds.width / 2;
+	    var pi2 = Math.PI / 2;
+	    var d = center1.getDistance(center2);
+	    var u1, u2;
+	
+	    if (radius1 == 0 || radius2 == 0) {
+	        return null;
+		}
+	
+	    if (d > maxDistance || d <= Math.abs(radius1 - radius2)) {
+	        return;
+	    } else if (d < radius1 + radius2) { // case circles are overlapping
+	        return null;
+	    } else {
+	        u1 = 0;
+	        u2 = 0;
+	    }
+	    
+	    var center3 = center2.clone();
+	    center3.x = center2.x - center1.x;
+	    center3.y = center2.y - center1.y;
+	
+	    var angle1 = center3.getAngleInRadians();
+	    var angle2 = Math.acos((radius1 - radius2) / d);
+	    var angle1a = angle1 + u1 + (angle2 - u1) * v;
+	    var angle1b = angle1 - u1 - (angle2 - u1) * v;
+	    var angle2a = angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v;
+	    var angle2b = angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v;
+	    var p1a = center1.add(this.getVector(angle1a, radius1));
+	    var p1b = center1.add(this.getVector(angle1b, radius1));
+	    var p2a = center2.add(this.getVector(angle2a, radius2));
+	    var p2b = center2.add(this.getVector(angle2b, radius2));
+	
+	    // define handle length by the distance between
+	    // both ends of the curve to draw
+	    var totalRadius = (radius1 + radius2);
+	    var p3a = p2a.clone();
+	    p3a.x = p1a.x - p2a.x;
+	    p3a.y = p1a.y - p2a.y;
+	    var d2 = Math.min(v * handle_len_rate, p3a.length / totalRadius);
+	
+	    // case circles are overlapping:
+	    d2 *= Math.min(1, d * 2 / (radius1 + radius2));
+	
+	    radius1 *= d2;
+	    radius2 *= d2;
+	
+	    var path = new paper.Path([p1a, p2a, p2b, p1b]);
+	    path.style = ball1.style;
+	    path.closed = true;
+	    var segments = path.segments;
+	    segments[0].handleOut = this.getVector(angle1a - pi2, radius1);
+	    segments[1].handleIn = this.getVector(angle2a + pi2, radius2);
+	    segments[2].handleOut = this.getVector(angle2b - pi2, radius2);
+	    segments[3].handleIn = this.getVector(angle1b + pi2, radius1);
+	    return path;
+	},
+	
+	// ------------------------------------------------
+	getVector: function(radians, length) {
+		var angle = radians * 180 / Math.PI;
+	    return new paper.Point({"angle": angle,
+	        "length": length
+	    });
+	},
+	
+	//apparition de l'infoBulle
+	highlight: function(node) {
+		scale = this.parametres.activerZoom ? this.scale(this.rect[0][0].getAttribute("x")) : 1;
+		this.infoBulle.html(this.parametres.infobulleHtml(node));
+		if (this.infoBulle.classed("selected")) {
+			this.infoBulle.transition()
+				.duration(500)
+				.style("left", node.x + "px")
+				.style("top", node.y + "px");
+		}
+		else {
+			this.infoBulle.classed("selected", true)
+				.style("left", node.x + "px")
+				.style("top", node.y + "px")
+				.transition()
+				.duration(300)
+				.style("opacity", 0.5);
+		}
+	},
+	
+	//disparition de l'infoBulle
+	blur: function() {
+		this.infoBulle.classed("selected", false)
+			.transition()
+			.duration(400)
+			.style("opacity", 0);
+		this.infoBulle.style("top", this.parametres.height);
+	},
+	
+	//zoom sur un point
+	grow: function(node, scale) {
+		if (!this.static) {
+			node.attr("z-index", 1)
+				.transition()
+				.duration(200)
+				.attr("r", this.parametres.nodeSizeInterval[1]*scale);
+		}
+	},
+	
+	//dézoom sur un point
+	shrink: function(radius, node) {
+		if (!this.static) {
+			node.transition()
+				.duration(70)
+				.attr("z-index", 0)
+				.attr("r", radius);
+		}
+	
+	},
+	
+	//réaffichage du fond sur recadrage du navigateur
+	onResize: function(event) {
+		paper.view.draw();
+		d3.select('#' + this.parametres.canvasID)
+			.attr("width", d3.select('#' + this.parametres.selectorID).attr("width"))
+			.attr("height", d3.select('#' + this.parametres.selectorID).attr("height"));
+	},
+	
+	dragstart: function(d,i, object) {
+    	object.rect.xi = object.scale(d3.event.x);
+    	object.static = true;
+    },
+    
+	dragmove: function(d, i) {
+		d.x += d3.event.dx;
+		d3.select(this).attr("x", d.x);
+	},
+	
+	dragend: function(d, i, object) {
+		
+		var ratio = object.scale(d3.event.x)/object.rect.xi;
+		object.infoBulle.classed("selected", false)
+			.style("opacity", 0);
+			
+		object.zoom(ratio);
+		object.resetCanvas();
+		
+		object.zoomTransition(ratio);
+		
+		object.static = false;
+		var timer = new Date().getTime() - object.beginDate.getTime();
+		console.log(timer);
+	},
+	
+	resetCanvas: function() {
+		paper.view.remove();
+		this.hideView();
+		paper.setup(this.canvas);
+		paper.view.viewSize = new paper.Size(this.parametres.width, this.parametres.height);
+	},
+	
+	hideView: function() {
+		var ctx = this.canvas.getContext('2d');
+		ctx.fillStyle = 'rgb(0,0,0)';
+		ctx.fillRect(this.canvas.left, this.canvas.top, this.canvas.width, this.canvas.height);
+	},
+	
+	scale: function(x) {
+		return (this.parametres.scaleInterval[0] + (this.parametres.scaleInterval[1] - this.parametres.scaleInterval[0])*(x/this.parametres.width));
+	},
+	
+	getScale: function(rect) {
+		if (rect == undefined || !this.parametres.activerZoom) {
+			return 1;
+		}
+		else {
+			if (this.parametres.zoomNode) {
+				return this.scale(rect[0][0].getAttribute("x"));
+			}
+			else if (this.parametres.logZoomNode) {
+				return Math.log(1 + this.scale(rect[0][0].getAttribute("x"))*this.parametres.logZoomNodeParameter)/(Math.log(1 + this.parametres.logZoomNodeParameter));
+			}
+			else {
+				return 1;
+			}
+		}
+	},
+	
+	zoom: function(ratio) {
+		var object = this;
+		var scale = this.getScale(this.rect);
+		
+		this.svg.selectAll("circle.node")
+			.each(function(d) {
+				d.x = object.parametres.width/2 
+					- ratio*(object.parametres.width/2- d.x);
+				
+				d.y = object.parametres.height/2 
+					- ratio*(object.parametres.height/2 - d.y);
+				
+				d.scaledPoids = d.poids*scale;
+			});
+	},
+	
+	zoomTransition: function(ratio) {
+		var object = this;
+		var c = 0;
+		this.svg.selectAll("circle.node")
+			.transition()
+				.duration(Math.max(400*ratio, 400/ratio))
+				.attr("cx", function(d) {
+					return d.x;
+				})
+				.attr("cy", function(d) {
+					return d.y;
+				})
+				.attr("r", function(d) { return object.radius(d.scaledPoids);})
+				.each("end", function() {
+					c++;
+					if (c == object.length) {
+						object.static = false;
+						object.createBackground();
+					}
+				});
+	},
+	
+	mouseDown: function(e) {
+		this.isDown = true;
+		this.mouse.x = e.x;
+		this.mouse.y = e.y;
+		this.center = {};
+		/*this.center.x = paper.view.center.x;
+		this.center.y = paper.view.center.y;*/
+		var object = this;
+		setTimeout(function() {
+				if (object.isDown) {
+					object.blur();
+					//d3.select(object.canvas).style("visibility", "hidden");
+					object.resetCanvas();
+					object.static = true;
+					object.drag2 = true;
+				}	
+			}, 100);
+	},
+	
+	mouseMove: function(e) {
+		if (this.drag2) {
+			diff = {};
+			diff.x = e.x - this.mouse.x;
+			diff.y = e.y - this.mouse.y;
+			/*this.center.x -= diff.x;
+			this.center.y -= diff.y;*/
+			this.svg.selectAll('circle.node')
+				.attr("cx", function(d) {
+					d.x += diff.x;
+					return d.x;
+					})
+				.attr("cy", function(d) {
+					d.y += diff.y;
+					return d.y;
+					});
+			this.mouse.x = e.x; this.mouse.y = e.y;
+		}
+	},
+	
+	mouseUp: function(e) {
+		if (this.drag2) {
+			//paper.view.center = new paper.Point(this.center.x, this.center.y);
+			//d3.select(object.canvas).style("visibility", "visible");
+			this.createBackground();
+			this.static = false;
+			this.drag2 = false;
+		}
+		this.isDown = false;
+		this.mouse = {};
+	}
+	
+};
\ No newline at end of file