adding zoomview component
authorHarris Baptiste <harris.baptiste@iri.centrepompidou.fr>
Fri, 22 Jul 2016 16:48:08 +0200
changeset 61 22c88b2c325f
parent 60 8f1af46307e1
child 62 8702ab13783e
adding zoomview component
src/iconolab/static/iconolab/css/iconolab.css
src/iconolab/static/iconolab/js/iconolab-bundle/.babelrc
src/iconolab/static/iconolab/js/iconolab-bundle/package.json
src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/Cutout.vue
src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/shape-resizer.js
src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/snapsvg-zoom.js
src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/svgboard.js
src/iconolab/static/iconolab/js/iconolab-bundle/src/components/zoomview/Zoomview.vue
src/iconolab/static/iconolab/js/iconolab-bundle/src/components/zoomview/template.html
src/iconolab/static/iconolab/js/iconolab-bundle/src/main.js
src/iconolab/static/iconolab/js/iconolab-bundle/webpack.config.js
src/iconolab/templates/iconolab/change_annotation.html
src/iconolab/templates/iconolab/detail_annotation.html
--- a/src/iconolab/static/iconolab/css/iconolab.css	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/css/iconolab.css	Fri Jul 22 16:48:08 2016 +0200
@@ -6,4 +6,3 @@
 
 .form-drawing-wrapper .selected {border: 1px solid orange; color: white; background-color: orange}
 .showPointer {cursor: pointer;}
-.zoomview-wrapper {border: 1px solid red; width: 135px; height: 100px; text-align: center;}
\ No newline at end of file
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/.babelrc	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/.babelrc	Fri Jul 22 16:48:08 2016 +0200
@@ -1,3 +1,3 @@
 {
-  "presets": ["es2015"]
+  "presets": ["es2015"],
 }
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/package.json	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/package.json	Fri Jul 22 16:48:08 2016 +0200
@@ -25,6 +25,7 @@
   "devDependencies": {
     "babel-core": "^6.0.0",
     "babel-loader": "^6.0.0",
+    "babel-plugin-transform-es2015-shorthand-properties": "^6.8.0",
     "babel-preset-es2015": "^6.0.0",
     "cross-env": "^1.0.6",
     "css-loader": "^0.23.1",
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/Cutout.vue	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/Cutout.vue	Fri Jul 22 16:48:08 2016 +0200
@@ -3,7 +3,7 @@
 	import svgboard from './svgboard'
 	import Typeahead from '../typeahead/Typeahead.vue'
 	import Zoomview from '../zoomview/Zoomview.vue'
-	
+
 	export default {
 
 		el: '#drawing-zone',
@@ -37,22 +37,26 @@
 						self.setDrawingMode(mode, false);
 					}
 			});
-
+			this.$refs.zoomview.setZoomTarget(this.drawingComponent.getPaper());
 			this.showForm();	
 		},
 
 		methods: {
-			
-			incraseDrawingZoom: function () {
-				this.drawingComponent.increaseZoom();
-			},
+
+			computeCentreredViewBox: function () {
+				var zoomSvg = this.$refs.zoomSvg;
+				var viewBox = [];
+				var imageWidth = zoomSvg.getAttribute("width"); 
+				var imageHeight = zoomSvg.getAttribute("height");
 
-			decreaseDrawingZoom: function () {
-				this.drawingComponent.decreaseZoom();
-			},
+				/* normalize */
+				var xRatio = imageWidth / 100 ;
+				var yRatio = imageHeight / 100;
 
-			resetDrawingZoom: function () {
-				this.drawingComponent.reset();
+				var zTarget = this.drawingComponent.getShapeBBox(); 
+				viewBox = [(zTarget.x - 1) * xRatio, (zTarget.y - 1) * yRatio, (zTarget.w + 2) * xRatio, (zTarget.h + 2) * yRatio];
+				return viewBox.join(" ");
+
 			},
 
 			computeZoomedViewBox: function () {
@@ -78,17 +82,11 @@
 		       	else {
 		       		shapeBBox.x = Math.max(0, shapeBBox.x - (((shapeBBox.h / imgRatio) - shapeBBox.w) / 2));
 		            shapeBBox.w = shapeBBox.h / imgRatio;
-		         	console.log(shapeBBox.y);
 		       	}
 		       	viewBoxArray = [shapeBBox.x, shapeBBox.y, shapeBBox.w, shapeBBox.h];
 
 				if (!shapeBBox) { return false; }
-				
-				/* test compute zoom
-					agrandir de sorte max bbox.h==viewPort.h && bbox.w
-				*/
-				//var viewBoxArray = [shapeBBox.x, shapeBBox.y, imageWidth, imageHeight]  
-				console.log(viewBoxArray);
+			
 				return viewBoxArray.join(" ");
 			},
 
@@ -102,7 +100,7 @@
 				}
 
 				if (this.$options.ZOOM_IN === direction) { 
-					mainSvgWrapper.setAttribute("viewBox", this.computeZoomedViewBox());
+					mainSvgWrapper.setAttribute("viewBox", this.computeCentreredViewBox());//this.computeZoomedViewBox());//
 					this.canZoom = false; 
 				}
 			},
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/shape-resizer.js	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/shape-resizer.js	Fri Jul 22 16:48:08 2016 +0200
@@ -1,9 +1,8 @@
 /* Enabling us to resize a shape width a handler 
 	#http://stackoverflow.com/questions/32390028/how-to-drag-and-resize-svg-rectangle-using-cursor-types
 */
-import EventEmitter from "event-emitter"
+import { eventEmitter } from "../utils"
 
-var eventEmitter = EventEmitter({});
 var ShapeResizer = function (paper, shape) {
 	this.paper = paper;
 	this.shape = shape;
@@ -20,6 +19,7 @@
 var api = ShapeResizer.prototype = {
 
 	init: function () {
+		this.currentZoomFactor = {x: 1, y: 1};
 		this.showHandlers();
 	},
 
@@ -29,6 +29,8 @@
 		var handleX = bbox.x - this.HANDLER_SIZE/2;
 		var handleY = bbox.y - this.HANDLER_SIZE/2;
 		var handler = this.paper.rect(handleX, handleY, this.HANDLER_SIZE, this.HANDLER_SIZE).attr({fill: 'red'});
+		handler.addClass("drawingHandler");
+		this.shape.addClass("drawingHandler");
 		var handlerInfos = {position: "t_r", handler: handler};
 		this.handlers.push(handlerInfos);
 		this.shapesGroup = this.paper.g(this.shape, handler);
@@ -46,6 +48,7 @@
 		/*to the right => reduce the size */
 		var newWidth = (dx > 0) ? shapeBBox.width - dx : shapeBBox.width + Math.abs(dx); 
 		var newHeight = (dy > 0) ? shapeBBox.height - dy : shapeBBox.height + Math.abs(dy);
+		
 		var transformValue = this.shape.data('origTransform') + (this.shape.data('origTransform') ? "T" : "t") + [dx, dy];
 		this.shape.attr({'transform': transformValue, width: newWidth, height: newHeight});
 	},
@@ -61,6 +64,8 @@
 		},
 
 		onMove: function (handlerData, dx, dy, x, y, e) {
+			dx = dx / this.currentZoomFactor.x;
+			dy = dy / this.currentZoomFactor.y;
 			var transformValue = handlerData.handler.data('origTransform') + (handlerData.handler.data('origTransform') ? "T" : "t") + [dx, dy];
 			this.currentPosition.x = e.clientX;
 			this.currentPosition.y = e.clientY;
@@ -111,8 +116,14 @@
 			self.destroy();
 		});
 
+		eventEmitter.on("zoomChanged", function (zoomInfos) {
+			self.currentZoomFactor = zoomInfos.zoomFactor; 
+		});
+
 		this.shapesGroup.drag(function (dx, dy) {
 			if (self.isResizing) { return; }
+			dx = dx / self.currentZoomFactor.x;
+			dy = dy / self.currentZoomFactor.y;
 			var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy];
 			this.transform(transformValue);
 		}, function () {
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/snapsvg-zoom.js	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/snapsvg-zoom.js	Fri Jul 22 16:48:08 2016 +0200
@@ -6,40 +6,72 @@
 	constructor (params) {
 		this.zoomFactor = 0.1 || params.zoomFactor;
 		this.paper = params.paper;
-		this.viewport = {width: 529, height: 800};
+		var width = parseInt(this.paper.select(".main-image").attr("width"));
+		var height = parseInt(this.paper.select(".main-image").attr("height"));
+		this.viewport = {width: width, height: height};
 		this.scale = 1;
 		this.disableDrag = false;
-		this.paper.attr({"preserveAspectRatio": "xMidYMid meet"});
 		this.currentViewBox = [0, 0, this.viewport.width, this.viewport.height];
-		this.paper.drag(this.onDrag.bind(this), this.onStart.bind(this), this.onStop.bind(this));
 		this.lastPosition = []; 
 	}
 
+	testShowCenter (cx, cy) {
+	
+		if (this.center) {
+			this.center.remove();
+		}
+		this.center = this.paper.rect(cx - 3, cy - 3, 20, 20);
+		this.center.attr({"fill" : "red"});
+	}
+	
+	drawTestRectangle (cx, cy, w, h) {
+		var x = cx - w / 2;
+		var y = cy - h / 2;
+		this.paper.rect(x, y, w, h);
+	}
+
 	zoomIn () {
-		var viewBoxW = this.viewport.width - (this.viewport.width * this.zoomFactor * this.scale);
-		var viewBoxH = this.viewport.height - (this.viewport.height * this.zoomFactor * this.scale);
-		if (!viewBoxW || !viewBoxH) { return false; }
 
-		/* center the viewbox */
-		var topX = (this.viewport.width - viewBoxW) / 2; 
-		var topY = (this.viewport.height - viewBoxH) / 2; 
+		/* current center */
+		var currentCenterX = this.currentViewBox[0] + (this.currentViewBox[2] / 2);
+		var currentCenterY = this.currentViewBox[1] + (this.currentViewBox[3] / 2);
+		var scaleFactor = this.zoomFactor * this.scale;
+		var viewBoxW = this.viewport.width - (this.viewport.width * scaleFactor);
+		var viewBoxH = this.viewport.height - (this.viewport.height * scaleFactor);
+		
+		if (viewBoxW <= 0 && viewBoxH <=0) {
+			return false;
+		}
 
-		this.currentViewBox[0] = topX; 
-		this.currentViewBox[1] = topY;
+		this.currentViewBox[0] = currentCenterX - viewBoxW / 2;
+		this.currentViewBox[1] = currentCenterY - viewBoxH / 2;
 
 		this.currentViewBox[2] = viewBoxW;
 		this.currentViewBox[3] = viewBoxH;
-
 		this.updateViewBox();
 		this.scale ++;
 	}
 	
-	updateViewBox () {
+	updateViewBox (currentViewBox, notify) {
+		notify = (typeof notify === "boolean") ? notify : true;
+
+		if (currentViewBox && currentViewBox.length != 4) { throw new Error("Provided currentViewBox is not valid!"); }
+		if (currentViewBox) {
+			this.currentViewBox = currentViewBox;
+		}
+		
 		this.paper.attr({"viewBox": this.currentViewBox});
+
+		if (!notify) { return false; }
+
+		var self = this;		
 		eventEmitter.emit("zoomChanged", {
+			updateFunction: function (updatedViewBox) {
+				self.updateViewBox(updatedViewBox, false);
+			},
 			"zoomFactor": this.getZoomFactor(),
 			"viewport": this.viewport,
-			"currentViewBox": this.currentViewBox
+			"currentViewBox": this.currentViewBox.slice()
 		});
 	}
 	
@@ -103,15 +135,19 @@
 
 	zoomOut () {
 		if (this.scale === 1) { return false; }
+		
+		var currentCenterX = this.currentViewBox[0] + (this.currentViewBox[2] / 2);
+		var currentCenterY = this.currentViewBox[1] + (this.currentViewBox[3] / 2);
+
 		var viewBoxW = this.currentViewBox[2] + (this.viewport.width * this.zoomFactor);
 		var viewBoxH = this.currentViewBox[3] + (this.viewport.height * this.zoomFactor);
-		if (viewBoxW > this.viewport.width || viewBoxW > this.viewport.height) {
+		if (viewBoxW >= this.viewport.width || viewBoxW >= this.viewport.height) {
 			return false;
 		}
+		var topX = currentCenterX - viewBoxW / 2;
+		var topY = currentCenterY - viewBoxH / 2;
 
-		var topX = (this.viewport.width - viewBoxW) / 2;
-		var topY = (this.viewport.height - viewBoxH) / 2; 
-  
+		/* fix size */ 
 		this.currentViewBox[0] = topX;
 		this.currentViewBox[1] = topY; 
 		this.currentViewBox[2] = viewBoxW;
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/svgboard.js	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/svgboard.js	Fri Jul 22 16:48:08 2016 +0200
@@ -2,11 +2,9 @@
 
 import Snap from 'snapsvg'
 import ShapeResizer from './shape-resizer'
-import EventEmitter from 'event-emitter'
+import { eventEmitter } from '../utils'
 import SnapsvgZoom from './snapsvg-zoom'
 
-var eventEmitter = EventEmitter({});
-
 /* custom plugins */
 Snap.plugin(function (Snap, Element, Paper, glob) {
 	var elproto = Element.prototype;
@@ -29,7 +27,7 @@
 var mainImage = null;
 var pointData = [];
 var viewBoxBounds = {X: 100, Y:100};
-var zoomManager = null;
+var zoomFactor = {x:1, y:1};
 var config = null;
 var readOnly = false;
 var startPoint = null;
@@ -189,11 +187,10 @@
 
 var onMoveHandler = function (dx, dy, posX, posY, e) {
 	isDragged = true;
-	/* update point then update the view */
-	var zoomFactor = zoomManager.getZoomFactor();
-	console.log(zoomFactor);
-	dx = dx / zoomFactor.x;
-	dy = dy / zoomFactor.y;
+	var factorX = zoomFactor.x ? zoomFactor.x : 1;
+	var factorY = zoomFactor.y ? zoomFactor.y : 1;
+	dx = dx / factorX;
+	dy = dy / factorY;
 	var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy];
 	this.attr({ transform: transformValue});
 	var boxSize = this.getBBox();
@@ -216,7 +213,6 @@
 	});
 	
 	point.handler.drag(onMoveHandler, function () {
-		console.log(this.transform().globalMatrix);
         this.data('origTransform', this.transform().local );
 	}, function () {
 		if (!isDragged) { return true; }
@@ -330,7 +326,7 @@
 	paper.mouseup(function () {
 		if ((drawingMode === FREE_MODE) || pathIsClosed || !rectZone) { return false; }
 		drawing_path = rectZone;
-		ShapeResizer.apply_resize(paper, rectZone);
+		ShapeResizer.enable_resizer(paper, rectZone);
 		canDraw = false;
 		pathIsClosed = true;
 	});
@@ -347,18 +343,18 @@
 	});
 };
 
+
+var attachZoomEvents = function () {
+	eventEmitter.on("zoomChanged", function (zoomInfos) {
+		zoomFactor = zoomInfos.zoomFactor;
+	});
+}
+
+
 var API = {
 
-	increaseZoom: function () {
-		zoomManager.zoomIn();
-	},	
-
-	reset: function () {
-		zoomManager.reset();
-	},
-
-	decreaseZoom: function () {
-		zoomManager.zoomOut();
+	getPaper: function () {
+		return paper;
 	},
 
 	setPath: function (pathString) {
@@ -376,8 +372,8 @@
 			return;
 		}
 		/* deal with path nomalization x = ImageWith/MaxXBound */
-		var xRatio = mainImage.width() / viewBoxBounds.X;
-		var yRatio = mainImage.height() / viewBoxBounds.Y;
+		var xRatio = mainImage.attr("width") / viewBoxBounds.X;
+		var yRatio = mainImage.attr("height") / viewBoxBounds.Y;
 		
 		if (isNaN(xRatio) || isNaN(yRatio)) {
 			new Error('Ratio should be a number.');
@@ -439,6 +435,10 @@
 		var currentPath = this.getPath();
 		return Snap.path.getBBox(currentPath);
 	},
+	
+	getShape: function () {
+		return this.getPath();
+	},
 
 	getPath: function () {
 		/* retourne le chemin */
@@ -454,11 +454,10 @@
 				}
 
 				else {
-
+					
 					var shapeX = drawing_path.node.getAttribute('x');
 					var shapeY = drawing_path.node.getAttribute('y');
-
-					var transformMatrix = transform.globalMatrix;
+					var transformMatrix = transform.totalMatrix;
 					var fakeShape = paper.rect(transformMatrix.x(shapeX, shapeY),transformMatrix.y(shapeX, shapeY), bBox.width, bBox.height);
 					shapePath = fakeShape.getBBox().path;
 					fakeShape.remove();
@@ -472,8 +471,9 @@
 			}
 		}
 
-		var xRatio = viewBoxBounds.X / mainImage.width();
-		var yRatio = viewBoxBounds.Y / mainImage.height();
+		var xRatio = viewBoxBounds.X / mainImage.attr("width");
+		var yRatio = viewBoxBounds.Y / mainImage.attr("height");
+
 		if(isNaN(xRatio) || isNaN(yRatio)) {
 			new Error('ratio should be a number.');
 		}
@@ -482,8 +482,8 @@
 			path = (drawingMode === RECT_MODE) ? ";RECT" : ";FREE";
 			return path;
 		}
+		var normalizeMatrix = Snap.matrix(xRatio, 0, 0, yRatio, 0, 0);
 
-		var normalizeMatrix = Snap.matrix(xRatio, 0, 0, yRatio, 0, 0);
 		path = Snap.path.map(path, normalizeMatrix).toString();
 
 				/* save the type */
@@ -494,6 +494,7 @@
 		
 		path += type;
 
+
 		return path;
 	}
 };
@@ -532,6 +533,12 @@
 			height: mainImage.attr('height'),
 			//viewBox: '0 0 100 100'
 		});
+		
+		/* fix the container size */
+		jQuery(config.wrapperId).css({ 
+			width: mainImage.attr('width'),
+			height: parseInt(mainImage.attr('height')) + 5
+		});
 
 		if (typeof config.readOnly === 'boolean' && config.readOnly === true) {
 			readOnly = true;
@@ -546,7 +553,7 @@
 			path.remove();
 		}
 		/* enable zoom */
-		zoomManager = SnapsvgZoom.enable_zoom({paper : paper});
+		attachZoomEvents();
 		attachPointEvents(paper);
 		attachRectEvents(paper);
 
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/zoomview/Zoomview.vue	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/zoomview/Zoomview.vue	Fri Jul 22 16:48:08 2016 +0200
@@ -4,31 +4,154 @@
 <script>
 
 import {eventEmitter} from '../utils'
+import ZoomHandler from '../cutout/snapsvg-zoom'
 
 import Snap from 'snapsvg'
 
 export default {
 
-	props: ['image-url', 'width', 'height'],
+	props: ['image-url', 'width', 'height', 'zoomTarget'],
 	
 	mounted () {
 		this.root = new Snap(this.$refs['root-svg']);
+		this.handler = this.root.select('#zoom-handler');
+		this.image = this.root.select('#small-image');
+		//this.ratio = this.image.attr('width') / this.image.attr('height');
+		this.root.attr({width: this.width, height: this.height});
+		this.handlerSize = 20;
+		this.handler.attr({'width': this.handlerSize, 'height': this.handlerSize});
+		this.currentViewBox  = null;
+		this.currentViewport = null;
+		this.updateFunction = null;
+		var target = document.getElementById('zoomTarget');
+
+		if (target) {
+			this.setZoomTarget(target);
+		}
 		this.handleEvents();
 	},
 
-	/* reduire size en fonction */
+	data () {
+		return {
+			showHandler: false
+		}
+	},
 	methods: {
 		
+		setZoomTarget: function (zoomtarget) {
+			if (zoomtarget.hasOwnProperty("type") && zoomtarget.type === 'svg') {
+				this.paper = zoomtarget; 	
+			}
+			else {
+				this.paper = new Snap(zoomtarget);
+			}
+			this.zoomHandler =  ZoomHandler.enable_zoom(this.paper);
+		},
+
+		incraseDrawingZoom: function () {
+			this.zoomHandler.zoomIn();
+		},
+
+		resetDrawingZoom: function () {
+			this.zoomHandler.reset();
+		},
+
+		decreaseDrawingZoom: function () {
+			this.zoomHandler.zoomOut();
+		},
+		
 		handleEvents: function () {
-			eventEmitter.on("zoomChanged", function (zoomInfos) {
-				console.log("zoomInfos", zoomInfos.currentViewBox);
-			});
+			eventEmitter.on('zoomChanged', this.handleZoomChanged.bind(this));
+			var self = this;
+			
+			this.handler.drag(function (dx, dy) {
+				var bbox = this.getBBox();
+				var startX = this.data("startPosition").x;
+				var startY = this.data("startPosition").y;
+				var imageWidth = parseInt(self.image.attr('width'));
+				var imageHeight = parseInt(self.image.attr('height'));
+
+				/* New X */
+				var newX = startX + dx;
+				var newY = startY + dy;
+				
+				/* XBound */
+				if (newX + bbox.w >= imageWidth) {
+					newX = imageWidth - bbox.w; 
+				}
+				/* YBound */
+				if (newY + bbox.h >= imageHeight) {
+					newY = imageHeight - bbox.h;
+				}
+
+				newX = newX < 0 ? 0: newX;
+				newY = newY < 0 ? 0: newY;
+
+				var transformedValue = "T" + [newX, newY];
+				this.attr("transform", transformedValue);
+				self.notifyMove(this.getBBox().x, this.getBBox().y);
+
+			}, function () {
+				this.data("startPosition", {x: this.getBBox().x, y: this.getBBox().y});
+				this.data("origTransform", this.transform().local);
+			}, function () {});
 		},
 
-		createHandler: function () {},
+		moveHandlerToCenter: function (x, c) {
+			cx = cx ? cx : this.width / 2;
+			cy = cy ? cy : this.height / 2;
+			var moveX = cx - this.handler.getBBox().w / 2;
+			var moveY = cy - this.handler.getBBox().h / 2;
+			this.handler.transform( "T" + [moveX, moveY]);
+		},
+		
+		notifyMove: function (x, y) {
+			eventEmitter.emit("moveZoomHandler", {
+				x: x,
+			 	y: y, 
+			 viewport: {
+			 	width: this.image.attr("width"),
+			 	height: this.image.attr("height")
+			 }});
+
+			if (!this.currentViewBox || !this.currentViewport || typeof this.updateFunction !== 'function') { return false; }
+			var xRatio = this.currentViewport.width / this.image.attr('width');
+			var yRatio = this.currentViewport.height / this.image.attr('height');
+			var newX = x * xRatio;
+			var newY = y * yRatio;
+			this.currentViewBox[0] = newX;
+			this.currentViewBox[1] = newY;
+			this.updateFunction(this.currentViewBox);
+		},
 
-		handlerZoomChanged: function (params) {
-			console.log("params", params);
+		handleZoomChanged: function (zoomInfos) {
+			
+			if (zoomInfos.zoomFactor.x === 1 && zoomInfos.zoomFactor.y === 1) { 
+				this.showHandler = false;
+				return; 
+			}
+
+			this.showHandler = true;
+
+			this.currentViewport = zoomInfos.viewport;
+			this.currentViewBox = zoomInfos.currentViewBox;
+			this.updateFunction = zoomInfos.updateFunction;
+
+			var vpW = zoomInfos.viewport.width;
+			var vpH = zoomInfos.viewport.height;
+			
+			/* handler size */
+			var minSize = Math.min(zoomInfos.currentViewBox[2], zoomInfos.currentViewBox[3]);
+			var HSize = minSize * Math.min(this.image.attr("width"),this.image.attr("height")) / Math.min(vpW,vpH);
+			this.handler.attr("width", HSize);
+			this.handler.attr("height", HSize);
+			//this.handler.attr("width", (zoomInfos.currentViewBox[2]) * this.image.attr("width") / vpW);
+			//this.handler.attr("height", (zoomInfos.currentViewBox[2]) * this.image.attr("width") / vpW);
+
+			/*compute handler x, y */
+			var x = zoomInfos.currentViewBox[0] * this.image.attr("width") / vpW;
+			var y = zoomInfos.currentViewBox[1] * this.image.attr("height")/ vpH;
+			this.handler.transform( "T" + [x, y]);
 		} 
 	}
 }
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/zoomview/template.html	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/zoomview/template.html	Fri Jul 22 16:48:08 2016 +0200
@@ -1,12 +1,22 @@
 <div class="zoomview-wrapper">
+
+	<p class='form-drawing pullright'><strong>Zoom</strong></p>
+
 	<svg ref="root-svg">
 		<image 
+			id="small-image"
 			x="0" 
 			y="0" 
 			xmlns:xlink="http://www.w3.org/1999/xlink" 
 			:xlink:href="imageUrl" 
 			:width="width" 
 			:height="height"></image>
-		<rect ref='handler' x="0" y="0" width="10" height="10" style="fill:orange; opacity:0.7"></rect>
+		<rect v-show="showHandler" id="zoom-handler" ref="handler" x="0" y="0" style="fill:orange; opacity:0.7"></rect>
 	</svg>
+
+	<ul class="form-drawing-wrapper list-inline">
+		<li class="small showPointer" @click="incraseDrawingZoom"><i class="fa fa-search-plus"></i> </li>
+		<li class="small showPointer" @click="resetDrawingZoom"><i class="fa fa-square"></i> </li>
+		<li class="small showPointer" @click="decreaseDrawingZoom"><i class="fa fa-search-minus"></i> </li>
+	</ul>
 </div>
\ No newline at end of file
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/main.js	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/main.js	Fri Jul 22 16:48:08 2016 +0200
@@ -4,11 +4,13 @@
 import VueResource from 'vue-resource'
 import Typeahead from './components/typeahead/Typeahead.vue'
 import Cutout from './components/cutout'
+import Zoomview from './components/zoomview/Zoomview.vue'
 
 var iconolab = {
 	Cutout : Cutout,
 	VueComponents : {
 		Typeahead: Typeahead,
+		Zoomview: Zoomview
 	}
 };
 
--- a/src/iconolab/static/iconolab/js/iconolab-bundle/webpack.config.js	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/static/iconolab/js/iconolab-bundle/webpack.config.js	Fri Jul 22 16:48:08 2016 +0200
@@ -19,7 +19,8 @@
       {
         test: /\.js$/,
         loader: 'babel',
-        exclude: /node_modules/
+        exclude: /node_modules/,
+        "plugins": ["transform-es2015-shorthand-properties"]
       },
       {
         test: /\.(png|jpg|gif|svg)$/,
--- a/src/iconolab/templates/iconolab/change_annotation.html	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/templates/iconolab/change_annotation.html	Fri Jul 22 16:48:08 2016 +0200
@@ -14,15 +14,9 @@
 					<li @click="setDrawingMode('RECT')" v-bind:class="{ 'selected': isRect }" class='pull-md-right drawingModeBtn'>Rect.</li>
 					<li @click="setDrawingMode('FREE')" v-bind:class="{ 'selected': !isRect }" class='pull-md-right drawingModeBtn'>Libre</li>
 				</ul>
-				
-				<ul class="form-drawing-wrapper list-inline">
-					<p class='form-drawing pullright'><strong>Zoom</strong></p>
-					<li class="small" @click="incraseDrawingZoom"><i class="fa fa-plus"></i> Zoomer</li>
-					<li class="small" @click="resetDrawingZoom"><i class="fa fa-square"></i> Taille réelle</li>
-					<li class="small" @click="decreaseDrawingZoom"><i class="fa fa-minus"></i> Dézoomer</li>
-				</ul>
+
 				{% thumbnail image.media "x100" crop="center" as im %}
-					<zoomview :image-url="'{{ im.url }}'" :width="{{ im.width }}" :height="{{ im.height }}">
+					<zoomview ref="zoomview" :image-url="'{{ im.url }}'" :width="{{ im.width }}" :height="{{ im.height }}">
 					</zoomview>
 				{% endthumbnail %}
 
@@ -58,7 +52,7 @@
 				<div class="small-image-wrapper" style="position: relative">
 					{% thumbnail image.media "x300" crop="center" as im %}
 
-						<svg ref="smallSvgWrapper" width="{{ im.width }}" preserveAspectRatio="xMinYMin meet" height="{{ im.height }}" version="1.1">
+						<svg ref="smallSvgWrapper" width="{{ im.width }}" height="{{ im.height }}" version="1.1">
 							
 							<image ref="smallImage" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="{{ im.url }}" x="0" y="0"  width="{{ im.width }}" height="{{ im.height }}"></image>
 
--- a/src/iconolab/templates/iconolab/detail_annotation.html	Wed Jul 13 17:57:35 2016 +0200
+++ b/src/iconolab/templates/iconolab/detail_annotation.html	Fri Jul 22 16:48:08 2016 +0200
@@ -11,11 +11,11 @@
 	<a href="{% url 'image_detail' collection_name image_guid %}"><i class="fa fa-list"></i> Revoir l'image </a>
 	<div id="annotation-wrapper" class="row" style="border: 1px solid gray">
 		
-		<div v-show="formView" class="col-md-12">
-			<div class="col-xs-6">
+		<div id="detail-annotation" class="col-md-12">
+			<div v-show="!showZoom" class="col-md-6">
 				<div class="small-image-wrapper" style="position: relative">
 					{% thumbnail annotation.image.media "x300" crop="center" as im %}
-						<img v-el:small-image src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
+						<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
 
 						<svg width="{{ im.width }}" height="{{ im.height }}" version="1.1" style="position:absolute; top:0px; left: 0px">
 
@@ -27,9 +27,10 @@
 						
 					{% endthumbnail %}
 				</div>
+        <p @click="toggleZoomView" style="padding-top:2px"><i class="fa fa-search-plus showPointer"></i></p>
 			</div>
 
-			<div id="detail-annotation" class='col-xs-6' style="">
+			<div v-show="!showZoom" class='col-md-6'>
         <h4>Annotation créée par {{ annotation.author.username }}</h4>
                 
 				<p> <strong>Title:</strong> {{ annotation.current_revision.title }}</p>
@@ -39,6 +40,32 @@
 				<a href="{% url 'annotation_edit' collection_name image_guid annotation_guid  %}">{% if user == annotation.author %}Editer{% else %}Proposer une révision{% endif %}</a>
                 
 			</div>
+      <!-- zoomView -->
+      <div class="col-md-12 zoom-view" style="display:none" v-show="showZoom">
+        
+        <div class="col-md-2">
+          {% thumbnail annotation.image.media "x100" crop="center" as im %}
+          <zoomview ref="zoomview" :zoomtarget="'zoomTarget'" :image-url="'{{ im.url }}'" :width="{{ im.width }}" :height="{{ im.height }}"></zoomview>
+          {% endthumbnail %}
+
+          <p><i class="fa fa-close">Voir le détail</p>
+        </div>
+
+        <div class="col-md-8">
+          {% thumbnail annotation.image.media "x900" crop="center" as im %}
+            <svg id="zoomTarget" ref="zoomTarget" width="{{ im.width }}" height="{{ im.height }}">
+              <image class="main-image" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="{{ im.url }}" x="0" y="0" width="{{ im.width }}" height="{{ im.height }}"></image>
+              <g transform="matrix({% transform_matrix im_width=im.width im_height=im.height max_x=100 max_y=100 %})">
+                <path d="{{ annotation.current_revision.fragment|clean_path }}" opacity="0.7" fill="orange"></path>
+              </g>
+            </svg>
+          {% endthumbnail %}
+        </div>
+        <div class="col-md-2">
+          <p class="showPointer" @click="toggleZoomView"><i class="fa fa-close"></i></p>
+        </div>
+      </div>
+
 		</div>
         <div class='col-md-12'>
           <h4>Commentaires</h4>
@@ -114,8 +141,30 @@
   <script>
     new Vue({
       el: "#detail-annotation",
-      components: {'Typeahead': iconolab.VueComponents.Typeahead }, 
-      methods: { yo: function(){alert(1);} }
+      
+      data: {
+        showZoom: false
+      },
+
+      methods: {
+        
+        toggleZoomView: function () {
+          if (this.showZoom) {
+            this.showZoom = false;
+          }
+
+          else {
+            this.showZoom = true;
+          }
+        },
+
+
+      },
+
+      components: {
+        'Typeahead': iconolab.VueComponents.Typeahead,
+        'Zoomview': iconolab.VueComponents.Zoomview
+      }
     });
   </script>
 {% endblock %}
\ No newline at end of file