d3-svg-annotation
Version:
Full documentation: [http://d3-annotation.susielu.com](http://d3-annotation.susielu.com)
1,717 lines (1,440 loc) • 63.7 kB
JavaScript
import { event, select } from 'd3-selection';
import { drag } from 'd3-drag';
import { arc, curveCatmullRom, curveLinear, line } from 'd3-shape';
import { dispatch } from 'd3-dispatch';
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var _extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var get = function get(object, property, receiver) {
if (object === null) object = Function.prototype;
var desc = Object.getOwnPropertyDescriptor(object, property);
if (desc === undefined) {
var parent = Object.getPrototypeOf(object);
if (parent === null) {
return undefined;
} else {
return get(parent, property, receiver);
}
} else if ("value" in desc) {
return desc.value;
} else {
var getter = desc.get;
if (getter === undefined) {
return undefined;
}
return getter.call(receiver);
}
};
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var toConsumableArray = function (arr) {
if (Array.isArray(arr)) {
for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];
return arr2;
} else {
return Array.from(arr);
}
};
var Annotation = function () {
function Annotation(_ref) {
var _ref$x = _ref.x,
x = _ref$x === undefined ? 0 : _ref$x,
_ref$y = _ref.y,
y = _ref$y === undefined ? 0 : _ref$y,
nx = _ref.nx,
ny = _ref.ny,
_ref$dy = _ref.dy,
dy = _ref$dy === undefined ? 0 : _ref$dy,
_ref$dx = _ref.dx,
dx = _ref$dx === undefined ? 0 : _ref$dx,
_ref$color = _ref.color,
color = _ref$color === undefined ? "grey" : _ref$color,
data = _ref.data,
type = _ref.type,
subject = _ref.subject,
connector = _ref.connector,
note = _ref.note,
disable = _ref.disable,
id = _ref.id,
className = _ref.className;
classCallCheck(this, Annotation);
this._dx = nx !== undefined ? nx - x : dx;
this._dy = ny !== undefined ? ny - y : dy;
this._x = x;
this._y = y;
this._color = color;
this.id = id;
this._className = className || "";
this._type = type || "";
this.data = data;
this.note = note || {};
this.connector = connector || {};
this.subject = subject || {};
this.disable = disable || [];
}
createClass(Annotation, [{
key: "updatePosition",
value: function updatePosition() {
if (this.type.setPosition) {
this.type.setPosition();
if (this.type.subject && this.type.subject.selectAll(":not(.handle)").nodes().length !== 0) {
this.type.redrawSubject();
}
}
}
}, {
key: "clearComponents",
value: function clearComponents() {
this.type.clearComponents && this.type.clearComponents();
}
}, {
key: "updateOffset",
value: function updateOffset() {
if (this.type.setOffset) {
this.type.setOffset();
if (this.type.connector.selectAll(":not(.handle)").nodes().length !== 0) {
this.type.redrawConnector();
}
this.type.redrawNote();
}
}
}, {
key: "className",
get: function get$$1() {
return this._className;
},
set: function set$$1(className) {
this._className = className;
if (this.type.setClassName) this.type.setClassName();
}
}, {
key: "type",
get: function get$$1() {
return this._type;
},
set: function set$$1(type) {
this._type = type;
this.clearComponents();
}
}, {
key: "x",
get: function get$$1() {
return this._x;
},
set: function set$$1(x) {
this._x = x;
this.updatePosition();
}
}, {
key: "y",
get: function get$$1() {
return this._y;
},
set: function set$$1(y) {
this._y = y;
this.updatePosition();
}
}, {
key: "color",
get: function get$$1() {
return this._color;
},
set: function set$$1(color) {
this._color = color;
this.updatePosition();
}
}, {
key: "dx",
get: function get$$1() {
return this._dx;
},
set: function set$$1(dx) {
this._dx = dx;
this.updateOffset();
}
}, {
key: "dy",
get: function get$$1() {
return this._dy;
},
set: function set$$1(dy) {
this._dy = dy;
this.updateOffset();
}
}, {
key: "nx",
set: function set$$1(nx) {
this._dx = nx - this._x;
this.updateOffset();
}
}, {
key: "ny",
set: function set$$1(ny) {
this._dy = ny - this._y;
this.updateOffset();
}
}, {
key: "offset",
get: function get$$1() {
return { x: this._dx, y: this._dy };
},
set: function set$$1(_ref2) {
var x = _ref2.x,
y = _ref2.y;
this._dx = x;
this._dy = y;
this.updateOffset();
}
}, {
key: "position",
get: function get$$1() {
return { x: this._x, y: this._y };
},
set: function set$$1(_ref3) {
var x = _ref3.x,
y = _ref3.y;
this._x = x;
this._y = y;
this.updatePosition();
}
}, {
key: "translation",
get: function get$$1() {
return {
x: this._x + this._dx,
y: this._y + this._dy
};
}
}, {
key: "json",
get: function get$$1() {
var json = {
x: this._x,
y: this._y,
dx: this._dx,
dy: this._dy
};
if (this.data && Object.keys(this.data).length > 0) json.data = this.data;
if (this.type) json.type = this.type;
if (this._className) json.className = this._className;
if (Object.keys(this.connector).length > 0) json.connector = this.connector;
if (Object.keys(this.subject).length > 0) json.subject = this.subject;
if (Object.keys(this.note).length > 0) json.note = this.note;
return json;
}
}]);
return Annotation;
}();
var AnnotationCollection = function () {
function AnnotationCollection(_ref) {
var annotations = _ref.annotations,
accessors = _ref.accessors,
accessorsInverse = _ref.accessorsInverse;
classCallCheck(this, AnnotationCollection);
this.accessors = accessors;
this.accessorsInverse = accessorsInverse;
this.annotations = annotations;
}
createClass(AnnotationCollection, [{
key: "clearTypes",
value: function clearTypes(newSettings) {
this.annotations.forEach(function (d) {
d.type = undefined;
d.subject = newSettings && newSettings.subject || d.subject;
d.connector = newSettings && newSettings.connector || d.connector;
d.note = newSettings && newSettings.note || d.note;
});
}
}, {
key: "setPositionWithAccessors",
value: function setPositionWithAccessors() {
var _this = this;
this.annotations.forEach(function (d) {
d.type.setPositionWithAccessors(_this.accessors);
});
}
}, {
key: "editMode",
value: function editMode(_editMode) {
this.annotations.forEach(function (a) {
if (a.type) {
a.type.editMode = _editMode;
a.type.updateEditMode();
}
});
}
}, {
key: "updateDisable",
value: function updateDisable(disable) {
this.annotations.forEach(function (a) {
a.disable = disable;
if (a.type) {
disable.forEach(function (d) {
if (a.type[d]) {
a.type[d].remove && a.type[d].remove();
a.type[d] = undefined;
}
});
}
});
}
}, {
key: "updateTextWrap",
value: function updateTextWrap(textWrap) {
this.annotations.forEach(function (a) {
if (a.type && a.type.updateTextWrap) {
a.type.updateTextWrap(textWrap);
}
});
}
}, {
key: "updateText",
value: function updateText() {
this.annotations.forEach(function (a) {
if (a.type && a.type.drawText) {
a.type.drawText();
}
});
}
}, {
key: "updateNotePadding",
value: function updateNotePadding(notePadding) {
this.annotations.forEach(function (a) {
if (a.type) {
a.type.notePadding = notePadding;
}
});
}
}, {
key: "json",
get: function get$$1() {
var _this2 = this;
return this.annotations.map(function (a) {
var json = a.json;
if (_this2.accessorsInverse && a.data) {
json.data = {};
Object.keys(_this2.accessorsInverse).forEach(function (k) {
json.data[k] = _this2.accessorsInverse[k]({ x: a.x, y: a.y });
//TODO make this feasible to map back to data for other types of subjects
});
}
return json;
});
}
}, {
key: "noteNodes",
get: function get$$1() {
return this.annotations.map(function (a) {
return _extends({}, a.type.getNoteBBoxOffset(), { positionX: a.x, positionY: a.y });
});
}
//TODO: come back and rethink if a.x and a.y are applicable in all situations
// get connectorNodes() {
// return this.annotations.map(a => ({ ...a.type.getConnectorBBox(), startX: a.x, startY: a.y}))
// }
// get subjectNodes() {
// return this.annotations.map(a => ({ ...a.type.getSubjectBBox(), startX: a.x, startY: a.y}))
// }
// get annotationNodes() {
// return this.annotations.map(a => ({ ...a.type.getAnnotationBBox(), startX: a.x, startY: a.y}))
// }
}]);
return AnnotationCollection;
}();
var pointHandle = function pointHandle(_ref) {
var _ref$cx = _ref.cx,
cx = _ref$cx === undefined ? 0 : _ref$cx,
_ref$cy = _ref.cy,
cy = _ref$cy === undefined ? 0 : _ref$cy;
return { move: { x: cx, y: cy } };
};
var circleHandles = function circleHandles(_ref2) {
var _ref2$cx = _ref2.cx,
cx = _ref2$cx === undefined ? 0 : _ref2$cx,
_ref2$cy = _ref2.cy,
cy = _ref2$cy === undefined ? 0 : _ref2$cy,
r1 = _ref2.r1,
r2 = _ref2.r2,
padding = _ref2.padding;
var h = { move: { x: cx, y: cy } };
if (r1 !== undefined) {
h.r1 = { x: cx + r1 / Math.sqrt(2), y: cy + r1 / Math.sqrt(2) };
}
if (r2 !== undefined) {
h.r2 = { x: cx + r2 / Math.sqrt(2), y: cy + r2 / Math.sqrt(2) };
}
if (padding !== undefined) {
h.padding = { x: cx + r1 + padding, y: cy };
}
return h;
};
//arc handles
var addHandles = function addHandles(_ref5) {
var group = _ref5.group,
handles = _ref5.handles,
_ref5$r = _ref5.r,
r = _ref5$r === undefined ? 10 : _ref5$r;
//give it a group and x,y to draw handles
//then give it instructions on what the handles change
var h = group.selectAll("circle.handle").data(handles);
h.enter().append("circle").attr("class", "handle").attr("fill", "grey").attr("fill-opacity", 0.1).attr("cursor", "move").attr("stroke-dasharray", 5).attr("stroke", "grey").call(drag().container(select("g.annotations").node()).on("start", function (d) {
return d.start && d.start(d);
}).on("drag", function (d) {
return d.drag && d.drag(d);
}).on("end", function (d) {
return d.end && d.end(d);
}));
group.selectAll("circle.handle").attr("cx", function (d) {
return d.x;
}).attr("cy", function (d) {
return d.y;
}).attr("r", function (d) {
return d.r || r;
}).attr("class", function (d) {
return "handle " + (d.className || "");
});
h.exit().remove();
};
var leftRightDynamic = function leftRightDynamic(align, y) {
if (align === "dynamic" || align === "left" || align === "right") {
if (y < 0) {
align = "top";
} else {
align = "bottom";
}
}
return align;
};
var topBottomDynamic = function topBottomDynamic(align, x) {
if (align === "dynamic" || align === "top" || align === "bottom") {
if (x < 0) {
align = "right";
} else {
align = "left";
}
}
return align;
};
var orientationTopBottom = ["topBottom", "top", "bottom"];
var orientationLeftRight = ["leftRight", "left", "right"];
var noteAlignment = (function (_ref) {
var _ref$padding = _ref.padding,
padding = _ref$padding === undefined ? 0 : _ref$padding,
_ref$bbox = _ref.bbox,
bbox = _ref$bbox === undefined ? { x: 0, y: 0, width: 0, height: 0 } : _ref$bbox,
align = _ref.align,
orientation = _ref.orientation,
_ref$offset = _ref.offset,
offset = _ref$offset === undefined ? { x: 0, y: 0 } : _ref$offset;
var x = -bbox.x;
var y = 0; //-bbox.y
if (orientationTopBottom.indexOf(orientation) !== -1) {
align = topBottomDynamic(align, offset.x);
if (offset.y < 0 && orientation === "topBottom" || orientation === "top") {
y -= bbox.height + padding;
} else {
y += padding;
}
if (align === "middle") {
x -= bbox.width / 2;
} else if (align === "right") {
x -= bbox.width;
}
} else if (orientationLeftRight.indexOf(orientation) !== -1) {
align = leftRightDynamic(align, offset.y);
if (offset.x < 0 && orientation === "leftRight" || orientation === "left") {
x -= bbox.width + padding;
} else {
x += padding;
}
if (align === "middle") {
y -= bbox.height / 2;
} else if (align === "top") {
y -= bbox.height;
}
}
return { x: x, y: y };
});
var lineBuilder = function lineBuilder(_ref) {
var data = _ref.data,
_ref$curve = _ref.curve,
curve = _ref$curve === undefined ? curveLinear : _ref$curve,
canvasContext = _ref.canvasContext,
className = _ref.className,
classID = _ref.classID;
var lineGen = line().curve(curve);
var builder = {
type: 'path',
className: className,
classID: classID,
data: data
};
if (canvasContext) {
lineGen.context(canvasContext);
builder.pathMethods = lineGen;
} else {
builder.attrs = {
d: lineGen(data)
};
}
return builder;
};
var arcBuilder = function arcBuilder(_ref2) {
var data = _ref2.data,
canvasContext = _ref2.canvasContext,
className = _ref2.className,
classID = _ref2.classID;
var builder = {
type: 'path',
className: className,
classID: classID,
data: data
};
var arcShape = arc().innerRadius(data.innerRadius || 0).outerRadius(data.outerRadius || data.radius || 2).startAngle(data.startAngle || 0).endAngle(data.endAngle || 2 * Math.PI);
if (canvasContext) {
arcShape.context(canvasContext);
builder.pathMethods = lineGen;
} else {
builder.attrs = {
d: arcShape()
};
}
return builder;
};
var noteVertical = (function (_ref) {
var align = _ref.align,
_ref$x = _ref.x,
x = _ref$x === undefined ? 0 : _ref$x,
_ref$y = _ref.y,
y = _ref$y === undefined ? 0 : _ref$y,
bbox = _ref.bbox,
offset = _ref.offset;
align = leftRightDynamic(align, offset.y);
if (align === "top") {
y -= bbox.height;
} else if (align === "middle") {
y -= bbox.height / 2;
}
var data = [[x, y], [x, y + bbox.height]];
return { components: [lineBuilder({ data: data, className: "note-line" })] };
});
var noteHorizontal = (function (_ref) {
var align = _ref.align,
_ref$x = _ref.x,
x = _ref$x === undefined ? 0 : _ref$x,
_ref$y = _ref.y,
y = _ref$y === undefined ? 0 : _ref$y,
offset = _ref.offset,
bbox = _ref.bbox;
align = topBottomDynamic(align, offset.x);
if (align === "right") {
x -= bbox.width;
} else if (align === "middle") {
x -= bbox.width / 2;
}
var data = [[x, y], [x + bbox.width, y]];
return { components: [lineBuilder({ data: data, className: "note-line" })] };
});
var lineSetup = function lineSetup(_ref) {
var type = _ref.type,
subjectType = _ref.subjectType;
var annotation = type.annotation;
var offset = annotation.position;
var x1 = annotation.x - offset.x,
x2 = x1 + annotation.dx,
y1 = annotation.y - offset.y,
y2 = y1 + annotation.dy;
var subjectData = annotation.subject;
if (subjectType === "circle" && (subjectData.outerRadius || subjectData.radius)) {
var h = Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
var angle = Math.asin(-y2 / h);
var r = subjectData.outerRadius || subjectData.radius + (subjectData.radiusPadding || 0);
x1 = Math.abs(Math.cos(angle) * r) * (x2 < 0 ? -1 : 1);
y1 = Math.abs(Math.sin(angle) * r) * (y2 < 0 ? -1 : 1);
}
if (subjectType === "rect") {
var width = subjectData.width,
height = subjectData.height;
if (width > 0 && annotation.dx > 0 || width < 0 && annotation.dx < 0) {
if (Math.abs(width) > Math.abs(annotation.dx)) x1 = width / 2;else x1 = width;
}
if (height > 0 && annotation.dy > 0 || height < 0 && annotation.dy < 0) {
if (Math.abs(height) > Math.abs(annotation.dy)) y1 = height / 2;else y1 = height;
}
if (x1 === width / 2 && y1 === height / 2) {
x1 = x2;y1 = y2;
}
}
return [[x1, y1], [x2, y2]];
};
var connectorLine = (function (connectorData) {
var data = lineSetup(connectorData);
return { components: [lineBuilder({ data: data, className: "connector" })] };
});
var connectorElbow = (function (_ref) {
var type = _ref.type,
subjectType = _ref.subjectType;
var annotation = type.annotation;
var offset = annotation.position;
var x1 = annotation.x - offset.x,
x2 = x1 + annotation.dx,
y1 = annotation.y - offset.y,
y2 = y1 + annotation.dy;
var subjectData = annotation.subject;
if (subjectType === "rect") {
var width = subjectData.width,
height = subjectData.height;
if (width > 0 && annotation.dx > 0 || width < 0 && annotation.dx < 0) {
if (Math.abs(width) > Math.abs(annotation.dx)) x1 = width / 2;else x1 = width;
}
if (height > 0 && annotation.dy > 0 || height < 0 && annotation.dy < 0) {
if (Math.abs(height) > Math.abs(annotation.dy)) y1 = height / 2;else y1 = height;
}
if (x1 === width / 2 && y1 === height / 2) {
x1 = x2;y1 = y2;
}
}
var data = [[x1, y1], [x2, y2]];
var diffY = y2 - y1;
var diffX = x2 - x1;
var xe = x2;
var ye = y2;
var opposite = y2 < y1 && x2 > x1 || x2 < x1 && y2 > y1 ? -1 : 1;
if (Math.abs(diffX) < Math.abs(diffY)) {
xe = x2;
ye = y1 + diffX * opposite;
} else {
ye = y2;
xe = x1 + diffY * opposite;
}
if (subjectType === "circle" && (subjectData.outerRadius || subjectData.radius)) {
var r = (subjectData.outerRadius || subjectData.radius) + (subjectData.radiusPadding || 0);
var length = r / Math.sqrt(2);
if (Math.abs(diffX) > length && Math.abs(diffY) > length) {
x1 = length * (x2 < 0 ? -1 : 1);
y1 = length * (y2 < 0 ? -1 : 1);
data = [[x1, y1], [xe, ye], [x2, y2]];
} else if (Math.abs(diffX) > Math.abs(diffY)) {
var angle = Math.asin(-y2 / r);
x1 = Math.abs(Math.cos(angle) * r) * (x2 < 0 ? -1 : 1);
data = [[x1, y2], [x2, y2]];
} else {
var _angle = Math.acos(x2 / r);
y1 = Math.abs(Math.sin(_angle) * r) * (y2 < 0 ? -1 : 1);
data = [[x2, y1], [x2, y2]];
}
} else {
data = [[x1, y1], [xe, ye], [x2, y2]];
}
return { components: [lineBuilder({ data: data, className: "connector" })] };
});
var connectorCurve = (function (_ref) {
var type = _ref.type,
connectorData = _ref.connectorData,
subjectType = _ref.subjectType;
if (!connectorData) {
connectorData = {};
}
if (!connectorData.points || typeof connectorData.points === "number") {
connectorData.points = createPoints(type.annotation.offset, connectorData.points);
}
if (!connectorData.curve) {
connectorData.curve = curveCatmullRom;
}
var handles = [];
if (type.editMode) {
var cHandles = connectorData.points.map(function (c, i) {
return _extends({}, pointHandle({ cx: c[0], cy: c[1] }), { index: i });
});
var updatePoint = function updatePoint(index) {
connectorData.points[index][0] += event.dx;
connectorData.points[index][1] += event.dy;
type.redrawConnector();
};
handles = type.mapHandles(cHandles.map(function (h) {
return _extends({}, h.move, { drag: updatePoint.bind(type, h.index) });
}));
}
var data = lineSetup({ type: type, subjectType: subjectType });
data = [data[0]].concat(toConsumableArray(connectorData.points), [data[1]]);
var components = [lineBuilder({ data: data, curve: connectorData.curve, className: "connector" })];
return { components: components, handles: handles };
});
var createPoints = function createPoints(offset) {
var anchors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 2;
var diff = { x: offset.x / (anchors + 1), y: offset.y / (anchors + 1) };
var p = [];
var i = 1;
for (; i <= anchors; i++) {
p.push([diff.x * i + i % 2 * 20, diff.y * i - i % 2 * 20]);
}
return p;
};
var connectorArrow = (function (_ref) {
var annotation = _ref.annotation,
start = _ref.start,
end = _ref.end,
_ref$scale = _ref.scale,
scale = _ref$scale === undefined ? 1 : _ref$scale;
var offset = annotation.position;
if (!start) {
start = [annotation.dx, annotation.dy];
} else {
start = [-end[0] + start[0], -end[1] + start[1]];
}
if (!end) {
end = [annotation.x - offset.x, annotation.y - offset.y];
}
var x1 = end[0],
y1 = end[1];
var dx = start[0];
var dy = start[1];
var size = 10 * scale;
var angleOffset = 16 / 180 * Math.PI;
var angle = Math.atan(dy / dx);
if (dx < 0) {
angle += Math.PI;
}
var data = [[x1, y1], [Math.cos(angle + angleOffset) * size + x1, Math.sin(angle + angleOffset) * size + y1], [Math.cos(angle - angleOffset) * size + x1, Math.sin(angle - angleOffset) * size + y1], [x1, y1]];
//TODO add in reverse
// if (canvasContext.arrowReverse){
// data = [[x1, y1],
// [Math.cos(angle + angleOffset)*size, Math.sin(angle + angleOffset)*size],
// [Math.cos(angle - angleOffset)*size, Math.sin(angle - angleOffset)*size],
// [x1, y1]
// ]
// } else {
// data = [[x1, y1],
// [Math.cos(angle + angleOffset)*size, Math.sin(angle + angleOffset)*size],
// [Math.cos(angle - angleOffset)*size, Math.sin(angle - angleOffset)*size],
// [x1, y1]
// ]
// }
return {
components: [lineBuilder({
data: data,
className: "connector-end connector-arrow",
classID: "connector-end"
})]
};
});
var connectorDot = (function (_ref) {
var line$$1 = _ref.line,
_ref$scale = _ref.scale,
scale = _ref$scale === undefined ? 1 : _ref$scale;
var dot = arcBuilder({
className: "connector-end connector-dot",
classID: "connector-end",
data: { radius: 3 * Math.sqrt(scale) }
});
dot.attrs.transform = "translate(" + line$$1.data[0][0] + ", " + line$$1.data[0][1] + ")";
return { components: [dot] };
});
var subjectCircle = (function (_ref) {
var subjectData = _ref.subjectData,
type = _ref.type;
if (!subjectData.radius && !subjectData.outerRadius) {
subjectData.radius = 20;
}
var handles = [];
var c = arcBuilder({ data: subjectData, className: "subject" });
if (type.editMode) {
var h = circleHandles({
r1: c.data.outerRadius || c.data.radius,
r2: c.data.innerRadius,
padding: subjectData.radiusPadding
});
var updateRadius = function updateRadius(attr) {
var r = subjectData[attr] + event.dx * Math.sqrt(2);
subjectData[attr] = r;
type.redrawSubject();
type.redrawConnector();
};
var cHandles = [_extends({}, h.r1, {
drag: updateRadius.bind(type, subjectData.outerRadius !== undefined ? "outerRadius" : "radius")
})];
if (subjectData.innerRadius) {
cHandles.push(_extends({}, h.r2, { drag: updateRadius.bind(type, "innerRadius") }));
}
handles = type.mapHandles(cHandles);
}
c.attrs["fill-opacity"] = 0;
return { components: [c], handles: handles };
});
var subjectRect = (function (_ref) {
var subjectData = _ref.subjectData,
type = _ref.type;
if (!subjectData.width) {
subjectData.width = 100;
}
if (!subjectData.height) {
subjectData.height = 100;
}
var handles = [];
var width = subjectData.width,
height = subjectData.height;
var data = [[0, 0], [width, 0], [width, height], [0, height], [0, 0]];
var rect = lineBuilder({ data: data, className: "subject" });
if (type.editMode) {
var updateWidth = function updateWidth() {
subjectData.width = event.x;
type.redrawSubject();
type.redrawConnector();
};
var updateHeight = function updateHeight() {
subjectData.height = event.y;
type.redrawSubject();
type.redrawConnector();
};
var rHandles = [{ x: width, y: height / 2, drag: updateWidth.bind(type) }, { x: width / 2, y: height, drag: updateHeight.bind(type) }];
handles = type.mapHandles(rHandles);
}
rect.attrs["fill-opacity"] = 0.1;
return { components: [rect], handles: handles };
});
var subjectThreshold = (function (_ref) {
var subjectData = _ref.subjectData,
type = _ref.type;
var offset = type.annotation.position;
var x1 = (subjectData.x1 !== undefined ? subjectData.x1 : offset.x) - offset.x,
x2 = (subjectData.x2 !== undefined ? subjectData.x2 : offset.x) - offset.x,
y1 = (subjectData.y1 !== undefined ? subjectData.y1 : offset.y) - offset.y,
y2 = (subjectData.y2 !== undefined ? subjectData.y2 : offset.y) - offset.y;
var data = [[x1, y1], [x2, y2]];
return { components: [lineBuilder({ data: data, className: 'subject' })] };
});
var subjectBadge = (function (_ref) {
var _ref$subjectData = _ref.subjectData,
subjectData = _ref$subjectData === undefined ? {} : _ref$subjectData,
_ref$type = _ref.type,
type = _ref$type === undefined ? {} : _ref$type;
var annotation = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var typeSettings = type.typeSettings && type.typeSettings.subject;
if (!subjectData.radius) {
if (typeSettings && typeSettings.radius) {
subjectData.radius = typeSettings.radius;
} else {
subjectData.radius = 14;
}
}
if (!subjectData.x) {
if (typeSettings && typeSettings.x) {
subjectData.x = typeSettings.x;
}
}
if (!subjectData.y) {
if (typeSettings && typeSettings.y) {
subjectData.y = typeSettings.y;
}
}
var handles = [];
var components = [];
var radius = subjectData.radius;
var innerRadius = radius * 0.7;
var x = 0;
var y = 0;
var notCornerOffset = Math.sqrt(2) * radius;
var placement = {
xleftcorner: -radius,
xrightcorner: radius,
ytopcorner: -radius,
ybottomcorner: radius,
xleft: -notCornerOffset,
xright: notCornerOffset,
ytop: -notCornerOffset,
ybottom: notCornerOffset
};
if (subjectData.x && !subjectData.y) {
x = placement["x" + subjectData.x];
} else if (subjectData.y && !subjectData.x) {
y = placement["y" + subjectData.y];
} else if (subjectData.x && subjectData.y) {
x = placement["x" + subjectData.x + "corner"];
y = placement["y" + subjectData.y + "corner"];
}
var transform = "translate(" + x + ", " + y + ")";
var circlebg = arcBuilder({ className: "subject", data: { radius: radius } });
circlebg.attrs.transform = transform;
circlebg.attrs.fill = annotation.color;
circlebg.attrs["stroke-linecap"] = "round";
circlebg.attrs["stroke-width"] = "3px";
var circle = arcBuilder({
className: "subject-ring",
data: { outerRadius: radius, innerRadius: innerRadius }
});
circle.attrs.transform = transform;
// circle.attrs.fill = annotation.color
circle.attrs["stroke-width"] = "3px";
circle.attrs.fill = "white";
var pointer = void 0;
if (x && y || !x && !y) {
pointer = lineBuilder({
className: "subject-pointer",
data: [[0, 0], [x || 0, 0], [0, y || 0], [0, 0]]
});
} else if (x || y) {
var notCornerPointerXY = function notCornerPointerXY(v) {
var sign = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
return v && v / Math.sqrt(2) / Math.sqrt(2) || sign * radius / Math.sqrt(2);
};
pointer = lineBuilder({
className: "subject-pointer",
data: [[0, 0], [notCornerPointerXY(x), notCornerPointerXY(y)], [notCornerPointerXY(x, -1), notCornerPointerXY(y, -1)], [0, 0]]
});
}
if (pointer) {
pointer.attrs.fill = annotation.color;
pointer.attrs["stroke-linecap"] = "round";
pointer.attrs["stroke-width"] = "3px";
components.push(pointer);
}
if (type.editMode) {
var dragBadge = function dragBadge() {
subjectData.x = event.x < -radius * 2 ? "left" : event.x > radius * 2 ? "right" : undefined;
subjectData.y = event.y < -radius * 2 ? "top" : event.y > radius * 2 ? "bottom" : undefined;
type.redrawSubject();
};
var bHandles = { x: x * 2, y: y * 2, drag: dragBadge.bind(type) };
if (!bHandles.x && !bHandles.y) {
bHandles.y = -radius;
}
handles = type.mapHandles([bHandles]);
}
var text = void 0;
if (subjectData.text) {
text = {
type: "text",
className: "badge-text",
attrs: {
fill: "white",
stroke: "none",
"font-size": ".7em",
text: subjectData.text,
"text-anchor": "middle",
dy: ".25em",
x: x,
y: y
}
};
}
components.push(circlebg);
components.push(circle);
components.push(text);
return { components: components, handles: handles };
});
//Note options
//Connector options
//Subject options
var Type = function () {
function Type(_ref) {
var a = _ref.a,
annotation = _ref.annotation,
editMode = _ref.editMode,
dispatcher = _ref.dispatcher,
notePadding = _ref.notePadding,
accessors = _ref.accessors;
classCallCheck(this, Type);
this.a = a;
this.note = annotation.disable.indexOf("note") === -1 && a.select("g.annotation-note");
this.noteContent = this.note && a.select("g.annotation-note-content");
this.connector = annotation.disable.indexOf("connector") === -1 && a.select("g.annotation-connector");
this.subject = annotation.disable.indexOf("subject") === -1 && a.select("g.annotation-subject");
this.dispatcher = dispatcher;
if (dispatcher) {
var handler = addHandlers.bind(null, dispatcher, annotation);
handler({ component: this.note, name: "note" });
handler({ component: this.connector, name: "connector" });
handler({ component: this.subject, name: "subject" });
}
this.annotation = annotation;
this.editMode = annotation.editMode || editMode;
this.notePadding = notePadding !== undefined ? notePadding : 3;
this.offsetCornerX = 0;
this.offsetCornerY = 0;
if (accessors && annotation.data) {
this.init(accessors);
}
}
createClass(Type, [{
key: "init",
value: function init(accessors) {
if (!this.annotation.x) {
this.mapX(accessors);
}
if (!this.annotation.y) {
this.mapY(accessors);
}
}
}, {
key: "mapY",
value: function mapY(accessors) {
if (accessors.y) {
this.annotation.y = accessors.y(this.annotation.data);
}
}
}, {
key: "mapX",
value: function mapX(accessors) {
if (accessors.x) {
this.annotation.x = accessors.x(this.annotation.data);
}
}
}, {
key: "updateEditMode",
value: function updateEditMode() {
this.a.selectAll("circle.handle").remove();
}
}, {
key: "drawOnSVG",
value: function drawOnSVG(component, builders) {
var _this = this;
if (!Array.isArray(builders)) {
builders = [builders];
}
builders.filter(function (b) {
return b;
}).forEach(function (_ref2) {
var type = _ref2.type,
className = _ref2.className,
attrs = _ref2.attrs,
handles = _ref2.handles,
classID = _ref2.classID;
if (type === "handle") {
addHandles({ group: component, r: attrs && attrs.r, handles: handles });
} else {
newWithClass(component, [_this.annotation], type, className, classID);
var el = component.select(type + "." + (classID || className));
var addAttrs = Object.keys(attrs);
var removeAttrs = [];
var currentAttrs = el.node().attributes;
for (var i = currentAttrs.length - 1; i >= 0; i--) {
var name = currentAttrs[i].name;
if (addAttrs.indexOf(name) === -1 && name !== "class") removeAttrs.push(name);
}
addAttrs.forEach(function (attr) {
if (attr === "text") {
el.text(attrs[attr]);
} else {
el.attr(attr, attrs[attr]);
}
});
removeAttrs.forEach(function (attr) {
return el.attr(attr, null);
});
}
});
}
//TODO: how to extend this to a drawOnCanvas mode?
}, {
key: "getNoteBBox",
value: function getNoteBBox() {
return bboxWithoutHandles(this.note, ".annotation-note-content text");
}
}, {
key: "getNoteBBoxOffset",
value: function getNoteBBoxOffset() {
var bbox = bboxWithoutHandles(this.note, ".annotation-note-content");
var transform = this.noteContent.attr("transform").split(/\(|\,|\)/g);
bbox.offsetCornerX = parseFloat(transform[1]) + this.annotation.dx;
bbox.offsetCornerY = parseFloat(transform[2]) + this.annotation.dy;
bbox.offsetX = this.annotation.dx;
bbox.offsetY = this.annotation.dy;
return bbox;
}
}, {
key: "drawSubject",
value: function drawSubject() {
var _this2 = this;
var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var subjectData = this.annotation.subject;
var type = context.type;
var subjectParams = { type: this, subjectData: subjectData };
var subject = {};
if (type === "circle") subject = subjectCircle(subjectParams);else if (type === "rect") subject = subjectRect(subjectParams);else if (type === "threshold") subject = subjectThreshold(subjectParams);else if (type === "badge") subject = subjectBadge(subjectParams, this.annotation);
var _subject = subject,
_subject$components = _subject.components,
components = _subject$components === undefined ? [] : _subject$components,
_subject$handles = _subject.handles,
handles = _subject$handles === undefined ? [] : _subject$handles;
components.forEach(function (c) {
if (c && c.attrs && !c.attrs.stroke) {
c.attrs.stroke = _this2.annotation.color;
}
});
if (this.editMode) {
handles = handles.concat(this.mapHandles([{ drag: this.dragSubject.bind(this) }]));
components.push({ type: "handle", handles: handles });
}
return components;
}
}, {
key: "drawConnector",
value: function drawConnector() {
var _this3 = this;
var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var connectorData = this.annotation.connector;
var type = connectorData.type || context.type;
var connectorParams = { type: this, connectorData: connectorData };
connectorParams.subjectType = this.typeSettings && this.typeSettings.subject && this.typeSettings.subject.type;
var connector = {};
if (type === "curve") connector = connectorCurve(connectorParams);else if (type === "elbow") connector = connectorElbow(connectorParams);else connector = connectorLine(connectorParams);
var _connector = connector,
_connector$components = _connector.components,
components = _connector$components === undefined ? [] : _connector$components,
_connector$handles = _connector.handles,
handles = _connector$handles === undefined ? [] : _connector$handles;
var line$$1 = components[0];
//TODO: genericize this into fill t/f stroke t/f
if (line$$1) {
line$$1.attrs.stroke = this.annotation.color;
line$$1.attrs.fill = "none";
}
var endType = connectorData.end || context.end;
var end = {};
if (endType === "arrow") {
var s = line$$1.data[1];
var e = line$$1.data[0];
var distance = Math.sqrt(Math.pow(s[0] - e[0], 2) + Math.pow(s[1] - e[1], 2));
if (distance < 5 && line$$1.data[2]) {
s = line$$1.data[2];
}
end = connectorArrow({
annotation: this.annotation,
start: s,
end: e,
scale: connectorData.endScale
});
} else if (endType === "dot") {
end = connectorDot({ line: line$$1, scale: connectorData.endScale });
} else if (!endType || endType === "none") {
this.connector && this.connector.select(".connector-end").remove();
}
if (end.components) {
end.components.forEach(function (c) {
c.attrs.fill = _this3.annotation.color;
c.attrs.stroke = _this3.annotation.color;
});
components = components.concat(end.components);
}
if (this.editMode) {
if (handles.length !== 0) components.push({ type: "handle", handles: handles });
}
return components;
}
}, {
key: "drawNote",
value: function drawNote() {
var _this4 = this;
var context = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var noteData = this.annotation.note;
var align = noteData.align || context.align || "dynamic";
var noteParams = {
bbox: context.bbox,
align: align,
offset: this.annotation.offset
};
var lineType = noteData.lineType || context.lineType;
var note = {};
if (lineType === "vertical") note = noteVertical(noteParams);else if (lineType === "horizontal") note = noteHorizontal(noteParams);
var _note = note,
_note$components = _note.components,
components = _note$components === undefined ? [] : _note$components,
_note$handles = _note.handles,
handles = _note$handles === undefined ? [] : _note$handles;
components.forEach(function (c) {
c.attrs.stroke = _this4.annotation.color;
});
if (this.editMode) {
handles = this.mapHandles([{ x: 0, y: 0, drag: this.dragNote.bind(this) }]);
components.push({ type: "handle", handles: handles });
var dragging = this.dragNote.bind(this),
start = this.dragstarted.bind(this),
end = this.dragended.bind(this);
this.note.call(drag().container(select("g.annotations").node()).on("start", function (d) {
return start(d);
}).on("drag", function (d) {
return dragging(d);
}).on("end", function (d) {
return end(d);
}));
} else {
this.note.on("mousedown.drag", null);
}
return components;
}
}, {
key: "drawNoteContent",
value: function drawNoteContent(context) {
var noteData = this.annotation.note;
var padding = noteData.padding !== undefined ? noteData.padding : this.notePadding;
var orientation = noteData.orientation || context.orientation || "topBottom";
var lineType = noteData.lineType || context.lineType;
var align = noteData.align || context.align || "dynamic";
if (lineType === "vertical") orientation = "leftRight";else if (lineType === "horizontal") orientation = "topBottom";
var noteParams = {
padding: padding,
bbox: context.bbox,
offset: this.annotation.offset,
orientation: orientation,
align: align
};
var _noteAlignment = noteAlignment(noteParams),
x = _noteAlignment.x,
y = _noteAlignment.y;
this.offsetCornerX = x + this.annotation.dx;
this.offsetCornerY = y + this.annotation.dy;
this.note && this.noteContent.attr("transform", "translate(" + x + ", " + y + ")");
return [];
}
}, {
key: "drawOnScreen",
value: function drawOnScreen(component, drawFunction) {
return this.drawOnSVG(component, drawFunction);
}
}, {
key: "redrawSubject",
value: function redrawSubject() {
this.subject && this.drawOnScreen(this.subject, this.drawSubject());
}
}, {
key: "redrawConnector",
value: function redrawConnector() {
this.connector && this.drawOnScreen(this.connector, this.drawConnector());
}
}, {
key: "redrawNote",
value: function redrawNote() {
var bbox = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.getNoteBBox();
this.noteContent && this.drawOnScreen(this.noteContent, this.drawNoteContent({ bbox: bbox }));
this.note && this.drawOnScreen(this.note, this.drawNote({ bbox: bbox }));
}
}, {
key: "setPosition",
value: function setPosition() {
var position = this.annotation.position;
this.a.attr("transform", "translate(" + position.x + ", " + position.y + ")");
}
}, {
key: "clearComponents",
value: function clearComponents() {
this.subject && this.subject.select("*").remove();
this.connector && this.connector.select("*").remove();
// this.note && this.note.select("*").remove()
}
}, {
key: "setOffset",
value: function setOffset() {
if (this.note) {
var offset = this.annotation.offset;
this.note.attr("transform", "translate(" + offset.x + ", " + offset.y + ")");
}
}
}, {
key: "setPositionWithAccessors",
value: function setPositionWithAccessors(accessors) {
if (accessors && this.annotation.data) {
this.mapX(accessors);
this.mapY(accessors);
}
this.setPosition();
}
}, {
key: "setClassName",
value: function setClassName() {
this.a.attr("class", "annotation " + (this.className && this.className()) + " " + (this.editMode ? "editable" : "") + " " + (this.annotation.className || ""));
}
}, {
key: "draw",
value: function draw() {
this.setClassName();
this.setPosition();
this.setOffset();
this.redrawSubject();
this.redrawConnector();
this.redrawNote();
}
}, {
key: "dragstarted",
value: function dragstarted() {
event.sourceEvent.stopPropagation();
this.dispatcher && this.dispatcher.call("dragstart", this.a, this.annotation);
this.a.classed("dragging", true);
this.a.selectAll("circle.handle").style("pointer-events", "none");
}
}, {
key: "dragended",
value: function dragended() {
this.dispatcher && this.dispatcher.call("dragend", this.a, this.annotation);
this.a.classed("dragging", false);
this.a.selectAll("circle.handle").style("pointer-events", "all");
}
}, {
key: "dragSubject",
value: function dragSubject() {
var position = this.annotation.position;
position.x += event.dx;
position.y += event.dy;
this.annotation.position = position;
}
}, {
key: "dragNote",
value: function dragNote() {
var offset = this.annotation.offset;
offset.x += event.dx;
offset.y += event.dy;
this.annotation.offset = offset;
}
}, {
key: "mapHandles",
value: function mapHandles(handles) {
var _this5 = this;
return handles.map(function (h) {
return _extends({}, h, {
start: _this5.dragstarted.bind(_this5),
end: _this5.dragended.bind(_this5)
});
});
}
}]);
return Type;
}();
var customType = function customType(initialType, typeSettings, _init) {
return function (_initialType) {
inherits(customType, _initialType);
function customType(settings) {
classCallCheck(this, customType);
var _this6 = possibleConstructorReturn(this, (customType.__proto__ || Object.getPrototypeOf(customType)).call(this, settings));
_this6.typeSettings = typeSettings;
if (typeSettings.disable) {
typeSettings.disable.forEach(function (d) {
_this6[d] && _this6[d].remove();
_this6[d] = undefined;
if (d === "note") {
_this6.noteContent = undefined;
}
});
}
return _this6;
}
createClass(customType, [{
key: "className",
value: function className() {
return "" + (typeSettings.className || get(customType.prototype.__proto__ || Object.getPrototypeOf(customType.prototype), "className", this) && get(customType.prototype.__proto__ || Object.getPrototypeOf(customType.prototype), "className", this).call(this) || "");
}
}, {
key: "drawSubject",
value: function drawSubject(context) {
this.typeSettings.subject = _extends({}, typeSettings.subject, this.typeSettings.subject);
return get(customType.prototype.__proto__ || Object.getPrototypeOf(customType.prototype), "drawSubject", this).call(this, _extends({}, context, this.typeSettings.subject));
}
}, {
key: "drawConnector",
value: function drawConnector(context) {
this.typeSettings.connector = _extends({}, typeSettings.connector, this.typeSettings.connector);
return get(customType.prototype.__proto__ || Object.getPrototypeOf(customType.prototype), "drawConnector", this).call(this, _extends({}, context, typeSettings.connector, this.typeSettings.connector));
}
}, {
key: "drawNote",
value: function drawNote(context) {
this.typeSettings.note = _extends({}, typeSettings.note, this.typeSettings.note);
return get(customType.prototype.__proto__ || Object.getPrototypeOf(customType.prototype), "drawNote", this).call(this, _extends({}, context, typeSettings.note, this.typeSettings.note));
}
}, {
key: "drawNoteContent",
value: function drawNoteContent(context) {
return get(customType.prototype.__proto__ || Object.getPrototypeOf(customType.prototype), "drawNoteContent", this).call(this, _extends({}, context, typeSettings.note, this.typeSettings.note));
}
}], [{
key: "init",
value: function init(annotation, accessors) {
get(customType.__proto__ || Object.getPrototypeOf(customType), "init", this).call(this, annotation, accessors);
if (_init) {
annotation = _init(annotation, accessors);
}
return annotation;
}
}]);
return customType;
}(initialType);
};
var d3NoteText = function (_Type) {
inherits(d3NoteText, _Type);
function d3NoteText(params) {
classCallCheck(this, d3NoteText);
var _this7 = possibleConstructorReturn(this, (d3NoteText.__proto__ || Object.getPrototypeOf(d3NoteText)).call(this, params));
_this7.textWrap = params.textWrap || 120;
_this7.drawText();
return _this7;
}
createClass(d3NoteText, [{
key: "updateTextWrap",
value: function updateTextWrap(textWrap) {
this.textWrap = textWrap;
this.drawText();
}
//TODO: add update text functionality
}, {
key: "drawText",
value: function drawText() {
if (this.note) {
newWithClass(this.note, [this.annotation], "g", "annotation-note-content");
var noteContent = this.note.select("g.annotation-note-content");
newWithClass(noteContent, [this.annotation], "rect", "annotation-note-bg");
newWithClass(noteContent, [this.annotation], "text", "annotation-note-label");
newWithClass(noteContent, [this.annotation], "text", "annotation-note-title");
var titleBBox = { height: 0 };
var label = this.a.select("text.annotation-note-label");
var wrapLength = this.annotation.note && this.annotation.note.wrap || this.typeSettings && this.typeSettings.note && this.typeSettings.note.wrap || this.textWrap;
var wrap