# HG changeset patch # User Harris Baptiste # Date 1467362402 -7200 # Node ID ccc449ef6f16f024426a7c90886694a6eeecd019 # Parent 51257e2701d9b93de46d561f528ab26307542372 Fixing drawing component diff -r 51257e2701d9 -r ccc449ef6f16 .hgignore --- a/.hgignore Wed Jun 29 14:56:27 2016 +0200 +++ b/.hgignore Fri Jul 01 10:40:02 2016 +0200 @@ -6,6 +6,8 @@ ^src/iconolab/settings/dev\.py$ ^src/iconolab/static/iconolab/js/node_modules/ ^src/iconolab/static/iconolab/js/iconolab-bundle/node_modules/ +^src/iconolab/static/iconolab/js/iconolab-bundle/dist/ + ^web/* ^\.pydevproject$ diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/index.html --- a/src/iconolab/static/iconolab/js/iconolab-bundle/index.html Wed Jun 29 14:56:27 2016 +0200 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/index.html Fri Jul 01 10:40:02 2016 +0200 @@ -5,8 +5,12 @@ iconolab-bundle - Ajouter un tag:
-
+ +
+ + +
+ diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/package.json --- a/src/iconolab/static/iconolab/js/iconolab-bundle/package.json Wed Jun 29 14:56:27 2016 +0200 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/package.json Fri Jul 01 10:40:02 2016 +0200 @@ -5,10 +5,15 @@ "private": true, "scripts": { "dev": "webpack-dev-server --inline --hot", + "start": "webpack --progress --colors --watch", "build": "cross-env NODE_ENV=production webpack --progress --hide-modules" }, "dependencies": { - "vue": "^2.0.0-alpha.2", + "import": "0.0.6", + "loader": "^2.1.1", + "snapsvg": "^0.4.0", + "vue": "^2.0.0-alpha.8", + "vue-loader": "^9.1.1", "vue-resource": "^0.9.1", "vue-typeahead": "^2.1.0" }, diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/Cutout.vue --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/Cutout.vue Fri Jul 01 10:40:02 2016 +0200 @@ -0,0 +1,99 @@ + + diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/index.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/index.js Fri Jul 01 10:40:02 2016 +0200 @@ -0,0 +1,9 @@ + +import CutoutVue from './Cutout.vue' + +export default { + + init: function () { + new Vue(CutoutVue); + } +} \ No newline at end of file diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/shape-resizer.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/shape-resizer.js Fri Jul 01 10:40:02 2016 +0200 @@ -0,0 +1,129 @@ +/* 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" + +var eventEmitter = EventEmitter({}); +var ShapeResizer = function (paper, shape) { + this.paper = paper; + this.shape = shape; + this.handlers = []; + this.isResizing = false; + this.currentPosition = {}; + this.HANDLER_SIZE = 8; + this.SHAPE_MIN_SIZE = 20; + this.states = {}; + this.noop = function (){} + this.init(); +} + +var api = ShapeResizer.prototype = { + + init: function () { + this.showHandlers(); + }, + + showHandlers: function () { + /* show handler here */ + var bbox = this.shape.getBBox(); + 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'}); + var handlerInfos = {position: "t_r", handler: handler}; + this.handlers.push(handlerInfos); + this.shapesGroup = this.paper.g(this.shape, handler); + this.attachEvents(); + }, + + /*one handlers */ + updateShapePositions: function (handlerData, dx, dy) { + //start + var handlerBBox = handlerData.handler.getBBox(); + var shapeBBox = this.shape.data("origBbox"); + var newX = handlerBBox.x + this.HANDLER_SIZE / 2; + var newY = handlerBBox.y + this.HANDLER_SIZE / 2; + + /*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}); + }, + + dragEvents: { + onStart: function (handlerData, dx, dy, e) { + this.startPosition = {x: e.clientX, y: e.clientY}; + this.isResizing = true; + this.currentPosition = {}; + handlerData.handler.data("origTransform", handlerData.handler.transform().local); + this.shape.data("origBbox", this.shape.getBBox()); + this.shape.data("origTransform", this.shape.transform().local); + }, + + onMove: function (handlerData, dx, dy, x, y, e) { + var transformValue = handlerData.handler.data('origTransform') + (handlerData.handler.data('origTransform') ? "T" : "t") + [dx, dy]; + this.currentPosition.x = e.clientX; + this.currentPosition.y = e.clientY; + /* deal with boundaries */ + if (! this.checkBondaries(dx, dy)) { return; } + handlerData.handler.attr({ transform: transformValue}); + this.updateShapePositions(handlerData, dx, dy); + }, + + onStop: function () { + this.isResizing = false; + this.startPosition = {}; + this.currentPosition = {}; + } + }, + + checkBondaries: function (dx, dy) { + var result = true; + + if (this.shape.data("origBbox").width - dx <= this.SHAPE_MIN_SIZE) { + result = false; + } + + if (this.shape.data("origBbox").height - dy <= this.SHAPE_MIN_SIZE) { + result = false; + } + + return result; + }, + + destroy: function () { + this.handlers.map(function (handlerData) { + handlerData.handler.remove(); + }); + delete this; + }, + + attachEvents: function () { + var self = this; + this.handlers.map(function (handlerData) { + handlerData.handler.drag( + self.dragEvents.onMove.bind(self, handlerData), + self.dragEvents.onStart.bind(self, handlerData), + self.dragEvents.onStop.bind(self, handlerData)); + }); + + eventEmitter.on("cutout:clear", function () { + self.destroy(); + }); + + this.shapesGroup.drag(function (dx, dy) { + if (self.isResizing) { return; } + var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy]; + this.transform(transformValue); + }, function () { + this.data('origTransform', this.transform().local); + }, this.noop); + }, +} + +export default { + + apply_resize : function (paper, rect) { + new ShapeResizer(paper, rect); + } +} \ No newline at end of file diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/svgboard.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/cutout/svgboard.js Fri Jul 01 10:40:02 2016 +0200 @@ -0,0 +1,525 @@ + + +var Snap = require('snapsvg'); +//import Snap from 'snapsvg' +import ShapeResizer from './shape-resizer' +import EventEmitter from 'event-emitter' +var eventEmitter = EventEmitter({}); + +/* custom plugin */ +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 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 = 6; +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.apply_resize(paper, drawing_path); +}; + + +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, + "stroke-width": 3, + fill: "white", + opacity: 0.1 + }); + + /* bring all handler to front */ + pointData.map(function (point) { + 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 onMoveHandler = function (dx, dy, posX, posY, e) { + isDragged = true; + /* update point then update the view */ + var transformValue = this.data('origTransform') + (this.data('origTransform') ? "T" : "t") + [dx, dy]; + this.attr({ transform: transformValue}); + var boxSize = this.getBBox(); + + var wasUpdated = updatePointPosition(this.data('point'), boxSize.x + (HANDLE_SIZE / 2) , boxSize.y + (HANDLE_SIZE / 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 handleX = point.x - HANDLE_SIZE/2; + var handleY = point.y - HANDLE_SIZE/2; + + handler = p.rect(handleX, handleY, HANDLE_SIZE, HANDLE_SIZE); + + 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.apply_resize(paper, rectZone); + 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 API = { + + 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.width() / viewBoxBounds.X; + var yRatio = mainImage.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; + }, + + 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.globalMatrix; + 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.width(); + var yRatio = viewBoxBounds.Y / mainImage.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('img').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 (!cutCanvas.length) { + var cutCanvas = jQuery('').addClass('cut-canvas'); + jQuery(config.wrapperId).append(cutCanvas); + } + + if (!mainImage) { + new Error(config.wrapperId + "Can't be found ..."); + } + + cutCanvas.css({ + position: 'absolute', + top: '0px', + left: '15px', + marginLeft: 'auto', + marginRight: 'auto', + width: mainImage.width(), + height: mainImage.height(), + viewBox: '0 0 100 100' + }); + + if (typeof config.readOnly === 'boolean' && config.readOnly === true) { + readOnly = true; + } + + paper = new Snap(cutCanvas.get(0)); + + if (path.length) { + jQuery(cutCanvas).append(path); + API.setPath(path.attr("d")); + } + + attachPointEvents(paper); + attachRectEvents(paper); + + return API; + } +}; \ No newline at end of file diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/static/iconolab/js/iconolab-bundle/src/components/taglist/Taglist.vue --- a/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/taglist/Taglist.vue Wed Jun 29 14:56:27 2016 +0200 +++ b/src/iconolab/static/iconolab/js/iconolab-bundle/src/components/taglist/Taglist.vue Fri Jul 01 10:40:02 2016 +0200 @@ -1,5 +1,4 @@ - {% endblock %} \ No newline at end of file diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/templates/iconolab/detail_annotation.html --- a/src/iconolab/templates/iconolab/detail_annotation.html Wed Jun 29 14:56:27 2016 +0200 +++ b/src/iconolab/templates/iconolab/detail_annotation.html Fri Jul 01 10:40:02 2016 +0200 @@ -29,11 +29,12 @@ -
-

Annotation

+
+

Annotation

Title: {{ annotation.current_revision.title }}

Description: {{ annotation.current_revision.description }}

-

Tags: {{ tags_data }}

+

Tags:

+ Editer | Proposer une révision @@ -89,3 +90,13 @@
{% endblock %} + +{% block footer_js %} + +{% endblock %} \ No newline at end of file diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/templates/iconolab_base.html --- a/src/iconolab/templates/iconolab_base.html Wed Jun 29 14:56:27 2016 +0200 +++ b/src/iconolab/templates/iconolab_base.html Fri Jul 01 10:40:02 2016 +0200 @@ -7,7 +7,8 @@ {% block title %} {% endblock %} {% block main_js %} - + + {% endblock %} {% block page_js %} {% endblock %} @@ -28,6 +29,8 @@ {% include "partials/header.html"%} {% block content %} {% endblock %}
- {% block footer_js %} {% endblock %} + {% block footer_js %} + + {% endblock %} \ No newline at end of file diff -r 51257e2701d9 -r ccc449ef6f16 src/iconolab/views.py --- a/src/iconolab/views.py Wed Jun 29 14:56:27 2016 +0200 +++ b/src/iconolab/views.py Fri Jul 01 10:40:02 2016 +0200 @@ -79,6 +79,7 @@ context = self.get_context_data(**kwargs) context["image"] = image context["form"] = annotation_form + context["tags_data"] = "[]" return render(request, 'iconolab/change_annotation.html', context) def post(self, request, *args, **kwargs): @@ -105,6 +106,8 @@ user_name = request.user.username ) return RedirectView.as_view(url=reverse("annotation_detail", kwargs={'collection_name': collection_name, 'image_guid': image_guid, 'annotation_guid': new_annotation.annotation_guid}))(request) + print(annotation_form.errors) + return HttpResponse("error") class ShowAnnotationView(View, ContextMixin):