src_js/iconolab-bundle/src/components/cutout/svgboard.js
changeset 146 f912b591e1c1
child 156 e1e14766f608
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src_js/iconolab-bundle/src/components/cutout/svgboard.js	Fri Aug 19 19:04:26 2016 +0200
@@ -0,0 +1,576 @@
+
+
+import Snap from 'snapsvg'
+import ShapeResizer from './shape-resizer'
+import { eventEmitter } from '../utils'
+import SnapsvgZoom from './snapsvg-zoom'
+
+/* custom plugins */
+Snap.plugin(function (Snap, Element, Paper, glob) {
+	var elproto = Element.prototype;
+
+	elproto.toBack = function () {
+		this.prependTo(this.paper);
+	};
+
+	elproto.toFront = function () {
+		this.appendTo(this.paper);
+	};
+});
+
+Element.prototype.getTransformedXY = function( x, y ) {
+            var m = this.transform().globalMatrix;
+            return { x: m.x(x,y), y: m.y(x,y) };             
+    };
+
+var paper = null;
+var mainImage = null;
+var pointData = [];
+var viewBoxBounds = {X: 100, Y:100};
+var zoomFactor = {x:1, y:1};
+var viewPort= {width:850, height:850};
+var currentViewBox = []; 
+var config = null;
+var readOnly = false;
+var startPoint = null;
+var drawing_path = null;
+var canDraw = false;
+var rectZone = null;
+var PATH_COLOR = "#ff00ff";
+var STROKE_COLOR = "red";
+var FILL_COLOR = "orange";
+
+var SELECTED_COLOR = "#ffff00";
+var FIRST_NODE_COLOR = "#FF0000";
+var HANDLE_SIZE = 8;
+var isDragged = false;
+var enablePoint = true;
+var pathIsClosed = false;
+var ENABLE_NEW_NODE = true;
+var RECT_MODE ='RECT';
+var drawingMode = RECT_MODE; //free
+var FREE_MODE = 'FREE';
+var availableModes = [RECT_MODE, FREE_MODE];
+var onChangeCallback = null;
+
+var getId = (function () {
+		var cpt = 0;
+		var defautPrefix = "item_"; 
+		return function (prefix) {
+			prefix = (typeof prefix === 'string') ? prefix : defautPrefix;
+			cpt = cpt + 1;
+			return prefix + cpt; 
+		}
+	}());
+
+var handleRectPath = function (path) {
+	if (readOnly) {
+		paper.path(path).attr({ stroke:'red', opacity: 0.6});
+		return;
+	}
+
+	var bbBox = Snap.path.getBBox(path);
+	rectZone = paper.rect(bbBox.x, bbBox.y, bbBox.width, bbBox.height);
+	rectZone.attr({fill: FILL_COLOR, stroke: STROKE_COLOR, opacity: 0.6});
+	drawing_path = rectZone;
+	canDraw = false;
+	pathIsClosed = true;
+	ShapeResizer.enable_resizer(paper, drawing_path, viewPort, currentViewBox);
+};
+
+
+var handleFreePath = function (path) {
+
+	if (readOnly) {
+		
+		paper.path(path).attr({
+			stroke: 'orange',
+			fill: 'orange',
+			opacity: 0.5,
+		});
+
+		return; 
+	}
+
+	var pathInfos = Snap.parsePathString(path);
+	pathInfos.map(function (pathData) {
+		if(pathData[0] !== 'Z') {
+			createPoint(paper, pathData[1], pathData[2], pointData);
+		} else {
+			pathIsClosed = true;
+			updatePath(paper, onClosePath);
+		}
+	});
+
+	/* replay the path here */
+
+}; 
+//transform point to path
+var updatePath = function (paper, updateCallback) {
+	var path = "M";
+
+	if (pointData.length <= 1) {
+		return;
+	}
+
+	path += pointData[0].x + ',' + pointData[0].y;
+
+	for (var i=0; i < pointData.length; i++) {
+		if (i == 0) continue;
+
+		var pointInfos = pointData[i];
+		var lPath = "L" + pointInfos.x + "," + pointInfos.y;
+		path += " " + lPath;
+	}
+	
+	path += (pathIsClosed) ? " Z": "";
+
+	/* remove prev path */
+	if (drawing_path) {
+		drawing_path.remove();
+	}	
+
+	drawing_path = paper.path(path);
+
+	drawing_path.attr({
+		stroke: STROKE_COLOR,
+		"vector-effect": "non-scaling-stroke",//prevent line to be zoom in
+		"stroke-width": 3,
+		fill: "white",
+		opacity: 0.1
+	});
+	
+	/* bring all handler to front */
+	pointData.map(function (point) {
+		
+		/*deal with handler size */
+		var handleSize = computeHandleSize();
+		if (point.handler) {
+			point.handler.toFront();
+		}
+	});
+
+	if (typeof updateCallback === 'function' && pathIsClosed) {
+		updateCallback();
+	}
+
+	if (!updateCallback && pathIsClosed) {
+		applyClosedStyle();	
+	}
+};
+
+var applyClosedStyle = function () {
+	drawing_path.attr({ fill: FILL_COLOR, strokeWidth: 1, opacity:.6 });
+} 
+
+var onClosePath = function () {
+	ENABLE_NEW_NODE = false;
+	applyClosedStyle();
+};
+
+
+var onClickOnHandler = function (point, p, e) {
+	//close path
+	if (point.isFirst && pointData.length > 2) {
+		pathIsClosed = true;
+	}
+};
+
+var updatePointPosition = function (newPoint, x, y) {
+	var index = pointData.indexOf(newPoint);
+	if (index !== -1) {
+		pointData[index].x = x;
+		pointData[index].y = y; 
+		return true;
+	} else {
+		return false;
+	}
+};
+
+var clearPreviousPath = function () {
+	drawing_path.remove();
+};
+
+var computeHandleSize = function () {
+
+	if(!currentViewBox.length) {
+		currentViewBox = [0, 0, parseInt(mainImage.width()), parseInt(mainImage.height())]
+	}
+	var currentHandleSize = HANDLE_SIZE * Math.min(currentViewBox[2], currentViewBox[3]) / 850; 
+	return currentHandleSize;
+}
+ 
+var onMoveHandler = function (dx, dy, posX, posY, e) {
+	isDragged = true;
+	var tdx, tdy;
+    var snapInvMatrix = this.transform().diffMatrix.invert();
+    snapInvMatrix.e = snapInvMatrix.f = 0;
+    tdx = snapInvMatrix.x( dx,dy ); 
+    tdy = snapInvMatrix.y( dx,dy );
+	var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [tdx, tdy];
+	this.attr({ transform: transformValue});
+	var boxSize = this.getBBox();
+
+	var wasUpdated = updatePointPosition(this.data('point'), boxSize.x + (computeHandleSize() / 2) , boxSize.y + (computeHandleSize() / 2));
+	
+	if (wasUpdated) {
+		updatePath(this.paper);
+	}
+}
+
+var bindHandlerEvent = function (point, p) {
+	point.handler.click(onClickOnHandler.bind(this, point, p));
+	/* -- handler -- */
+	point.handler.hover(function () {
+		point.handler.attr({fill: 'yellow'});
+	}, function () {
+		var fillColor = point.isFirst ? FIRST_NODE_COLOR : "";
+		point.handler.attr({fill: fillColor});
+	});
+	
+	point.handler.drag(onMoveHandler, function () {
+        this.data('origTransform', this.transform().local );
+	}, function () {
+		if (!isDragged) { return true; }
+		isDragged = false;
+		enablePoint = false;
+	});
+}
+
+var createPointHandler = function (p, point) {
+	var handler;
+	var handleSize = computeHandleSize();
+	var handleX = point.x - handleSize / 2;
+	var handleY = point.y - handleSize / 2;
+
+	/* preserve initial size of 5px a quoi correspond 5 deal with current vp */
+	handler = p.rect(handleX, handleY, handleSize, handleSize);
+	
+	handler.addClass("drawingHandler");
+	point.handler = handler;
+	point.handler.data('point', point);
+	if (pointData.length === 0) {
+		point.isFirst = true;
+	}
+	
+	bindHandlerEvent(point, p);
+	point.handler.attr({
+		fill: (pointData.length === 0) ? FIRST_NODE_COLOR : "",
+		opacity: 0.9,
+		stroke: PATH_COLOR
+	});
+
+	return point;
+}
+
+//create paper
+var createPoint = function (paper, x, y, pointData) {
+
+	var point = {x:x, y:y, id: getId()};
+
+	if (pathIsClosed) {
+		updatePath(paper, onClosePath);
+		return;
+	}
+
+	if (!enablePoint) {
+		enablePoint = true;
+		return false;
+	}
+
+	point = createPointHandler(paper, point);
+	pointData.push(point);
+	updatePath(paper);
+};
+
+var attachRectEvents = function (paper) {
+	if (readOnly) { return false; }
+
+	var startPosition = {};
+	var currentPosition = {};
+	/* add resizer */
+
+	paper.mousedown(function (e) {
+
+		if (drawingMode === FREE_MODE || pathIsClosed) { return; }
+		startPosition.x = e.offsetX;
+		startPosition.y = e.offsetY;
+		canDraw = true;
+	});
+
+	paper.mousemove(function (e) {
+		if (drawingMode === FREE_MODE) { return; }
+		if (!canDraw) { return; }
+		var x, y;
+		currentPosition.x = e.offsetX;
+		currentPosition.y = e.offsetY;
+		
+		if (rectZone) {
+			rectZone.remove();
+		}
+
+		/* bas -> droite */
+		var width = Math.abs(currentPosition.x - startPosition.x);
+		var height = Math.abs(startPosition.y - currentPosition.y);
+
+		if (currentPosition.y > startPosition.y && currentPosition.x > startPosition.x) {
+			x = startPosition.x;
+			y = startPosition.y;	
+		}
+		
+		/* haut -> droite */
+		if (currentPosition.y < startPosition.y && currentPosition.x > startPosition.x) {
+			x = currentPosition.x - width;
+			y = currentPosition.y; 
+		}
+		
+		/* haut -> gauche */
+		if (currentPosition.y < startPosition.y && currentPosition.x < startPosition.x) {
+			x = currentPosition.x;
+			y = currentPosition.y;
+		}
+
+		/* bas -> gauche */
+		if (currentPosition.y > startPosition.y && currentPosition.x < startPosition.x) {
+			x = currentPosition.x
+			y = currentPosition.y - height;
+		}
+		if(!x || !y) { return; }	
+
+		rectZone = paper.rect(x, y, width, height);
+		rectZone.attr({fill: FILL_COLOR, stroke: STROKE_COLOR, opacity: 0.6});
+	});
+
+
+	paper.mouseup(function () {
+		if ((drawingMode === FREE_MODE) || pathIsClosed || !rectZone) { return false; }
+		drawing_path = rectZone;
+		ShapeResizer.enable_resizer(paper, rectZone, viewPort, currentViewBox);
+		canDraw = false;
+		pathIsClosed = true;
+	});
+};
+var attachPointEvents = function (paper) {
+	if (readOnly) { return; }
+	paper.click( function(e) {
+		if (drawingMode === RECT_MODE) {
+			return true;
+		}
+
+		if (!ENABLE_NEW_NODE) { return true; }
+		createPoint(paper, e.offsetX, e.offsetY, pointData);
+	});
+};
+
+
+var attachZoomEvents = function () {
+
+	eventEmitter.on("zoomChanged", function (zoomInfos) {
+		zoomFactor = zoomInfos.zoomFactor;
+		currentViewBox = zoomInfos.currentViewBox;
+		var previousPath = API.getPath();
+		API.clear();
+		API.setPath(previousPath);
+	});
+
+}
+
+
+var API = {
+
+	getPaper: function () {
+		return paper;
+	},
+
+	setPath: function (pathString) {
+		/* redraw the path */
+		var pathInfos = pathString.split(';');
+		if ( availableModes.indexOf(pathInfos[1]) === -1) {
+			/* We assume then it is a free path */
+			pathInfos[1] = "FREE"; 
+		}
+
+		this.setDrawingMode(pathInfos[1]);
+		var pathData = pathInfos[0];
+		
+		if (!pathData.length) {
+			return;
+		}
+		/* deal with path nomalization x = ImageWith/MaxXBound */
+		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.');
+		}
+
+		var transformMatrix = Snap.matrix(xRatio, 0, 0, yRatio, 0, 0);
+		var path = Snap.path.map(pathData, transformMatrix).toString();
+		
+		/* always close path */
+		if (path.search(/[z|Z]/gi) === -1 ) {
+			path += "Z";
+		}
+		if (pathInfos.length >= 2) {
+			if (pathInfos[1] === RECT_MODE) {
+				handleRectPath(path);
+			}
+
+			if (pathInfos[1] === FREE_MODE) {
+				handleFreePath(path);
+			}
+		}
+	},
+
+	setDrawingMode: function (mode) {
+		
+		if (availableModes.indexOf(mode) !== -1) {
+			drawingMode = mode;
+		}
+		if (typeof onChangeCallback === "function") {
+			onChangeCallback(drawingMode);
+		}
+
+		this.clear();
+	},
+	
+	clear: function () {
+		/* clear previous path, point, handler */
+		pointData.map(function (point) {
+			if (point.handler) {
+				point.handler.remove();
+			}
+		});
+
+		/*clear path is exists*/
+		 if (drawing_path) {
+		 	drawing_path.remove();
+		 }		
+		eventEmitter.emit("cutout:clear");
+		pointData = [];
+		startPoint = null;
+		drawing_path = null;
+		isDragged = false;
+		enablePoint = true;
+		pathIsClosed = false;
+		ENABLE_NEW_NODE = true;
+	},
+
+	getShapeBBox: function () {
+		var currentPath = this.getPath();
+		return Snap.path.getBBox(currentPath);
+	},
+	
+	getShape: function () {
+		return this.getPath();
+	},
+
+	getPath: function () {
+		/* retourne le chemin */
+		/* send path and BBox | implement edit and load path */
+		var path = "";
+		if (drawing_path) {
+			if (drawingMode === RECT_MODE) {
+				var bBox = drawing_path.getBBox();
+				var transform = drawing_path.transform();
+
+				if (!transform.global.length) {
+					var shapePath = drawing_path.getBBox().path;
+				}
+
+				else {
+					
+					var shapeX = drawing_path.node.getAttribute('x');
+					var shapeY = drawing_path.node.getAttribute('y');
+					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();
+				}
+
+				path = Snap.path.toAbsolute(shapePath).toString();
+
+			}
+			else {
+				path = drawing_path.attr('d');
+			}
+		}
+
+		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.');
+		}
+
+		if (!path.length) {
+			path = (drawingMode === RECT_MODE) ? ";RECT" : ";FREE";
+			return path;
+		}
+		var normalizeMatrix = Snap.matrix(xRatio, 0, 0, yRatio, 0, 0);
+
+		path = Snap.path.map(path, normalizeMatrix).toString();
+
+				/* save the type */
+		var type = (drawingMode === RECT_MODE) ? ";RECT" : ";FREE";
+		if (path.search(/[z|Z]/gi) === -1) {
+			path += " Z";	
+		}
+		
+		path += type;
+
+
+		return path;
+	}
+};
+
+/* change to a component */
+export default {
+
+	init: function(config) {
+		mainImage = jQuery(config.wrapperId).find('.main-image').eq(0);
+		var cutCanvas = jQuery(config.wrapperId).find('.cut-canvas').eq(0);
+		var path = jQuery(config.wrapperId).find('.image-path').eq(0);
+
+		if (typeof config.onDrawingModeChange === 'function') {
+			onChangeCallback = config.onDrawingModeChange;
+		}
+		
+		if (!mainImage.length) {
+			throw new Error("The main image Can't be found ...");
+		}
+
+		if (!cutCanvas.length) {
+			var cutCanvas = jQuery('<svg version="1.1"></svg>').addClass('cut-canvas');
+			jQuery(config.wrapperId).append(cutCanvas);
+			cutCanvas.append(mainImage);
+		}
+
+		
+
+		cutCanvas.css({
+			marginLeft: 'auto',
+			marginRight: 'auto',
+			width: viewPort.width,
+			height: viewPort.height,
+		});
+		if (typeof config.readOnly === 'boolean' && config.readOnly === true) {
+			readOnly = true;
+		}
+
+		paper = new Snap(cutCanvas.get(0));
+
+		if (path.length) {
+			jQuery(cutCanvas).append(path);
+			var pathData = path.attr("d");
+			API.setPath(pathData);
+			path.remove();
+		}
+		/* enable zoom */
+		attachZoomEvents();
+		attachPointEvents(paper);
+		attachRectEvents(paper);
+
+		return API;
+	}
+};
\ No newline at end of file