From 6400d8eb8ec6bbd9a1617c31581ae227f94c178d Mon Sep 17 00:00:00 2001
From: juanf <juanf>
Date: Mon, 3 Mar 2014 14:30:58 +0000
Subject: [PATCH] BIS-604 / SP-1211: ELN UI - Hierarchical View (Zoom and Pan
 Controls)

SVN: 30801
---
 .../as/webapps/newbrowser/html/css/style.css  |  5 ++
 .../html/js/views/SampleHierarchy.js          | 79 +++++++++++++++++--
 2 files changed, 76 insertions(+), 8 deletions(-)

diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/css/style.css b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/css/style.css
index 61f9510db2d..7399d94a0a0 100644
--- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/css/style.css
+++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/css/style.css
@@ -422,6 +422,11 @@ table.downloads {
     fill: none;
 }
 
+.svgButton:hover {
+	fill: #005580;
+	stroke: #005580;
+}
+
 /*
  * Storage Widget
  */
diff --git a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleHierarchy.js b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleHierarchy.js
index cee3eb5b2a2..5c1fcab4291 100644
--- a/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleHierarchy.js
+++ b/plasmid/source/core-plugins/newbrowser/1/as/webapps/newbrowser/html/js/views/SampleHierarchy.js
@@ -137,11 +137,12 @@ function SampleHierarchy(serverFacade, inspector, containerId, profile, sample)
 			.append($filtersFormSliderParents)
 			.append("<span style='padding-right:15px;'></span>")
 			.append(' Show Types: ')
-			.append($filtersFormSampleTypes);
+			.append($filtersFormSampleTypes)
+			.append("<span style='position:absolute; left:30px; top:80px;'><svg height='100' width='100'><g id='svgControls'/></svg></span>");
 		
 		$('#'+this.containerId).append($filtersForm);
 		$('#'+this.containerId).append($('<div>', { 'id' : 'graphContainer' }));
-		$('#graphContainer').append("<svg><g transform='translate(20,20)'/></svg>");
+		$('#graphContainer').append("<svg id='svgMapContainer'><g id='svgMap' transform='translate(20,20) scale(1)'/></svg>");
 		
 		$('#childrenLimit').slider();
 		$('#childrenLimit').slider().on('slideStop', function(event){
@@ -159,6 +160,29 @@ function SampleHierarchy(serverFacade, inspector, containerId, profile, sample)
 		});
 		
 		this._filterSampleAndUpdate();
+		
+		//Add SVG Map Controls
+		var path1 = this._makeSVG('path', {'class' : 'svgButton', 'stroke-linecap':'round', 'stroke-miterlimit':'6', 'onclick':'javascript:mainController.currentView.pan( 0, 50);', 'd':'M50 10 l12 20 a40, 70 0 0,0 -24, 0z', 'stroke-width':'1.5', 'fill':'#0088CC', 'stroke': '#0088CC' });
+		var path2 = this._makeSVG('path', {'class' : 'svgButton', 'stroke-linecap':'round', 'stroke-miterlimit':'6', 'onclick':'javascript:mainController.currentView.pan( 50, 0);', 'd':'M10 50 l20 -12 a70, 40 0 0,0 0, 24z', 'stroke-width':'1.5', 'fill':'#0088CC', 'stroke': '#0088CC' });
+		var path3 = this._makeSVG('path', {'class' : 'svgButton', 'stroke-linecap':'round', 'stroke-miterlimit':'6', 'onclick':'javascript:mainController.currentView.pan( 0,-50);', 'd':'M50 90 l12  -20 a40, 70 0 0,1 -24,  0z', 'stroke-width':'1.5', 'fill':'#0088CC', 'stroke': '#0088CC' });
+		var path4 = this._makeSVG('path', {'class' : 'svgButton', 'stroke-linecap':'round', 'stroke-miterlimit':'6', 'onclick':'javascript:mainController.currentView.pan(-50, 0);', 'd':'M90 50 l-20 -12 a70, 40 0 0,1 0, 24z', 'stroke-width':'1.5', 'fill':'#0088CC', 'stroke': '#0088CC' });
+		var circle2 = this._makeSVG('circle', {'cx':'50', 'cy':'50', 'r':'20', 'stroke':'#000', 'stroke-width':'1.5', 'fill':'#fff', 'opacity':'0.75'});
+		var rect1 = this._makeSVG('rect', {'x':'46', 'y':'39.5', 'width':'8', 'height':'3' , 'fill':'#fff', 'style' : 'pointer-events: none;'});
+		var rect2 = this._makeSVG('rect', {'x':'46', 'y':'57.5', 'width':'8', 'height':'3' , 'fill':'#fff', 'style' : 'pointer-events: none;'});
+		var rect3 = this._makeSVG('rect', {'x':'48.5', 'y':'55', 'width':'3', 'height':'8' , 'fill':'#fff', 'style' : 'pointer-events: none;'});
+		var circle3 = this._makeSVG('circle', {'class' : 'svgButton', 'stroke-linecap':'round', 'stroke-miterlimit':'6', 'onclick':'javascript:mainController.currentView.zoom(0.8)', 'cx':'50', 'cy':'41', 'r':'8', 'stroke-width':'1.5', 'fill':'#0088CC', 'stroke': '#0088CC' });
+		var circle4 = this._makeSVG('circle', {'class' : 'svgButton', 'stroke-linecap':'round', 'stroke-miterlimit':'6', 'onclick':'javascript:mainController.currentView.zoom(1.25)', 'cx':'50', 'cy':'59', 'r':'8', 'stroke-width':'1.5', 'fill':'#0088CC', 'stroke': '#0088CC' });
+		var svgControls = $("#svgControls")
+			.append(path1)
+			.append(path2)
+			.append(path3)
+			.append(path4)
+			.append(circle2)
+			.append(circle3)
+			.append(circle4)
+			.append(rect1)
+			.append(rect2)
+			.append(rect3);
 	}
 	
 	this._filterSampleAndUpdate = function() {
@@ -380,7 +404,7 @@ function SampleHierarchy(serverFacade, inspector, containerId, profile, sample)
 		addSampleEdges(sample);
 		
 		// Render the directed graph
-		var svg = d3.select('svg');
+		var svgG = d3.select('#svgMap');
 		var renderer = new dagreD3.Renderer();
 		
 		// Custom transition function
@@ -394,15 +418,54 @@ function SampleHierarchy(serverFacade, inspector, containerId, profile, sample)
 							.nodeSep(20)
 							.rankDir("TB");
 		
-		var layout = renderer.layout(layout).run(g, svg.select('g'));
-		transition(d3.select('svg'))
+		//Render Layout
+		renderer.layout(layout).run(g, svgG);
+		transition(d3.select('#svgMapContainer'))
 			.attr('width', $(document).width() - 30)
 			.attr('height', $(document).height() - 120)
 		
-		d3.select('svg')
+		d3.select('#svgMapContainer')
 		.call(d3.behavior.zoom().on('zoom', function() {
 			var ev = d3.event;
-			svg.select('g').attr('transform', 'translate(' + ev.translate + ') scale(' + ev.scale + ')');
-		}));
+			svgG.attr('transform', 'translate(' + ev.translate + ') scale(' + ev.scale + ')');
+		})).on("dblclick.zoom", null);
 	}
+	
+	this._makeSVG = function(tag, attrs) {
+        var el= document.createElementNS('http://www.w3.org/2000/svg', tag);
+        for (var k in attrs)
+            el.setAttribute(k, attrs[k]);
+        return el;
+    }
+	
+	//
+	// Zoom and Pan controls
+	//
+    this.pan = function(dx, dy)
+    {
+    	var svgTransform = d3.select('#svgMap').attr("transform");
+		
+		var svgTransformTranslate  = /translate\(\s*([^\s,)]+)[ ,]([^\s,)]+)/.exec(svgTransform);
+		var translateX = parseFloat(svgTransformTranslate[1]) + dx;
+		var translateY = parseFloat(svgTransformTranslate[2]) + dy;
+		
+		var svgTransformScale  = /scale\(([^\s,)]+)/.exec(svgTransform);
+		var scaleRatio = parseFloat(svgTransformScale[1]);
+		
+		d3.select('#svgMap').attr('transform', 'translate(' + translateX + ',' + translateY + ') scale(' + scaleRatio + ')');
+    }
+    
+	this.zoom = function(scale)
+	{
+		var svgTransform = d3.select('#svgMap').attr("transform");
+		
+		var svgTransformTranslate  = /translate\(\s*([^\s,)]+)[ ,]([^\s,)]+)/.exec(svgTransform);
+		var translateX = parseFloat(svgTransformTranslate[1]);
+		var translateY = parseFloat(svgTransformTranslate[2]);
+		
+		var svgTransformScale  = /scale\(([^\s,)]+)/.exec(svgTransform);
+		var scaleRatio = parseFloat(svgTransformScale[1]) * scale;
+		
+		d3.select('#svgMap').attr('transform', 'translate(' + translateX + ',' + translateY + ') scale(' + scaleRatio + ')');
+    }
 }
-- 
GitLab