🎉 JointJS has new documentation! 🥳
This is the API reference to the open source JointJS core library. If you're looking for the JointJS+ diagramming toolkit documentation, you can find that here.
JointJS library exports three global variables: joint
, V
and g
.
The joint
namespace contains all the objects that you will use to build your diagrams.
Additionally, joint.version
property tells you which version of JointJS you're using.
The V global is lightweight SVG library that we call Vectorizer
. This tiny library makes manipulation with SVG documents much easier. JointJS uses this library internally. Normally, you don't have to get in touch with this library at all but for advanced uses, it can be handy.
The g global is another lightweight library used internally by JointJS that provides many useful geometry operations. Again, you might not get in touch with this library but when you do have the need to perform geometric operations in your applications, you'll certainly find it helpful.
An anchor of a link is a point in the reference element that this link wants to reach as its endpoint. (In reality, the reference element is probably in the way - then, it is the job of the connection point function to determine the actual location of the route endpoint with the obstructing reference element taken into account.) Anchors are set via an anchor
property provided within link end definitions (i.e. the objects provided to link.source()
and link.target()
functions). (If the reference object is a Link, JointJS looks at linkAnchor
property instead.)
There are many built-in anchor functions in JointJS:
'center'
- default anchor at center of view bbox'modelCenter'
- anchor at center of model bbox'perpendicular'
- anchor that ensures an orthogonal route to the other endpoint'midSide'
- anchor in the middle of the side of view bbox closest to the other endpoint'bottom'
- anchor in the middle of the bottom side of view bbox'left'
- anchor in the middle of the left side of view bbox'right'
- anchor in the middle of the right side of view bbox'top'
- anchor in the middle of the top side of view bbox'bottomLeft'
- anchor at the bottom-left corner of view bbox'bottomRight'
- anchor at the bottom-right corner of view bbox'topLeft'
- anchor at the top-left corner of view bbox'topRight'
- anchor at the top-right corner of view bboxExample:
link.source(model, {
anchor: {
name: 'midSide',
args: {
rotate: true,
padding: 20
}
}
});
The default anchor function is 'center'
; this can be changed with the defaultAnchor
paper option. Example:
paper.options.defaultAnchor = {
name: 'midSide',
args: {
rotate: true,
padding: 20
}
};
JointJS also contains mechanisms to define one's own custom anchor functions.
The 'bottom'
anchor function places the anchor of the link in the middle of the bottom side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'bottom',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'bottomLeft'
anchor function places the anchor of the link at the bottom-left corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'bottomLeft',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'bottomRight'
anchor function places the anchor of the link at the bottom-left corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'bottomRight',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'center'
anchor function is the default anchor function. It places the anchor of the link at center of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'center',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
New anchor functions can be defined in the joint.anchors
namespace (e.g. joint.anchors.myAnchor
) or passed directly as a function to the anchor
property of link source/target (or to the defaultAnchor
option of a paper).
In either case, the anchor function must return the anchor as a Point. The function is expected to have the form function(endView, endMagnet, anchorReference, args)
:
endView | dia.ElementView | The ElementView to which we are connecting. The Element model can be accessed as endView.model ; this may be useful for writing conditional logic based on element attributes. |
---|---|---|
endMagnet | SVGElement | The SVGElement in our page that contains the magnet (element/subelement/port) to which we are connecting. |
anchorReference | g.Point | A reference to another component of the link path that may be necessary to find this anchor point. If we are calling this method for a source anchor, it is the first vertex, or if there are no vertices the target anchor. If we are calling this method for a target anchor, it is the last vertex, or if there are no vertices the source anchor... |
SVGElement | ...if the anchor in question does not exist (yet), it is that link end's magnet. (The built-in methods usually use this element's center point as reference.) | |
args | object | An object with additional optional arguments passed to the anchor method by the user when it was called (the args property). |
The 'left'
anchor function places the anchor of the link in the middle of the left side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'left',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'midSide'
anchor function places the anchor of the link in the middle of the side of view bbox closest to the other endpoint. It accepts two arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
padding | number | Offset the anchor by padding away from view bbox. Default is 0 . |
Example:
link.source(model, {
anchor: {
name: 'midSide',
args: {
rotate: true,
padding: 20
}
}
});
The 'modelCenter'
anchor function places the anchor of the link at center of the model bbox.
Example:
link.source(model, {
anchor: {
name: 'modelCenter'
}
});
The 'perpendicular'
anchor function tries to place the anchor of the link inside the view bbox so that the link is made orthogonal. The anchor is placed along two line segments inside the view bbox (between the centers of the top and bottom side and between the centers of the left and right sides). If it is not possible to place the anchor so that the link would be orthogonal, the anchor is placed at the center of the view bbox instead. The function accepts one argument, which can be passed within the anchor.args
property:
padding | number | Limit the area inside the view bbox available for placing the anchor by padding . Default is 0 . |
---|
Example:
link.source(model, {
anchor: {
name: 'perpendicular',
args: {
padding: 10
}
}
});
When the link has no vertices, the other end cell's center is used as a reference point. By default, this means that a link using the 'perpendicular'
anchor slides
alongside the source element's edge while pointing to target element's center. To invert this behavior, and have the anchor slide
alongside the target element's edge while pointing to source element's center, pass a priority
option to the target function:
link.target(model, {
priority: true,
anchor: {
name: 'perpendicular',
args: {
padding: 10
}
}
});
The 'right'
anchor function places the anchor of the link in the middle of the right side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'right',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'top'
anchor function places the anchor of the link in the middle of the top side of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'top',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'topLeft'
anchor function places the anchor of the link at the top-left corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'topLeft',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
The 'topRight'
anchor function places the anchor of the link at the top-right corner of the view bbox. It accepts three arguments, which can be passed within the anchor.args
property:
rotate | boolean | Should the anchor bbox rotate with the end view? Default is false , meaning that the unrotated bbox is used. |
---|---|---|
dx | number | Offset the anchor by dx . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
dy | number | Offset the anchor by dy . Default is 0 . |
string | Percentage strings (e.g. '40%' ) are also accepted. |
Example:
link.source(model, {
anchor: {
name: 'topRight',
args: {
rotate: true,
dx: 10,
dy: '40%'
}
}
});
A link connection point is an endpoint of the link route. This point is (usually) different from the link anchor point, as it takes into account the presence of the end element. Connection points are set via an connectionPoint
property provided within link end definitions (i.e. the objects provided to link.source()
and link.target()
functions).
The built-in functions work by finding an intersection between the link path (the path from the link's source anchor, through its vertices, to its target anchor). However, the functions always only have access to a single path segment; the source connectionPoint is found by investigating the first segment (i.e. source anchor - first vertex, or source anchor - target anchor if there are no vertices), while the target connectionPoint is found by investigating the last segment (i.e. last vertex - target anchor, or source anchor - target anchor). This has consequences if the investigated path segment is entirely contained within the end element.
There are four built-in connection point functions in JointJS:
'anchor'
- connection point at anchor'bbox'
- default connection point at bbox boundary'boundary'
- connection point at actual shape boundary'rectangle'
- connection point at unrotated bbox boundaryExample:
link.source(model, {
connectionPoint: {
name: 'boundary',
args: {
sticky: true
}
}
});
The default connection point is 'boundary'
; this can be changed with the defaultConnectionPoint
paper option. Example:
paper.options.defaultConnectionPoint = {
name: 'boundary',
args: {
sticky: true
}
};
All four of the built-in connection point functions accept the following optional argument, in addition to their own arguments:
offset | number | Offset the connection point from the anchor by the specified distance along the end link path segment. Default is 0 . |
---|
Example:
link.source(model, {
connectionPoint: {
name: 'bbox',
args: {
offset: 10
}
}
});
JointJS also contains mechanisms to define one's own custom connection point functions.
The 'anchor'
places the connection point so that it coincides with the link end's anchor point (determined either by the anchor
function or by the defaultAnchor
paper option). The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
align | 'top' | 'left' | 'bottom' | 'right' | Offset the connection point to the point given by projecting the first vertex onto the vector which points from the anchor point in the direction specified. (If there are no vertices, use the projection of the other anchor point instead.) Notably, if the reference point is not the direction-most point of the two, the connection point is set to be the same as the anchor point. Let us illustrate that outcome and the other possible outcome on the `'top'` direction:
|
alignOffset | number | After having determined the position of the connection point according to the `align` algorithm (see above), additionally offset the connection point by the specified amount in the direction specified by `align`. |
Example:
link.source(model, {
connectionPoint: {
name: 'anchor',
args: {
offset: 10
}
}
});
The 'bbox'
connection point function is the default connection point function. It places the connection point at the intersection between the link path end segment and the end element bbox. The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
stroke | boolean | Should the stroke width be included when calculating the connection point? Default is false . |
Example:
link.source(model, {
connectionPoint: {
name: 'bbox',
args: {
offset: 10,
stroke: true
}
}
});
The 'boundary'
connection point function places the connection point at the intersection between the link path end segment and the actual shape of the end element. (If JointJS is unable to determine the actual shape - e.g. for text - the element bbox is used instead, just as in the 'bbox'
connection point function.) The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
insideout | boolean | What happens if the link path never leaves the interior area of the end element (e.g. because the other end anchor lies within the first end element)? Should the path line be extended until an intersection with the boundary is found? Default is true . |
extrapolate | boolean | What happens if the link path never enters the interior area of the end element (e.g. because the anchor lies outside the end element)? Should the path line be extended to try and find the boundary? Default is false . Note that even if this option is true , an intersection is still not guaranteed. This option takes precedence over connectionPoint.args.sticky . |
sticky | boolean | What happens if the link path never enters the interior area of the end element (e.g. because the anchor lies outside the end element)? Should the closest point on the end element boundary be used instead? Default is false . Note that setting this option to true guarantees that a connection point will be found on the shape boundary. |
precision | number | The precision of the path intersection algorithm. Uses a logarithmic scale; increasing the number by 1 reduces the maximum observed error by a factor of ten. Default is 2 , corresponding to 1% error. |
selector | string | A selector to identify subelement/magnet of the end element at whose boundary we want the connection point to be found. Default is undefined , meaning that the first non-group descendant of the end element's node will be considered. (An example of another setting that may be useful is 'root' , which forces the usage of the root group bbox instead.). If set to false , the magnet is used as is, even if it is an SVGGroup (it's the most suitable for use in conjunction with magnetSelector). |
stroke | boolean | Should the stroke width be included when calculating the connection point? Default is false . |
Example:
link.source(model, {
connectionPoint: {
name: 'boundary',
args: {
offset: 10,
insideout: false,
extrapolate: true,
sticky: true,
precision: 3,
stroke: true
}
}
});
New connection point function can be defined in the joint.connectionPoints
namespace (e.g. joint.connectionPoints.myConnectionPoint
) or passed directly as a function to the connectionPoint
property of link source/target (or to the defaultConnectionPoint
option of a paper).
In either case, the connection point function must return the connection point as a Point. The function is expected to have the form function(endPathSegmentLine, endView, endMagnet, args)
:
endPathSegmentLine | g.Line | The link path segment at which we are finding the connection point. If we are calling this method for a source connection point, it is the first segment (source anchor - first vertex, or source anchor - target anchor). If we are calling this method for a target connection point, it is the last segment (last vertex - target anchor, or source anchor - target anchor). |
---|---|---|
endView | dia.ElementView | The ElementView to which we are connecting. The Element model can be accessed as endView.model ; this may be useful for writing conditional logic based on element attributes. |
endMagnet | SVGElement | The SVGElement in our page that contains the magnet (element/subelement/port) to which we are connecting. |
args | object | An object with additional optional arguments passed to the connection point method by the user when it was called (the args property). |
The 'rectangle'
connection point function places the connection point at the intersection between the link path end segment and the element's unrotated bbox. The position of the connection point may be modified by several additional arguments, which may be passed within the connectionPoint.args
property:
offset | number | object | An object with x and y properties. The connection point will be moved:
x offset.
|
---|---|---|
stroke | boolean | Should the stroke width be included when calculating the connection point? Default is false . |
Example:
link.source(model, {
connectionPoint: {
name: 'rectangle',
args: {
offset: 10,
stroke: true
}
}
});
Connection strategies come into play when the user modifies the position of link endpoints. There are two situations in which this is relevant:
anchor
property that might have been assigned on the dragged link endpoint will be overridden by the connection strategy. If necessary, have a look at the custom connectionStrategy documentation for information on replicating the functionality of anchor functions.)Both the anchor
and connectionPoint
properties are rewritten in response to user interaction. None of the built-in connection strategies preserve the originally assigned anchor and connection point functions. To assign precisely what you need as the anchor and connection point functions, you may need to define your own custom connection strategy.
Connection strategies are not assigned on a link-by-link basis. They are set with the connectionStrategy
option on a paper.
There are three built-in connection strategies in JointJS:
useDefaults
- default strategy; ignore user pointer and use default anchor and connectionPoint functionspinAbsolute
- use user pointer coordinates to position anchor absolutely within end element; use default connectionPointpinRelative
- use user pointer coordinates to position anchor relatively within end element; use default connectionPointThe default connection strategy is specified as null
in paper settings, which is equivalent to joint.connectionStrategies.useDefaults
.
joint.connectionStrategies
namespace. Example:
paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;
New connection strategies can be defined in the joint.connectionStrategies
namespace (e.g. joint.connectionStrategies.myConnectionStrategy
) or passed directly as a function to the connectionStrategy
option of a paper.
In either case, the connection strategy function must return an end definition (i.e. an object in the format supplied to the link.source()
and link.target()
functions). The function is expected to have the form function(endDefinition, endView, endMagnet, coords)
:
endDefinition | object | An end definition; the output of the appropriate end function (link.source() or link.target() ). An object containing at least an id of the Element to which we are connecting. This object is expected to be changed by this function and then sent as the return value. |
---|---|---|
endView | dia.ElementView | The ElementView to which we are connecting. The Element model can be accessed as endView.model ; this may be useful for writing conditional logic based on element attributes. |
endMagnet | SVGElement | The SVGElement in our page that contains the magnet (element/subelement/port) to which we are connecting. |
coords | g.Point | A Point object recording the x-y coordinates of the user pointer when the connection strategy was invoked. |
Custom connection strategies may be enormously useful for your users. Here we provide some examples of custom functionality.
If your diagram makes heavy use of nested elements, it may be useful to always connect links to a top-level ancestor element (instead of the element on which the arrowhead was actually dropped by user interaction):
joint.connectionStrategies.topAncestor = function(end, endView) {
var ancestors = endView.model.getAncestors();
var numAncestors = ancestors.length;
var end = numAncestors ? ancestors[numAncestors - 1] : end;
return end;
}
paper.options.connectionStrategy = joint.connectionStrategies.topAncestor;
If your diagram uses ports, you usually do not want links to be able to connect anywhere else. The solution is similar to the one above:
joint.connectionStrategies.firstPort = function(end, endView) {
var ports = endView.model.getPorts();
var numPorts = ports.length;
var end = numPorts ? { id: end.id, port: ports[0].id } : end;
return end;
}
paper.options.connectionStrategy = joint.connectionStrategies.firstPort;
Furthermore, it is very easy to replicate the built-in anchor functions for connection strategy scenarios - simply apply the anchor function to the received end
parameter:
joint.connectionStrategy.midSide = function(end) {
end.anchor = {
name: 'midSide',
args: {
rotate: true
}
};
return end;
}
paper.options.connectionStrategy = joint.connectionStrategy.midSide;
What if we needed to replicate a built-in connection point function instead? We use the same idea as in the previous example:
joint.connectionStrategy.boundary = function(end) {
end.connectionPoint = {
name: 'boundary',
args: {
offset: 5
}
};
return end;
}
paper.options.connectionStrategy = joint.connectionStrategy.boundary;
Of course, it is also possible to combine both of the examples and assign an anchor
as well as connectionPoint
to the end
parameter of the modified link.
The pinAbsolute
connection strategy records the coordinates of user pointer and assigns the end anchor absolutely, by reference to the top-left corner of the view bbox of the element above which the endpoint was dropped. Absolute positioning ensures that if the element is subsequently resized, the anchor stays at the same absolute distance from the edges (e.g. staying 10 pixels away from the left side and 20 pixels away from the top side).
The end connection point is assigned according to defaultConnectionPoint
paper option.
Example:
paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;
The end (source or target) that is being modified gets the 'topLeft'
anchor assigned by this connection strategy:
end.anchor = {
name: 'topLeft',
args: {
rotate: true
dx: (coords.x - bbox.x),
dy: (coords.y - bbox.y)
}
};
The pinRelative
connection strategy records the coordinates of user pointer and assigns the end anchor relatively, by reference to the top-left corner of the view bbox of the element above which the endpoint was dropped. Relative positioning ensures that if the element is subsequently resized, the anchor stays at the same relative distance from the edges (e.g. staying 25% of the way from the left side and 75% of the way from the top side).
The end connection point is assigned according to defaultConnectionPoint
paper option.
Example:
paper.options.connectionStrategy = joint.connectionStrategies.pinRelative;
The end (source or target) that is being modified gets the 'topLeft'
anchor assigned by this connection strategy:
end.anchor = {
name: 'topLeft',
args: {
rotate: true
dx: percentageString(coords.x - bbox.x),
dy: percentageString(coords.y - bbox.y)
}
};
The useDefaults
connection strategy is the simplest connection strategy. It ignores the coordinates of user pointer and assigns the end anchor according to the defaultAnchor
paper option and the end connection point according to the defaultConnectionPoint
paper option.
Thus, this connection strategy is equivalent to a connection strategy of null
.
Example:
paper.options.connectionStrategy = joint.connectionStrategies.useDefaults;
Connectors take an array of link route points and generate SVG path commands so that the link can be rendered. The connector
property of a link can be accessed with the link.connector()
function.
There are six built-in connectors in JointJS:
'straight'
- connector with straight lines and different ways to handle corners (point, rounded, bevelled, gap)'jumpover'
- connector with angled straight lines and bridges over link intersections'normal'
- (deprecated) default connector with angled straight lines'rounded'
- (deprecated) connector with straight lines and rounded edges'smooth'
- connector interpolated as a bezier curve'curve'
- connector interpolating as a curve defined by ending tangentsExample:
link.connector('straight', {
cornerType: 'line',
cornerRadius: 20
});
The default connector is 'normal'
; this can be changed with the defaultConnector
paper option. Example:
paper.options.defaultConnector = {
name: 'straight',
args: {
cornerType: 'line',
cornerRadius: 20
}
}
All of the built-in connectors accept the following optional argument, in addition to their own arguments:
raw | boolean | Should the router return the connection path as a g.Path rather than as a string? Default is false . |
---|
Example:
link.connector('straight', {
raw: true
});
JointJS also contains mechanisms to define one's own custom connectors.
Note that the modular architecture of JointJS allows mixing-and-matching connectors with routers as desired; for example, a link may be specified to use the 'jumpover'
connector on top of the 'manhattan'
router:
var link = new joint.shapes.standard.Link();
link.source(rect);
link.target(rect2);
link.router('manhattan');
link.connector('jumpover');
New connectors can be defined in the joint.connectors
namespace (e.g. joint.connectors.myConnector
) or passed directly as a function to the connector
property of a link (or to the defaultConnector
paper option).
In either case, the connector function must return a g.Path representing the SVG path data that will be used to render the link. The function is expected to have the form function(sourcePoint, targetPoint, routePoints, args)
:
sourcePoint | g.Point | The source connection point. |
---|---|---|
targetPoint | g.Point | The target connection point. |
routePoints | Array<g.Point> | The points of the route, as returned by the router in use. |
args | object | An object with additional optional arguments passed to the connector method by the user when it was called (the args property). |
Example of a connector defined in the joint.connectors
namespace:
joint.connectors.wobble = function(sourcePoint, targetPoint, vertices, args) {
var SPREAD = args.spread || 20;
var points = vertices.concat(targetPoint)
var prev = sourcePoint;
var path = new g.Path(g.Path.createSegment('M', prev));
var n = points.length;
for (var i = 0; i < n; i++) {
var next = points[i];
var distance = prev.distance(next);
var d = SPREAD;
while (d < distance) {
var current = prev.clone().move(next, -d);
current.offset(
Math.floor(7 * Math.random()) - 3,
Math.floor(7 * Math.random()) - 3
);
path.appendSegment(g.Path.createSegment('L', current));
d += SPREAD;
}
path.appendSegment(g.Path.createSegment('L', next));
prev = next;
}
return path;
}
var link = new joint.shapes.standard.Link();
link.source(source);
link.target(target);
link.connector('wobble', {
spread: 10
});
An example of a connector passed as a function is provided below. Notice that this approach does not enable passing custom args
nor can it be serialized with the graph.toJSON()
function.
var link = new joint.shapes.standard.Link();
link.source(source);
link.target(target);
link.connector(function(sourcePoint, targetPoint, vertices, args) {
var SPREAD = 20;
var points = vertices.concat(targetPoint)
var prev = sourcePoint;
var path = new g.Path(g.Path.createSegment('M', prev));
var n = points.length;
for (var i = 0; i < n; i++) {
var next = points[i];
var distance = prev.distance(next);
var d = SPREAD;
while (d < distance) {
var current = prev.clone().move(next, -d);
current.offset(
Math.floor(7 * Math.random()) - 3,
Math.floor(7 * Math.random()) - 3
);
path.appendSegment(g.Path.createSegment('L', current));
d += SPREAD;
}
path.appendSegment(g.Path.createSegment('L', next));
prev = next;
}
return path;
});
The 'jumpover'
connector draws straight lines with small arcs in place of link-link intersections. (For the time being, it cannot detect intersections with 'smooth'
router links). It accepts the following additional arguments, which can be passed as the connector.args
property:
size | number | The size of the jump (the diameter of the arc, or the length of the empty spot on the line). Defaults to 5 . |
---|---|---|
jump | string | The style of the jump. Either 'arc' (using an Arcto SVG path command), 'cubic' (using a Curveto path command as a normalized approximation to Arcto), or 'gap' (leaving a blank space). Defaults to 'arc' . |
radius | number | The curve radius of the rounded corners. Default is 0 . |
Example:
link.connector('jumpover', {
type: 'gap'
});
Deprecated. Use the 'straight'
connector instead.
Example:
link.connector('straight');
The 'normal'
connector is the default connector for links and it is the simplest connector. It simply connects provided route points with straight-line segments.
Example:
// deprecated
link.connector('normal');
Deprecated. Use the 'straight'
connector with cornerType: 'cubic'
instead. (To exactly replicate the 'rounded'
connector functionality, you should also pass precision: 0
.)
Example:
link.connector('straight', {
cornerType: 'cubic',
precision: 0,
cornerRadius: 20
});
The 'rounded'
connector connects provided route points with straight lines while smoothing all corners on the route. It accepts one additional argument, which can be passed within the connector.args
property:
radius | number | The curve radius of the rounded corners. Default is 10 . |
---|
Example:
// deprecated
link.connector('rounded', {
radius: 20
});
The 'smooth'
connector interpolates route points using a cubic bezier curve.
Example:
link.connector('smooth');
The 'straight'
connector connects provided route points with straight lines while handling corners on the route in different ways depending on provided arguments (point, rounded, bevelled, gap). It accepts four additional arguments, which can be passed within the connector.args
property:
cornerType | string | (Optional) How should corners on the route be handled? The default is 'point' . The available values are the following:
|
||||||||
---|---|---|---|---|---|---|---|---|---|---|
cornerRadius | number | (Optional) The distance of endpoints of line segments around corner points (e.g. curve radius for 'cubic' corner type). Default is 10 . This argument is ignored for cornerType: 'point' . |
||||||||
cornerPreserveAspectRatio | boolean | (Optional) Should distance of both endpoints be adjusted if one of them needs to be placed closer to a corner point than specified by the cornerRadius argument; or are the two allowed to be independent? (This situation happens when the distance between two consecutive route points is lower than cornerRadius * 2 .) Default is false . This argument is ignored for cornerType: 'point' |
||||||||
precision | number | (Optional) To how many decimal places should the returned point coordinates be rounded? Default is 1 . This argument is ignored for cornerType: 'point' . |
Example of a bevelled connector:
link.connector('straight', {
cornerType: 'line',
cornerPreserveAspectRatio: true
});
Example of a rounded connector:
link.connector('straight', {
cornerType: 'cubic',
cornerRadius: 20
});
Example of a connector with gaps at corners:
link.connector('straight', {
cornerType: 'gap',
cornerRadius: 2
});
Example of a connector with simple angled lines connecting route points:
link.connector('straight');
The 'curve'
connector interpolates route points using a catmull-rom curve converted into cubic bezier curve. Tangents at endings of a curve can be defined using several options.
Available options:
direction | curve.Directions | The overall direction of the curve. The default is curve.Directions.AUTO . |
---|---|---|
sourceDirection | curve.TangentDirections|object|number | The unit vector direction of the tangent at the source point. If the option is an object it must have x and y properties. If the option is a number, it represents an angle relative to the x-axis in the positive direction (counterclockwise). |
targetDirection | curve.TangentDirections|object|number | The unit vector direction of the tangent at the target point. If the option is an object it must have x and y properties. If the option is a number, it represents an angle relative to the x-axis in the positive direction (counterclockwise). |
sourceTangent | object | Vector of the tangent to the curve at the source point. Has priority over the sourceDirection and direction . Object must have x and y properties. |
targetTangent | object | Vector of the tangent to the curve at the target point. Has priority over the targetDirection and direction . Object must have x and y properties. |
distanceCoefficient | number | Coefficient of the tangent vector length relative to the distance between points. The default is 0.6 . |
rotate | boolean | Whether the rotation of the source or target element should be taken into account. Only works for AUTO, HORIZONTAL and VERTICAL directions. The default is false . |
The example of how to make the source point tangent always horizontal and target point tangent always go upwards:
link.connector('curve', {
direction: curve.Directions.HORIZONTAL,
targetDirection: curve.TangentDirections.UP
});
Available values for the direction
option.
curve.Directions.AUTO | Determines the tangent direction depending on the side of the element where the end point is located. |
---|---|
curve.Directions.HORIZONTAL | Limits tangents to horizontal directions (left and right) only. |
curve.Directions.VERTICAL | Limits tangents to vertical directions (up and down) only. |
curve.Directions.CLOSEST_POINT | Defines the direction tangent as a vector in the direction of the closest point. |
curve.Directions.OUTWARDS | Defines the direction tangent as a vector in from the center of the element to the end point. |
Available values for sourceDirection
and targetDirection
options.
curve.TangentDirections.AUTO | Determines the tangent direction depending on the side of the element where the end point is located. |
---|---|
curve.TangentDirections.UP | Sets the direction of the tangent upwards. |
curve.TangentDirections.DOWN | Sets the direction of the tangent downwards. |
curve.TangentDirections.LEFT | Sets the direction of the tangent to the left. |
curve.TangentDirections.RIGHT | Sets the direction of the tangent to the right. |
curve.TangentDirections.CLOSEST_POINT | Defines the direction tangent as a vector in the direction of the closest point. |
curve.TangentDirections.OUTWARDS | Defines the direction tangent as a vector from the center of the element to the corresponding end point. |
The attributes in JointJS define how the graphics elements are to be rendered inside of the element and link views.
All the standard SVG styling properties are available (both kebab-case
and camelCase
styles).
In addition JointJS modifies the behavior of existing attributes (the use of calc()
for specifying attribute values) and defines new so-called "special" attributes and allows programmers to define their own.
The calc()
function lets you perform calculations when specifying SVG attributes values.
The calc()
function takes a simple expression as its parameter, with the expression's result used as the value. The expression can be any simple expression in one of the following forms:
'calc(w)'
'calc(w + 5)'
'calc(h - 10)'
'calc(2 * w)'
'calc(0.5 * h)'
'calc(w / 2)'
'calc(h / 3)'
'calc(w / 2)'
'calc(h / 3)'
'calc(2 * w + 5)'
'calc(0.5 * h - 10)'
'calc(w / 2 + 5)'
'calc(h / 3 - 10)'
Where:
Variable is a symbol representing a value that can change, when the model attributes change (size, attrs).
variable | name | description |
---|---|---|
w |
width | The current width of the model (model.prop('size/width') ). The value can be bound to an SVGElement's size instead by using ref attribute. |
h |
height | The current height of the model (model.prop('size/height') ). The value can be bound to an SVGElement's size instead by using ref attribute. |
x |
x | The current x coordinate of the SVGElement in the element's coordinate system. If the attribute is not bound to a specific SVGElement with ref attribute, the value of x is always zero. |
y |
y | The current y coordinate of the SVGElement in the element's coordinate system. If the attribute is not bound to a specific SVGElement with ref attribute, the value of y is always zero. |
s |
shortest | The shortest side of the rectangle. The minimum of width and height. |
l |
longest | The longest side of the rectangle. The maximum of width and height. |
d |
diagonal | The length of the diagonal of the rectangle of size width and height. |
*
symbol.
1.5 *
/
symbol followed by a number.
/ 2
+
or -
symbol followed by a number.
+ 5
+
, -
and *
operators do not require whitespace.calc()
functions, in which case the inner ones are evaluated first.
e.g. 'M 0 0 H calc(w - calc(h))'
It can be used with the following attributes:
el.resize(200, 100); // dia.Element
// <rect joint-selector="myRect" width="200" height="100" rx="20" ry="10" />
el.attr('myRect', {
width: 'calc(w)',
height: 'calc(h)',
rx: 'calc(0.1*w)',
ry: 'calc(0.1*h)'
});
// <image joint-selector="myImage" x="105" y="55" />
el.attr('myImage/x', 'calc(0.5*w+5)');
el.attr('myImage/y', 'calc(0.5*h+5)');
// <path joint-selector="myPath" d="M 10 50 190 50" />
el.attr('myPath/d', 'M 10 calc(0.5*h) calc(w-10) calc(0.5*h)')
// <polygon joint-selector="myPolygon" points="0,0 200,0 200,100 0,100" />
el.attr('myPolygon/d', '0,0 calc(w),0 calc(w),calc(h) 0,calc(h)');
// Resize the rectangle to match the text size with extra 5 pixels of padding
// <rect joint-selector="myTextBackground" />
// <text joint-selector="myText" >Some text</text>
el.attr('myTextBackground', {
ref: 'myText',
x: 'calc(x - 5)',
y: 'calc(y - 5)'
width: 'calc(w + 10)',
height: 'calc(h + 10)',
});
Here is the list of all built-in attributes.
Move the subelement to the point at a given distance along the connection path but do not auto-orient it according to the path's gradient. Use a positive number to define the distance from the source of the link and a negative number from the target of the link. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionLengthIgnoreGradient: 30, width: 10, height: 10, fill: 'red' });
alias: atConnectionLength
Move and auto-orient the subelement to the point at a given distance along the connection path. Use a positive number to define the distance from the source of the link and a negative number from the target of the link. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionLength: 30, width: 10, height: 10, fill: 'red' });
link.attr('rectSelector', { atConnectionLengthKeepGradient: 30, width: 10, height: 10, fill: 'red' });
Move the subelement to the point at a given ratio of the connection total length but do not auto-orient it according to the path's gradient. It accepts a number in the [0,1]
range. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionRatioKeepGradient: .5, width: 10, height: 10, fill: 'red' });
alias: atConnectionRatio
Move and auto-orient the subelement to the point at a given ratio of the connection total length. It accepts a number in the [0,1]
range. It is valid only within the LinkView context.
link.attr('rectSelector', { atConnectionRatio: .5, width: 10, height: 10, fill: 'red' });
link.attr('rectSelector', { atConnectionRatioKeepGradient: .5, width: 10, height: 10, fill: 'red' });
If true
, set the shape of the subelement to match the shape of the LinkView (set the 'd'
attribute to the result of the link connector). It's valid only for SVGPathElement
within the LinkView context.
You can also provide an object with the following options:
Name | Type | Description |
---|---|---|
stubs | Number | If provided, display only the beginning and end stubs of the connection and hide the remaining central section. A positive value determines the length of each stub. If the value is negative, it determines the length of the hidden section of the connection between the two stubs. |
link1.attr('pathSelector', { connection: true, stroke: 'red', fill: 'none' });
link2.attr('pathSelector', { connection: { stubs: -20 }});
Designate another subelement of the cell as a proxy target for the cell's container, whenever this subelement (the one on which this attribute is defined) becomes the target of an embedding. (Usually, this attribute needs to be defined on the `'root'` subelement of the cell, since that is the default target for the cell's container.) Expects a selector.
A change in the cell's container affects the following characteristics of the cell view:
// `standard.Rectangle` has `root`, `body` and `label` subelements
const rect = new joint.shapes.standard.Rectangle();
// we want to have the `label` on the outside and under the `body` of the rectangle:
rect.attr(['label'], { refY: '100%', textVerticalAnchor: 'top' });
// normally, when a user tries to embed a child to the rectangle, the `root` subelement would be highlighted (the wrapper of `body` and `label`)
// however, we want only the `body` to be highlighted, so we need to specify it as a proxy for `root`:
rect.attr(['root', 'containerSelector'], 'body');
Valid only for <text>
subelements.
If set to true
, the SVGTextElement with empty content will be rendered. It's useful when the text needs to be editable.
The event
attribute makes the selected node and its descendants trigger an arbitrary event when clicked (mousedown/touchstart).
This event is triggered on the view itself and the paper.
The paper handler is called with the signature cellView
, evt
, x
, y
,
while the cell view handler is called only with evt
, x
, y
.
element.attr({
image: {
// pointerdown on the image SVG node will trigger the `element:delete` event
event: 'element:delete',
xlinkHref: 'trash.png'
width: 20,
height: 20
}
});
// Binding handler to the event
paper.on('element:delete', function(elementView, evt) {
// Stop any further actions with the element view e.g. dragging
evt.stopPropagation();
if (confirm('Are you sure you want to delete this element?')) {
elementView.model.remove();
}
});
The fill
attribute becomes a special attribute only in case it's defined as an object, instead of the usual SVG syntax (e.g. "#ffaabb"
).
If it's defined as an object, it is assumed to be either a gradient or pattern definition. It must have the form defined by the defineGradient() (resp. definePattern()) paper method.
element.attr('rect/fill', {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#E67E22' },
{ offset: '20%', color: '#D35400' },
{ offset: '40%', color: '#E74C3C' },
{ offset: '60%', color: '#C0392B' },
{ offset: '80%', color: '#F39C12' }
]
});
element.attr('rect/fill', {
type: 'pattern',
attrs: {
width: 10,
height: 10
},
markup: [{
tagName: 'polygon',
attributes: {
points: '0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2'
}
}]
});
The filter
attribute becomes a special attribute only in case it's defined as an object, instead of the usual SVG syntax (e.g. "url(#myfilter)"
).
If it's defined as an object, it must have the form defined by the defineFilter() paper method.
element.attr('rect/filter', {
name: 'dropShadow',
args: {
dx: 2,
dy: 2,
blur: 3
}
});
Designate another subelement of the cell as a proxy target for the cell's magnet, whenever this subelement (the one on which this attribute is defined) becomes the source/target of a link. (Usually, this attribute needs to be defined on the `'root'` subelement of the cell, since that is the default target for the cell's magnet.) Expects a selector.
A change in the cell's magnet affects the following characteristics of the cell view:
It has no effect on the connection validation and link model attributes.
// `standard.Rectangle` has `root`, `body` and `label` subelements
const rect = new joint.shapes.standard.Rectangle();
// we want to have the `label` on the outside and under the `body` of the rectangle:
rect.attr(['label'], { refY: '100%', textVerticalAnchor: 'top' });
// normally, highlighters would appear around the `root` subelement (the wrapper of `body` and `label`)
// however, we want highlighters to highlight only to the `body` subelement, so we need to specify it as a proxy for `root`:
rect.attr(['root','highlighterSelector'], 'body');
When set to true
, the subelement can become the source/target of a link during link reconnection. Useful for
so called 'ports'. When set to magnet: 'passive'
, a new link won't be added to the graph when a user clicks
on the respective magnet. This use case is common for the creation of 'input' ports.
const paper = new joint.dia.Paper({
// Other Paper options
// The default behaviour of magnet validation
validateMagnet: function(_cellView, magnet, _evt) {
return magnet.getAttribute('magnet') !== 'passive';
}
});
const portIn = {
label: {
// Label position and markup
},
attrs: {
portBody: {
// Other portBody attributes
magnet: 'passive' // No link added upon user click
},
label: { text: 'port' }
},
markup: [{
tagName: 'rect',
selector: 'portBody'
}]
};
Designate another subelement of the cell as a proxy target for the cell's magnet, whenever this subelement (the one on which this attribute is defined) becomes the source/target of a link. (Usually, this attribute needs to be defined on the `'root'` subelement of the cell, since that is the default target for the cell's magnet.) Expects a selector.
A change in the cell's magnet affects the following characteristics of the cell view:
It has no effect on the connection validation, magnet highlighter and the source
and target
link model attributes.
// `standard.Rectangle` has `root`, `body` and `label` subelements
const rect = new joint.shapes.standard.Rectangle();
// we want to have the `label` on the outside and under the `body` of the rectangle:
rect.attr(['label'], { refY: '100%', textVerticalAnchor: 'top' });
// normally, links would connect to the `root` subelement (the wrapper of `body` and `label`)
// however, we want links to connect only to the `body` subelement, so we need to specify it as a proxy for `root`:
rect.attr(['root','magnetSelector'], 'body');
An object containing at least an id
property. This property uniquely identifies the port.
If a link gets connected to a magnet that has also a port
object defined, the id
property of the port object will be copied to the port
property
of the source/target of the link.
Special attribute for setting the properties of the HTMLElement. The value is not set as a node attribute, but using the DOM property.
element.attr('input', {
props: { value: 'text', readonly: false },
type: 'text',
placeholder: 'Enter text'
});
props
is an object with one or more of the following properties: value
, checked
, disabled
, readOnly
, contentEditable
, multiple
, selected
, indeterminate
.
For example, to set the content of the textarea element, use the following syntax:
element.attr('textarea/props/value', 'textarea content');
When setting a value of the select element, pass the value of the option element.
element.attr('select/props/value', 'option1');
For the select with multiple options, pass an array of values.
element.attr('select/props/value', ['option1', 'option2]', { rewrite: true });
Markup or CSS selector pointing to an element that is used as a reference for relative positioning attributes.
Set cx
attribute of the subelement relatively to the width of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the cx
of the subelement will be set as a percentage
of the width of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the width of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
Set cy
attribute of the subelement relatively to the height of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the cy
of the subelement will be set as a percentage
of the height of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the height of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
Set d
attribute of the <path>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refD
path data is scaled so that the path's dimensions match the reference element's dimensions, and translated so that the path's origin matches the origin of the reference element.
The original path data offset is preserved. This means that if the top-left corner of the refD
bounding box does not lie at 0,0
, the gap between the path and origin is preserved in the rendered shape, as well.
var Path = joint.dia.Element.define('examples.Path', {
attrs: {
path: {
refDKeepOffset: 'M 10 10 30 10 30 30 z', // path offset is 10,10
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'path'
});
var p = (new Path()).resize(40, 40).addTo(graph);
// the rendered path's `d` attribute will be 'M 10 10 50 10 50 50 z'
// can be obtained by `p.findView(paper).vel.findOne('path').attr('d');`
alias: refD
Set d
attribute of the <path>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refD
path data is scaled so that the path's dimensions match the reference element's dimensions, and translated so that the path's origin matches the origin of the reference element.
The original path data offset is not preserved. This means that if the top-left corner of the refD
bounding box does not lie at 0,0
, the path is translated so that this gap disappears. The rendered path then fits perfectly into the reference element's bounding box.
var Path = joint.dia.Element.define('examples.Path', {
attrs: {
path: {
refDResetOffset: 'M 10 10 30 10 30 30 z', // path offset of 10,10 will be discarded
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'path'
});
var p = (new Path()).resize(40, 40).addTo(graph);
// the rendered path's `d` attribute will be 'M 0 0 40 0 40 40 z'
// can be obtained by `p.findView(paper).vel.findOne('path').attr('d');`
alias: ref-dx
Make x-coordinate of the subelement relative to the right edge of the element referenced to by the selector in ref
attribute.
Make y-coordinate of the subelement relative to the bottom edge of the element referenced to by the selector in ref
attribute.
alias: ref-height
Set height of the subelement relatively to the height of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the height of the subelement will be set as a percentage
of the height of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the height of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support width
and height
attributes, such
as <rect>
.
Same as refHeight
. Useful when one needs to resize an element absolutely and relatively at the same time. Note that all percentages are relative to 100% of the referenced height.
{ refHeight: 20, refHeight2: '-75%' } // resizes the element to 25% of the reference height (25% = 100% - 75%) plus extra 20 pixels
Set the points
attribute of the <polygon>
or <polyline>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refPoints
are scaled so that the subelement's dimensions match the reference element's dimensions, and translated so that the their origin matches the origin of the reference element.
The original points offset is preserved. This means that if the top-left corner of the refPoints
bounding box does not lie at 0,0
, the gap between the points and origin is preserved in the rendered shape, as well.
var Polygon = joint.dia.Element.define('examples.Polygon', {
attrs: {
polygon: {
refPoints: '10,10 30,10 30,30', // points offset is 10,10
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'polygon'
});
var p = (new Polygon()).resize(40, 40).addTo(graph);
// the rendered polygon's `points` attribute will be '10,10 50,10 50,50'
// can be obtained by `p.findView(paper).vel.findOne('polygon').attr('points');`
alias: refPoints
Set the points
attribute of the <polygon>
or <polyline>
subelement relatively to the dimensions and position of the element referenced by the selector in the ref
attribute. The refPoints
are scaled so that the subelement's dimensions match the reference element's dimensions, and translated so that the their origin matches the origin of the reference element.
The original points offset is not preserved. This means that if the top-left corner of the refPoints
bounding box does not lie at 0,0
, the subelement is translated so that this gap disappears. The rendered subelement then fits perfectly into the reference element's bounding box.
var Polygon = joint.dia.Element.define('examples.Polygon', {
attrs: {
polygon: {
refPoints: '10,10 30,10 30,30', // points offset of 10,10 will be discarded
fill: 'red',
stroke: 'black'
}
}
}, {
markup: 'polygon'
});
var p = (new Polygon()).resize(40, 40).addTo(graph);
// the rendered polygon's `points` attribute will be '0,0 40,0 40,40'
// can be obtained by `p.findView(paper).vel.findOne('polygon').attr('d');`
Set the r
attribute of the subelement relatively to the circumscribed size (length of the diagonal of the bounding box) of the element referenced by the selector in the ref
attribute. If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the r
of the subelement will be set as a percentage of the size of the referenced element. If the value is <0
or >1
, the size of the subelement will be smaller/bigger than the size of the referenced element by the amount specified. Note that this makes sense only for SVG elements that support r
attribute, such as <circle>
.
alias: refR
Set the r
attribute of the subelement relatively to the inscribed size (width or height of the bounding box, whichever is smaller) of the element referenced by the selector in the ref
attribute. If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the r
of the subelement will be set as a percentage of the size of the referenced element. If the value is <0
or >1
, the size of the subelement will be smaller/bigger than the size of the referenced element by the amount specified. Note that this makes sense only for SVG elements that support r
attribute, such as <circle>
.
Set rx
attribute of the subelement relatively to the width of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the rx
of the subelement will be set as a percentage
of the width of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the width of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
Set ry
attribute of the subelement relatively to the height of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the ry
of the subelement will be set as a percentage
of the height of the referenced element. If the value is <0
or >1
, the height
of the subelement will be smaller/bigger than the height of the referenced element by the amount specified.
Note that this makes sense only for SVG elements that support rx
and ry
attributes, such
as <ellipse>
.
alias: ref-width
Set width of the subelement relatively to the width of the element referenced to by the selector in ref
attribute.
If the value is in the [0, 1]
interval (or expressed in percentages, e.g. '80%'
), the width of the subelement will be set as a percentage
of the width of the referenced element. If the value is <0
or >1
, the width
of the subelement will be smaller/bigger than the width of the referenced element by the amount specified.
For example, 'ref-width': .75
sets the width of the subelement to 75%
of the width of the referenced element.
'ref-width': 20
makes the width to be 20px
less than the width of the referenced element.
Note that this makes sense only for SVG elements that support width
and height
attributes, such
as <rect>
.
Same as refWidth
. Useful when one needs to resize an element absolutely and relatively at the same time. Note that all percentages are relative to 100% of the referenced width.
{ refWidth: 20, refWidth2: '-75%' } // resizes the element to 25% of the reference width (25% = 100% - 75%) plus extra 20 pixels
alias: ref-x
Make x-coordinate of the subelement relative to the x-coordinate of the element referenced to by the selector in ref
attribute.
If ref-x
is in the (0,1)
interval (or expressed in percentages, e.g. '80%'
), the offset is
calculated from the fraction of the bounding box of the referenced element. Otherwise, it is an absolute value in pixels.
Same as refX
. Useful when one needs to position an element absolutely and relatively at the same time.
{ refX: '50%', refX2: 20 } // moves the element by 50% of the referenced width plus extra 20 pixels.
alias: ref-y
Make y-coordinate of the subelement relative to the y-coordinate of the element referenced to by the selector in ref
attribute.
If ref-y
is in the (0,1)
interval (or expressed in percentages, e.g. '80%'
), the offset is
calculated from the fraction of the bounding box of the referenced element. Otherwise, it is an absolute value in pixels.
Same as refY
. Useful when one needs to position an element absolutely and relatively at the same time.
{ refY: '50%', refY2: 20 } // moves the element by 50% of the referenced height plus extra 20 pixels.
If set to true
, the subelement offset (the distance from x0 y0 to the most top-left point) will be reset to x0 y0.
element.attr({
path: {
// The path bbox for `d="M 10 10 20 20"` is x10 y10 w10 h10.
d: 'M 10 10 20 20',
// The path bbox will be changed to x0 y0 w10 h10.
// This has same effect as passing path with `d="M 0 0 10 10"`
resetOffset: true
}
});
Valid for <path>
subelements. It draws an SVG element at the beginning of a path (the source of the link). The element is automatically rotated based on the path direction.
It must have the form defined by the defineMarker() paper method.
link.attr('line/sourceMarker', {
type: 'circle', // SVG Circle
fill: '#666',
stroke: '#333',
r: 5, // radius of the circle
cx: 5 // move the centre of the circle 5 pixels from the end of the path
});
The stroke
attribute becomes a special attribute only in case it's defined as an object. This has the exact same
behaviour as the fill attribute.
An object containing CSS styles for a subelement.
Valid for <path>
subelements. The same as sourceMarker but draws an SVG element at the end of the path.
Note the coordinate system for drawing is rotated by 180 degrees for convenience (this allows to use the same marker for source and target and have them both face the connected elements).
Valid only for <text>
subelements.
The text
attribute contains the text that should be set on the subelement. If the provided string does not contain any newline characters ('\n'
), the text is set directly as the content of the <text>
subelement. Alternatively, if the provided string has multiple lines, each line becomes the content of a <tspan>
child of the <text>
subelement.
If you need the text to be automatically wrapped inside the <text>
subelement, you should use the textWrap
attribute instead. It can automatically add line breaks into the provided string as necessary, and mark them with newline characters.
Valid only for <text>
subelements.
textPath
can be either a string or an object.
If it is a string, it specifies a path the text will be rendered along.
If it is an object, then it can contain a d
property that specifies the path the text will be rendered along.
Alternatively use selector
property, which is useful for targeting a specific SVGPathElement within the shape. The option expects a string selector (CSS or JSONMarkup selector).
Use startOffset
property to control the text position on the path (e.g. '50%'
, 20
). See the Vectorizer.text
method for more details.
Valid only for <text/>
subelements. The attribute is used to (top-, middle- or bottom-) align a text, relative to a point determined by reference to provided x
, y
, refX
, refY
, and similar attributes. See the Vectorizer.text
method for more details.
Valid only for <text>
subelements.
It enables the text wrapping for the text
attribute (automatically breaks the lines to fit within the reference bounding box).
It expects an object with several optional properties.
width
and height
adjust the final size of the wrapped text.
'calc(w - 10)'
)'50%'
is half the width or height)50
fits the text within 50 pixels)-10
is an equivalent to 'calc(w - 10)'
resp. 'calc(h - 10)'
)null
to disable wrapping in a given dimensionIf the text cannot fit into the bounding box as specified, the overflowing words are cut off.
If the ellipsis
option is provided, an ellipsis string is inserted before the cutoff.
If no words can fit into the bounding box at all, no text is inserted.
element1.attr('label', {
text: 'Text to wrap.',
textWrap: {
width: 'calc(w - 10)', // reference width minus 10
height: null', // no height restriction
}
});
element2.attr('label', {
text: 'lorem ipsum dolor sit amet consectetur adipiscing elit',
textWrap: {
width: -10, // reference width minus 10
height: '50%', // half of the reference height
ellipsis: true // could also be a custom string, e.g. '...!?'
}
});
For more info see util.breakText.
Add the text-only description in form of a <title>
element to the associated node. The title is not rendered as part of the graphics, but it's displayed as a tooltip.
element.attr('rect/title', 'Description of the rectangle');
Valid for <path>
subelements. The same as sourceMarker but draws SVG elements at every vertex of the path.
alias: x-alignment
If set to 'middle'
, the subelement will be centered around its new x-coordinate.
alias: y-alignment
If set to 'middle'
, the subelement will be centered around its new y-coordinate.
new Cell([attributes], [options])
When creating an instance of a cell, you can pass in the initial values of the attributes, which will be set on the model.
If you pass a { mergeArrays: true }
as the options, all the arrays defined as class defaults will be merged instead of overridden.
const MyRect = joint.shapes.standard.Rectangle.define('Rect', { array: [1,2] });
const rect1 = new MyRect({ array: [3] });
console.log(rect1.get('array')); // [3] array was overridden
const rect2 = new MyRect({ array: [3] }, { mergeArrays: true });
console.log(rect2.get('array')); // [3,2] array was merged
define(type [, defaultAttributes, prototypeProperties, staticProperties])
Helper to define a new Cell class or extend an existing one.
The type
must be a unique identifier of the class, which determines the location of the class definition in the joint.shapes
namespace (type
is the path to the class definition delimited by dots: .
). When creating an instance of the cell, attributes will be set to the value from the defaultAttributes
, unless overridden by subclass or instance attributes.
// Define a new Ellipse class in `joint.shapes.examples` namespace
// Inherits from generic Element class
var Ellipse = joint.dia.Element.define('examples.Ellipse', {
// default attributes
markup: [{
tagName: 'ellipse',
selector: 'ellipse' // not necessary but faster
}],
attrs: {
ellipse: {
fill: 'white',
stroke: 'black',
strokeWidth: 4,
rx: 'calc(0.5*w)',
ry: 'calc(0.5*h)',
cx: 'calc(0.5*w)',
cy: 'calc(0.5*h)'
}
}
});
// Instantiate an element
var ellipse = (new Ellipse()).position(100, 100).size(120, 50).addTo(graph);
// Define a new ColoredEllipse class
// Inherits from Ellipse
var ColoredEllipse = Ellipse.define('examples.ColoredEllipse', {
// overridden Ellipse default attributes
// other Ellipse attributes preserved
attrs: {
ellipse: {
fill: 'lightgray'
}
}
}, {
// prototype properties
// accessible on `this.(...)` - as well as, more precisely, `this.prototype.(...)`
// useful for custom methods that need access to this instance
// shared by all instances of the class
randomizeStrokeColor: function() {
var randomColor = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6);
return this.attr('ellipse/stroke', randomColor);
}
}, {
// static properties
// accessible on `this.constructor.(...)`
// useful for custom methods and constants that do not need an instance to operate
// however, a new set of static properties is generated every time the constructor is called
// (try to only use static properties if absolutely necessary)
createRandom: function() {
return (new ColoredEllipse()).randomizeStrokeColor();
}
});
// Instantiate an element
var coloredEllipse = ColoredEllipse.createRandom().position(300, 100).size(120, 50).addTo(graph);
markup
Either an XML string or JSON markup specifying an array of JSON elements. Also JSON markup can be described using an svg tagged template. Used as a template to build DOM Elements on the fly when the associated cellView is rendered.
JSON markup is defined recursively as an array of JSONElement
or strings representing text nodes, where JSONElement
is a plain object with the following properties:
tagName
(string) (required) The type of element to be created.selector
(string) A unique selector for targeting the element within the attr
cell attribute.groupSelector
(string | string[]) A selector for targeting multiple elements within the attr
cell attribute. The group selector name must not be the same as an existing selector name.namespaceURI
(string) The namespace URI of the element. It defaults to SVG namespace "http://www.w3.org/2000/svg"
.attributes
(object with attributes name-value pairs) Attributes of the element.style
(object with CSS property-value pairs) The style
attribute of the element.className
(string) The class
attribute of the element.children
(JSONMarkup) The children of the element.textContent
(string) The text content of the element.// single DOM element
markup: [{ tagName: 'rect' }]
// multiple DOM elements
markup: [{
tagName: 'rect',
selector: 'body'
}, {
tagName: 'text',
selector: 'label',
attributes: {
'stroke': 'none'
}
}]
// nested DOM elements
markup: [{
tagName: 'g',
children: [{
tagName: 'circle',
selector: 'circle1',
groupSelector: 'circles'
},
'text content',
{
tagName: 'circle',
selector: 'circle2',
groupSelector: 'circles'
}]
}]
JSON markup also can be defined using an svg tagged template. This tagged template converts SVG representation of the markup into a JSON markup object. Let's write the previous example using an svg tagged template:
const svg = joint.util.svg;
// single DOM element
markup: svg`<rect\>`
// multiple DOM elements
markup: svg`
<rect @selector="body"\>
<text
@selector="label"
stroke="none"
\>`
// nested DOM elements
markup: svg`
<g>
<circle
@selector="circle1"
@group-selector="circles"
/>
text content
<circle
@selector="circle2"
@group-selector="circles"
/>
</g>`
Anything you define in markup
is evaluated once at CellView creation (the DOM elements and their attributes).
That means it's important to think about the runtime of your application. If you have SVG attributes that don't change
throughout the runtime, you can add them to the markup
.
As markup
is something all instances of the element type are expected to have in common, inheriting from
the subtype prototype is more efficient. Nevertheless, it is still possible to provide custom markup to individual instances
of your class by providing markup later.
Anything in the attrs
attribute is evaluated on every change of the model (e.g. a resize
or
an attrs
change). As JointJS
special attributes mostly depend on the current state of the model (size
, attrs
, angle
),
they should always be defined inside attrs
.
SVG attributes that are modified at some point in the application should also be added to attrs
(e.g. user
changes the color of the element).
joint.dia.Element.define('standard.Rectangle', {
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
stroke: 'red',
fill: '#333333' // We wan't to modify the fill color of our instances
},
label: {
// attributes
}
}
}, {
markup: [{
tagName: 'rect',
selector: 'body',
attributes: {
'stroke-width': 2 // Use native SVG kebab-case for attributes in markup
}
}, {
tagName: 'text',
selector: 'label',
attributes: {
'stroke': 'none' // We don't need any instances to have a stroke value for text
}
}]
});
selector
and groupSelector
.
selector
is unique i.e. can target a single node only.selector
takes precedence over groupSelector
.groupSelector
targeting n
nodes takes precedence over groupSelector
targeting m
nodes for n
< m
. When n
=== m
the order how the attributes are applied is unspecified.In the example below, the circle with the selector circle1
is filled with "red"
color. All the other circles are filled with "blue"
color.
cell.attr({
circle1: { fill: 'red' },
circles: { fill: 'blue', stroke: 'black' }
});
A valid XML string that contains either a single tagName
or XML that can be parsed with DOMParser
.
markup: 'rect'
markup: '<rect class="rectangle"/>'
markup: '<rect><g><circle/><circle/></g>'
Note that you also need to set the useCSSSelectors=true
on the model's prototype in order to use CSS selectors in the attrs
attribute.
joint.dia.Element.define('Rectangle', {
attrs: {
rect: { // CSS Selector for the <rect/> element
fill: 'red'
}
}
}, {
markup: '<rect class="rectangle"/>',
useCSSSelectors: true
});
cell.getParentCell()
Return the parent cell of cell
or null
if there is none.
cell.isElement()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.Element()
is equivalent to cell instanceof joint.dia.Element
. Example:
var cell = graph.getCell(myId)
if (cell.isElement()) {
// Do something if the cell is an element.
}
element.isLink()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isLink()
is equivalent to cell instanceof joint.dia.Link
. Example:
var cell = graph.getCell(myId)
if (cell.isLink()) {
// Do something if the cell is a link.
}
cell.parent()
Return the parent
property of the cell.
If the cell is a part of an embedding, the id
of the parent cell is returned as a string.
If you need to be sure that a Cell is returned (and not its id
), use the cell.getParentCell
function instead.
cell.z()
Return z – the stacking order (an equivalent to HTML z-index
).
When z is undefined
or null
, function returns 0
.
The view for the joint.dia.Cell model. It inherits from mvc.View and is responsible for:
To find the view associated with a specific cell (model), use the findViewByModel method of the paper. For example:
var cellView = paper.findViewByModel(cell);
When you define a custom CellView, which listens to the underlying model changes and update itself (modifying its sub-elements directly) you should follow the instructions below in order to make the view work correctly in the async mode.
Note, that when a view needs an update (a model attribute has changed e.g. size
has changed), it requests the update from the paper first. The paper confirms the update immediately (sync
mode) or in the next animation frame (async
mode). The updates may be also held by the paper as long as the paper is frozen and released when the paper changes its state to unfrozen.
The update requests are sent to the paper via flags
and later received back all at once. The paper accumulates flags received and confirms the updates when the right time has come.
flagLabel
- is an arbitrary string or array of strings
flags
- are encoded flag labels
CellView.prototype.presentationAttributes
- is an object that maps model attributes to flag labels.
CellView.prototype.confirmUpdate(flags, opt)
- This method receives all scheduled flags and based on them updates the view
CellView.prototype.hasFlag(flags, flagLabel)
- Checks whether a flagLabel
is present in the current update.
CellView.addPresentationAttributes(presentationAttributes)
- Extends the CellView presentation attributes with another presentationAttributes
. - The method makes sure the original CellView attributes are preserved. It returns a new object with all presentation attributes.
const FadeView = joint.dia.ElementView.extend({
// Make sure that all super class presentation attributes are preserved
presentationAttributes: joint.dia.ElementView.addPresentationAttributes({
// mapping the model attributes to flag labels
faded: 'flag:opacity'
}),
confirmUpdate(flags, ...args) {
joint.dia.ElementView.prototype.confirmUpdate.call(this, flags, ...args);
if (this.hasFlag(flags, 'flag:opacity')) this.toggleFade();
},
toggleFade() {
this.el.style.opacity = this.model.get('faded') ? 0.5 : 1;
}
});
cellView.findAttribute(attribute, node)
Return the value of the specified attribute
of node
.
If node
does not set a value for attribute
, start recursing up the DOM tree from node
to look for attribute
at the ancestors of node
. If the recursion reaches CellView's own node and attribute
is not found even there, return null
.
cellView.findNode(selector)
The method returns a DOM node matching the selector
.
The method searches for matching sub-element among the descendants of this.el
. If no such sub-element is found, null
is returned.
The available selectors
are defined by the markup
attribute of the Cell model from which the CellView was initialized.
cellView.findNodes(groupSelector)
The method returns an array of DOM nodes matching the groupSelector
.
The method searches for matching sub-elements among the descendants of this.el
. If no such sub-elements are found, an empty array is returned.
The available groupSelectors
are defined by the markup
attribute of the Cell model from which the CellView was initialized.
cellView.highlight([el[, options]])
Add a highlighter to the cell view.
When the method is called, the cell:highlight
paper event is triggered.
Arguments:
el
will be usedname
cellView.isDefaultInteractionPrevented(event)
Returns whether preventDefaultInteraction was ever called on this event
object.
cellView.preventDefaultInteraction(event)
The method tells the view that its default interactivity action should not be taken as it normally would be.
For example, if the view is a joint.dia.ElementView and the user clicks on it, the view will normally enter the interactive mode and starts moving the element.
Calling preventDefaultInteraction
will prevent the view from entering the interactive mode. The view will still trigger all events, but it will not react to them in the default way.
It is useful when you want to handle the pointer events yourself. For example, you can use it to implement your own custom interaction with the view. A selection lasso can be drawn whenever the user holds down the shift key and starts dragging from any point on the paper (regardless of whether it is an element, a link or a blank space).
paper.on('element:pointerdown', (elementView, evt) => {
if (evt.shiftKey) {
// prevent element movement
elementView.preventDefaultInteraction(evt);
// start drawing selection lasso
}
});
The following table shows which default interaction will be stopped depending on what point the method was called.
Paper events | Description | Paper interactive option |
---|---|---|
'element:pointerdown' |
Prevents element movement. | elementMove |
'element:magnet:pointerdown' |
Prevents adding link from magnet and element movement. | addLinkFromMagnet |
'link:pointerdown' |
Prevents link label and link movement. | labelMove and linkMove |
cellView.unhighlight([el[, options]])
Removes a highlighter added to the cell view.
When the method is called, the cell:unhighlight
paper event is triggered.
The basic model for diagram elements. It inherits from joint.dia.Cell with a few additional properties and methods specific to elements. For a quick introduction to elements, see our tutorial.
Elements' properties may be split into several groups according to their function:
position
- coordinates of the element, provided as an object with x
and y
keys. The property may be accessed or set directly using regular mvc.Model set('position')
/get('position')
methods or through Element's translate()
method.angle
- angle of rotation of the element in degrees. The rotation origin is always the center of the element. The property may be accessed or set directly using regular mvc.Model set('angle')
/get('angle')
methods or through Element's rotate()
method.size
- size of the element, provided as an object with width
and height
keys. The property may be accessed or set directly using regular mvc.Model set('size')
/get('size')
methods or through Element's translate()
method.Each joint.dia.Element
defines its own SVG markup
which is then used by joint.dia.ElementView
to render the element to the paper. For instance, the joint.shapes.standard.Rectangle
element (which inherits from joint.dia.Element
) defines its markup using the JSON array notation as follows:
markup: [{
tagName: 'rect',
selector: 'body',
}, {
tagName: 'text',
selector: 'label'
}]
As we can see, the joint.shapes.standard.Rectangle
shape consists of two subelements
: one SVGRectElement named 'body'
and one SVGTextElement named 'label'
. The attrs
object refers to the subelements' names (selectors
) to provide SVG attributes to these constituent SVGElements.
The keys of the attrs
object are selectors that match subelements defined in the element's markup
. The values of this object are SVG attributes that will be set on the selected subelements. One can find the full list of SVG attributes and their descriptions online, e.g. on MDN.
For example, in order to set a red fill color on a subelement called 'body'
, the attrs
object would contain:
body: { fill: 'red' }
If you simply need to change a value of an attribute, it is not recommended to modify the attrs
object directly. Instead, use the element.attr()
method.
We can use the joint.shapes.standard.Rectangle
element (which inherits from joint.dia.Element
) as an example. The attrs
object in its definition is provided below:
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
strokeWidth: 2,
stroke: '#000000',
fill: '#FFFFFF'
},
label: {
textVerticalAnchor: 'middle',
textAnchor: 'middle',
x: 'calc(0.5*w)',
y: 'calc(0.5*h)',
fontSize: 14,
fill: '#333333'
}
}
Notice that the object makes use of special JointJS attributes (e.g. textVerticalAnchor
)
on top of standard SVG attributes (e.g. stroke
, fill
). All of these special attributes
are listed in the
attributes section of this documentation, along with information on how to use calc()
for sizing.
You should also refer to our tutorial on special attributes.
Attributes defined in markup
are evaluated once at CellView creation,
while attributes defined in attrs
are evaluated on every model change. As JointJS special attributes usually depend on the
current state of the model, we should define them in attrs
, along with any other SVG attributes that will be modified
in the runtime of your application.
The z
property specifies the stack order of the element in the SVG DOM. An element with a higher z
value will be rendered in front of an element with a lower z
value. (This also applies to Links.)
You may define and group ports on the Element with the ports
attribute. Each can have its own markup and attrs, and its own label. Grouped ports may share properties and behaviors. See the element ports documentation for more information.
The last two properties of elements are embeds
and parent
. These two are related to elements that contain or are contained within other elements, forming a hierarchical structure.
embeds
- a list of id
's of the Cells that are embedded inside this Element.parent
- the id
of the Element that is the parent of this element. When a parent element is translated, all its children get translated too.Elements trigger several special events, detailed in the element events documentation.
It is possible to extend the joint.dia.Element
class to create a custom element. A custom element may override Element properties to assign its own defaults. These values override builtin defaults, if provided, and are applied to all instances of the new Element type, unless an individual instance overrides them with its own values. The following Element properties are applicable in this context:
markup
- provide default element markup for all instances of this Element type, specified with a JSON array.attrs
- provide default element styling for all instances of this Element type. These allow you to change the style and size of SVG elements, identified by their selectors.Creating custom elements is explained in more detail in our tutorial.
The following list contains events that you can react on:
change
- generic event triggered for any change on the element - fn(element, opt)change:position
- triggered when the element changes its position - fn(element, newPosition, opt)change:angle
- triggered when the element gets rotated - fn(element, newAngle, opt)change:size
- triggered when the element gets resized - fn(element, newSize, opt)change:attrs
- triggered when the element changes its attributes - fn(element, newAttrs, opt)change:embeds
- triggered when other cells were embedded into the element - fn(element, newEmbeds, opt)change:parent
- triggered when the element got embedded into another element - fn(element, newParent, opt)change:z
- triggered when the element is moved in the z-level (toFront and toBack) - fn(element, newZ, opt)transition:start
- triggered when a transition starts. - fn(element, pathToAttribute)transition:end
- triggered when a transiton ends. - fn(element, pathToAttribute)// Listening for changes of the position to a single element
element1.on('change:position', function(element1, position) {
alert('element1 moved to ' + position.x + ',' + position.y);
});
// All elements events are also propagated to the graph.
graph.on('change:position', function(element, position) {
console.log('Element ' + element.id + 'moved to ' + position.x + ',' + position.y);
});
// Using the option parameter and a custom attribute
graph.on('change:custom', function(element, custom, opt) {
if (opt.consoleOutput) {
console.log('Custom attribute value changed to "' + custom + '"');
}
});
element2.prop('custom', 'myValue', { consoleOutput: true });
Many diagramming applications deal with elements with ports. Ports are usually displayed as circles inside diagram elements and are used not only as "sticky" points for connected links, but they also further structure the linking information. It is common that certain elements have lists of input and output ports. A link might not then point to the element as a whole, but to a certain port instead.
It's easy to add ports to arbitrary shapes in JointJS. This can be done either by passing a ports definition as an option
in the constructor, or using the ports API to get/add/remove single or multiple ports. For more information on how to define ports please see the Port configuration section.
joint.dia.Element
hasPort
/ hasPorts
addPort
/ addPorts
removePort
/
removePorts
getPort
/ getPorts
portProp
getPortsPositions
// Single port definition
const port = {
// id: 'abc', // Generated if `id` value is not present
group: 'a',
args: {}, // Extra arguments for the port layout function, see `layout.Port` section
label: {
position: {
name: 'left',
args: { y: 6 } // Extra arguments for the label layout function, see `layout.PortLabel` section
},
markup: [{
tagName: 'text',
selector: 'label'
}]
},
attrs: {
body: { magnet: true, width: 16, height: 16, x: -8, y: -4, stroke: 'red', fill: 'gray'},
label: { text: 'port', fill: 'blue' }
},
markup: [{
tagName: 'rect',
selector: 'body'
}]
};
// a.) Add ports in constructor.
const rect = new joint.shapes.standard.Rectangle({
position: { x: 50, y: 50 },
size: { width: 90, height: 90 },
ports: {
groups: {
'a': {}
},
items: [
{ group: 'a' },
port
]
}
});
// b.) Or add a single port using API
rect.addPort(port);
rect.getGroupPorts('a');
/*
[
{ * Default port settings * },
{ * Follows port definition * },
{ * Follows port definition * }
]
*/
id | string |
It is automatically generated if no id is provided. IDs must be unique in the context of a single shape - two ports with the same port id are therefore not allowed (Element: found id duplicities in ports. error is thrown).
|
group | string | Group name, more info in the groups section. |
args | object |
Arguments for the port layout function. Available properties depend on the type of layout. More information can be found in layout.Port .
|
attrs | object |
JointJS style attribute definition. The same notation as the attrs property on Element .
|
markup | MarkupJSON | string |
A custom port markup. The default port markup is The root of the port markup is referenced by the If the markup contains more than one node, an extra group is created to wrap the nodes. This group becomes the new
|
label | object |
Port label layout configuration. E.g. label position, label markup. More information about port label layouts can be found in layout.PortLabel .
|
|
string | object |
Port label position configuration. It can be a
|
|
string |
It states the layout type. It matches the layout method name defined in the joint.layout.PortLabel namespace: name: 'left' is implemented as joint.layout.PortLabel.left .
|
|
object |
Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the layout.PortLabel section.
|
|
MarkupJSON | string |
A custom port label markup. The default port label markup is The root of the label markup is referenced by the If the markup contains more than one node, an extra group is created to wrap the nodes. This group becomes the new All Use an empty array |
z | number | string |
An alternative to HTML
Shapes most likely consist of 1 or more DOM elements, For instance, the first shape from the demo above with the following markup...
...will be rendered like this:
Ports will be placed in the
It will be rendered like this:
|
All properties described above are optional, and everything has its own default. E.g. element.addPorts([{}, {}])
will add 2 ports with default settings.
While single port definitions are useful, what if we want more control over our ports? This is where a port group can come into play. A group allows us to define multiple ports with similar properties, and influence the default port alignment. Any individual port can override a property in a port group definition except the type of layout(E.g. position: 'left'
). The group definition defines the layout, and the individual port args
are the only way a port can affect it.
// Port definition for input ports group
const portsIn = {
position: {
name: 'left', // Layout name
args: {}, // Arguments for port layout function, properties depend on type of layout
},
label: {
position: {
name: 'left',
args: { y: 6 }
},
markup: [{
tagName: 'text',
selector: 'label',
}]
},
attrs: {
body: { magnet: 'passive', width: 15, height: 15, stroke: 'red', x: -8, y: -8 },
label: { text: 'in1', fill: 'black' }
},
markup: [{
tagName: 'rect',
selector: 'body'
}]
};
// Define port groups in element constructor
const rect = new joint.shapes.basic.Rect({
// ...
ports: {
groups: {
'group1': portsIn,
// 'group2': ...,
// 'group3': ...,
},
items: [
// Initialize 'rect' with port in group 'group1'
{
group: 'group1',
args: { y: 40 } // Overrides `args` from the group level definition for first port
}
]
}
});
// Add another port using Port API
rect.addPort(
{ group: 'group1', attrs: { label: { text: 'in2' }}}
);
position | string | object |
Port position configuration. It can be a string to set the port layout type directly with default
settings, or an object where it's possible to set the layout type and options.
|
|
string |
It states the layout type. Match the layout method name defined in the joint.layout.Port namespace: name: 'left' is implemented as joint.layout.Port.left .
|
|
object |
Arguments for the port layout function. Available properties depend on the type of layout. More information can be found in layout.Port .
|
attrs | object |
JointJS style attribute definition. The same notation as the attrs property on Element .
|
markup | MarkupJSON | string |
Custom port markup. Multiple roots are not allowed. Valid notation would be:
The default port looks like the following: |
label | object |
Port label layout configuration. E.g. label position, label markup. More information about port label layouts can be found in the layout.PortLabel section.
|
|
string | object |
Port label position configuration. It can be a
|
|
string |
It states the layout type. It matches the layout method name defined in the joint.layout.PortLabel namespace: name: 'left' is implemented as joint.layout.PortLabel.left .
|
|
object |
Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the layout.PortLabel section.
|
|
MarkupJSON | string |
Custom port label markup. Multiple roots are not allowed. The default port label looks like the following: <text class="joint-port-label" fill="#000000"/> .
|
Both port and port label can have custom markup...
var rect = new joint.shapes.basic.Rect({
// ...
});
rect.addPort({
markup: [{
tagName: 'rect', selector: 'body', attributes: { 'width': 20, 'height': 20, 'fill': 'blue' }
}]
});
rect.addPort({
markup: [{
tagName: 'rect', selector: 'body', attributes: { 'width': 16, 'height': 16, 'fill': 'red' }
}],
label: {
markup: [{ tagName: 'text', selector: 'label', attributes: { 'fill': '#000000' }}]
},
attrs: { label: { text: 'port' }}
});
...or, it can be set as an default port markup/port label markup on an element model:
var rect = new joint.shapes.basic.Rect({
portMarkup: [{
tagName: 'rect',
selector: 'body',
attributes: {
'width': 20,
'height': 20,
'fill': 'blue',
'stroke': 'black',
'stroke-width': 5 // Use native SVG kebab-case for attributes in markup
}
}],
portLabelMarkup: [{
tagName: 'text',
selector: 'label',
attributes: {
'fill': 'yellow'
}
}]
// ...
});
element.addPort(port, [opt])
Add a single port, where port
could be defined as described in section Port interface
element.addPorts(ports, [opt])
Add array of ports
. Ports are validated for id
duplicities, if there is a id collision, no new ports are added, where port
could be defined as describe in section Port interface
element.addTo(graph)
Add the element to the graph
(an instance of joint.dia.Graph
). This is equivalent to calling graph.addCell(element)
.
element.angle()
Return the rotation of the element in degrees (0° - 360°).
element.attr(attrs [, opt])
Set presentation attributes (SVG and JointJS attributes) on view subelements. attrs
can either be an object or string representing a path to a nested attribute. If it is an object, the keys of the attrs
object are selectors (JSON Markup Selector or CSS Selector) matching the subelements. The values are objects containing SVG attributes and their values. attrs
object will be mixed with attrs
property of the element
model. This is a convenient way of rewriting only some of the attributes of the subelements. For overwriting all attributes of all subelements, use element.set('attrs', attrs)
.
element.attr({
// selectors as defined in the JSON markup
body: { width: 'calc(w)', height: 'calc(h)' },
label: { text: 'My Label' },
// using CSS selectors is significantly slower
rect: { fill: 'blue' },
text: { fill: 'white', fontSize: 15 },
'.myrect2': { fill: 'red' }
});
prop()
or set()
methods.
element.set('confirmed', true);
element.prop('data/count', 10);
element.attr(path, value [, opt])
An alternative call using a string/array path and a value:
element.attr('body/fill', 'red');
element.attr(['label', 'fontSize'], 12);
// Note: an equivalent expression is also
element.prop('attrs/label/fontSize', 12);
element.attr([path])
Get attribute value defined by a path. If no path provided the whole attrs
object is returned.
element.attr('body/fill') === 'red';
element.attr(['label', 'fontSize']) === 12;
element.clone(options)
Returns a new instance of the element with identical attributes. If options.deep === true
, then
all the embedded cells (elements, links) of the element are cloned as well. In this case, the return value is an array of
instances rather then a single instance.
element.embed(cell, [opt])
element.embed(cells, [opt])
Embed a cell (element or link), or an array of cells into the element. The element then becomes a parent of the embedded cell. When a parent is moved (translated), all cells embedded into that parent will move as well. If links are embedded, their vertices move with the parent. This way both options are available: if a link is not embedded but its source/target elements are and their parent moves, the embedded elements move with the parent but the link vertices stay at the same position. If the link is embedded with its source/target elements, its vertices move as the parent moves.
Any additional option or custom property provided in the options object will be accessible in the callback function of
the Element 'change:embeds'
event.
const rect1 = new joint.shapes.standard.Rectangle({
position: { x: 100, y: 100 },
size: { width: 90, height: 30 },
attrs: { label: { text: 'Rect' } }
});
const rect2 = rect1.clone();
rect2.translate(100, 0);
graph.addCells([rect1, rect2]);
rect1.on('change:embeds', function(element, newEmbeds, opt) {
console.log(opt); // {testOption: true}
});
// Add custom 'testOption' property
rect1.embed(rect2, { testOption: true });
element.findView(paper)
Find view (joint.dia.ElementView
) for the element model in the paper
.
This is a shortcut to the equivalent call paper.findViewByModel(element)
element.fitEmbeds([opt])
An alias for the element.fitToChildren
function.
element.fitParent([opt])
Resize and reposition this element's embedding parent element such that all of its children (including this element) end up within the parent's new bounding box.
Starting from a given element, this function proceeds upwards
to that element's parent (and further ancestors, if opt.deep
is used). In that sense, this function is the opposite of the element.fitToChildren
function.
Available options:
padding | number | Inflate the embedding parent element's calculated bounding box by this much additional padding. |
---|---|---|
expandOnly | boolean | If true , the algorithm is only ever allowed to expand the bounding box of the embedding parent element, never to shrink it. You can visualize this setting as the algorithm dragging the top-left and bottom-right corners of the parent's bounding box outward until its children (including this element) are within the bounding box. |
shrinkOnly | boolean |
If If only a portion of this element (or this element's sibling element) initially overlaps the embedding parent element, the parent's calculated bounding box will only include that portion. If there is no overlap between this element and its parent (i.e. this element is |
deep | boolean |
If Note that if this option is used in conjunction with |
terminator | Cell | Cell.ID |
If Handling of edge cases:
|
element.fitToChildren([opt])
Resize and reposition this element such that all of its embedded child elements end up within this element's new bounding box.
Starting from a given element, this function proceeds downwards
through that element's children (and further descendants, if opt.deep
is used). In that sense, this function is the opposite of the element.fitParent
function.
Available options:
padding | number | Inflate this element's calculated bounding box by this much additional padding. |
---|---|---|
expandOnly | boolean | If true , the algorithm is only ever allowed to expand the bounding box of this element, never to shrink it. You can visualize this setting as the algorithm dragging the top-left and bottom-right corners of this element's bounding box outward until all its embedded child elements are within the bounding box. |
shrinkOnly | boolean |
If If only a portion of an embedded child element initially overlaps this element, the calculated bounding box will only include that portion. If there is no overlap between this element and its children (i.e. all children are currently placed |
deep | boolean |
If Note that if this option is used in conjunction with |
element.getAbsolutePointFromRelative(x, y)
element.getAbsolutePointFromRelative(relativePoint)
Accept a point in the coordinate system of the element and return the point in the graph coordinate system, represented as a g.Point
.
The element coordinate system has its origin [0,0]
at the element.position()
rotated by the element.angle()
. Each axis x
and y
are rotated by the same angle.
element.getAncestors()
Return an array of all the ancestors of this element starting from the immediate parent all the way up to the most distant ancestor.
element.getBBox([opt])
Return the bounding box of the element model, represented as a g.Rect
.
Keep in mind that this function reports the dimensions of the element's model, not the dimensions of the element's view. (For example, the model bounding box does not include any associated ports). See the documentation of the elementView.getBBox
function for details about the differences.
if (element1.getBBox().intersect(element2.getBBox())) {
// intersection of the two elements
}
opt | description |
---|---|
deep | Return the union of the element's bounding box and all the elements embedded into it. |
rotate | Return the bounding box after the element's rotation. |
element.getEmbeddedCells([opt])
Return an array of all the embedded cells of an element. If all you need is id
's of all the embedded cells,
use element.get('embeds')
. If opt.deep
is true
, all the deeply embedded
cells will be returned. The order in which the cells are returned depends on the search algorithm used. By
default, Depth-first search (DFS) algorithm is used. If
opt.breadthFirst
is true
, the Breadth-first search algorithm will be used instead.
element.getGroupPorts(groupName)
Returns an array of all ports on the element with the given groupName
. If there is no such a port an empty array is returned.
element.getPort(id)
Returns the port specified by an id
. If such a port does not exist the method returns undefined
.
element.getPortIndex(portId)
Returns the port index in the array of port items.
element.getPorts()
Returns an array of all ports on the element. If there is no port an empty array is returned.
element.getPortsPositions(groupName)
Returns the positions and the angle of all ports in the group, relatively to the element position.
element.getRelativePointFromAbsolute(x, y)
element.getRelativePointFromAbsolute(absolutePoint)
Accept a point in the graph coordinate system and return the point in the coordinate system of the element, represented as a g.Point
.
The element coordinate system has its origin [0,0]
at the element.position()
rotated by the element.angle()
. Each axis x
and y
are rotated by the same angle.
element.getTransitions()
Return an array of all active transitions (their paths).
element.hasPort(id)
Check if an element contains a port with id
. Returns boolean
.
element.hasPorts()
Check if an element has any ports defined. Returns boolean
.
element.insertPort(before, port, [opt])
Insert a new port before another port, where port
could be defined as described in section Port interface and before
is either an index, port id or port itself.
element.isElement()
Always returns true
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isElement()
is equivalent to cell instanceof joint.dia.Element
. Example:
var cell = graph.getCell(myId)
if (cell.isElement()) {
// Do something if the cell is an element.
}
element.isEmbeddedIn(element [, opt])
Return true
if the element is embedded in another element element
. If opt.deep
is false
, only direct parentage will be checked. opt.deep
is true
by default.
element.isLink()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isLink()
is equivalent to cell instanceof joint.dia.Link
. Example:
var cell = graph.getCell(myId)
if (cell.isLink()) {
// Do something if the cell is a link.
}
element.portProp(portId, path, [value])
Set properties, possibly nested, on the element port. This is an equivalent of the attr() method but this time for custom data properties.
element.portProp('port-id', 'attrs/circle/fill', 'red');
element.portProp('port-id', 'attrs/circle/fill'); // 'red'
element.portProp('port-id', 'attrs/circle', { r: 10, stroke: 'green' });
element.portProp('port-id', 'attrs/circle'); // { r: 10, stroke: 'green', fill: 'red' }
element.position([opt])
If position()
is called without arguments, it returns the current position.
Option | Default | Description |
---|---|---|
parentRelative | false | The method returns the current position of the element relative to its parent. |
element.position(x, y, [opt])
Set the element position to x
and y
coordinates. This is almost equivalent to
element.set('position', { x, y }, opt)
. However, this method provides some additional functionality.
Option | Default | Description |
---|---|---|
parentRelative | false | If set to true the x and y
coordinates will be treated relatively to the parent element of this element. If the element has no parent or the parent is a link, the option is ignored.
|
deep | false | If set to true it will position the element and its descendants while keeping the original distances of the descendants to/from the element origin. The relative distances of the descendants are maintained. |
restrictedArea | null |
If set to a rectangle, the translation of the element will be restricted to that rectangle only.
The restrictedArea is an object of the form { x: Number, y: Number, width: Number, height: Number } .
This is useful, e.g. if you want to restrict the translation of an embedded element within its parent. The only thing
you have to do in this case is to pass the bounding box of the parent element to the restrictedArea option:
The code above makes sure that the element |
el1.position(100, 100);
el1.embed(el2);
el2.position(10, 10, { parentRelative: true });
el2.position() // --> 110@110
el1.position(200,200, { deep: true });
el2.position() // --> 210@210
const restrictedArea = paper.getArea();
el.position(x, y, { restrictedArea }); // --> makes sure x and y is within the area provided
element.prop(properties)
Set properties, possibly nested, on the element model. This is an equivalent of the attr() method but this time for custom data properties.
element.prop('name/first', 'John')
element.prop('name/first') // 'John'
element.prop({ name: { first: 'John' } })
// Nested arrays are supported too:
element.prop('mylist/0/data/0/value', 50)
element.prop({ mylist: [ { data: [ { value: 50 } ] } ] })
To overwrite attributes, enable rewrite mode by adding { rewrite: true }
as the 3rd argument. This differs from the default behaviour which is to merge our properties.
element.prop('custom/state/isCollapsed', true);
element.prop('custom/state', { isActive: false }, { rewrite: true });
// Output from element.toJSON();
// We can see our attributes have been overwritten
{
"type": "standard.Rectangle",
"position": { "x": 0, "y": 0 },
"size": { "width": 1, "height": 1 },
"angle": 0,
"id": "b1c02090-e46a-4d90-a5dc-5096f1559b9f",
"custom": {
"state": {
"isActive": false
}
},
"attrs": {}
}
When changing model attributes via prop()
or attr()
, some useful information is passed along with the
change event in JointJS. propertyPath
, propertyValue
, and propertyPathArray
are all values
which can be accessed when updating the model. This can prove useful if for some reason you need to listen to a specific
attribute change.
graph.on('change', (cell, opt) => {
console.log(opt);
// --> {propertyPath: 'attrs/body/fill', propertyValue: 'cornflowerblue', propertyPathArray: Array(3)}
if ('attrs' in cell.changed) {
console.log(opt.propertyPathArray, 'was changed');
// --> ['attrs', 'body', 'fill'] 'was changed'
}
});
element.prop('attrs/body/fill', 'cornflowerblue');
Advanced: Pass { isolate: true }
if the property change does not affect the connected links. Typically, changing the element fill color has zero effect on attached links. By default, the element and all connected links are updated.
element.prop(['attrs', 'body', 'fill'], 'red', { isolate: true });
element.remove(options)
Remove the element from the graph. All its embedded elements will get removed too and the element gets unembedded from its parent element. By default,
all the associated links are removed too. To suppress this behaviour, set options.disconnectLinks === true
. In this case, all the associated
links get disconnected from this element rather then removed completely from the graph.
element.removeAttr(path, [options])
Remove a previously set attribute from the element. path
can either be a string that specifies
the path to the, possibly nested, attribute to be removed or an array of more paths.
The associated element view makes sure the element gets re-rendered properly.
If options
is passed, it can contain data that is passed over to
the event listeners for the change:attrs
event triggered on the element
itself and also on the graph the element is in.
element.removePort(port, [opt])
Remove a port from an element, where port
is a port object, or portId
.
element.removePorts(ports [, opt])
Remove an array of ports
from the element.
The ports can be specified as Port interfaces or portIds
. The function skips over any ports in the array that do not exist on the element.
element.removePorts([opt])
If no array is provided, the function removes all ports from the element.
element.resize(width, height [, opt])
Resize an element in place so that the "scalable" group has width width
and height height
. In place in this case means
that the top-left corner of the element stays at the same position after resizing. In other words,
the element is streched to the bottom/right (by default). To change the direction of resizing, set
opt.direction
to one of 'left'
, 'right'
, 'top'
, 'bottom'
,
'top-right'
, 'top-left'
, 'bottom-left'
or 'bottom-right'
(the default).
There is a difference between a classical scale and
resize operations. resize
doesn't actually scale the whole SVG <g>
element grouping
all its subelements. It only scales subelements of the <g class"scalable"/>
group. This is very
useful and brings a lot of flexibility in defining which subelements should be scaled and which not. Imagine a simple
rectangle element with text inside. Usually, when we resize the whole element, we expect the rectangle to
get scaled while the text should stay the same size, only its position should be adjusted so that the text stays
in the center of the rectangle. This can be easilly achieved by adding the <rect/>
element to the <g class"scalable"/>
group
in the markup and positioning the text
subelement relatively to the <rect />
element: <text ref-x=".5" ref-y=".5" ref="rect" />
.
Note that neither of ref-x, ref-y and ref
attributes is an SVG standard attribute. These are special
attributes introduced by JointJS. More on these in the section on Special attributes.
element.rotate(deg, [absolute, origin, opt])
Rotate an element by deg
degrees around its center. If the optional absolute
parameter is true
,
the deg
will be considered an absolute angle, not an addition to the previous angle.
If origin
is passed in the form of an object with x
and y
properties,
then this point will be used as the origin for the rotation transformation.
element.scale(sx, sy, origin[, opt])
Scales the element's position and size relative to the given origin.
element.stopTransitions([path])
Stops all running transitions. If parameter path
is provided, it will stop only transitions specified by this path.
element.toBack([opt])
Move the element so it is behind all other cells (elements/links). If opt.deep
is
true
, all the embedded cells of this element will be updated in a Breadth-first
search (BFS) fashion.
If opt.foregroundEmbeds
is true
(as by default), all the embedded
cells will get a higher z
index than that of this element. This is especially useful in hierarchical diagrams where if you want to send an element to the back, you don't want its children (embedded cells) to be hidden behind that element. If
opt.foregroundEmbeds
is false
, the original order within the group is preserved, allowing children to remain behind their parents.
Set opt.breadthFirst
to false
to index the elements using Depth-first search (DFS).
element.toFront([opt])
Move the element so it is on top of all other cells (element/links).
If opt.deep
is true
, all the embedded cells of this element will be updated
in a Breadth-first search (BFS) fashion.
If opt.foregroundEmbeds
is true
(as by default), all the embedded
cells will get a higher z
index than that of this element. This is especially useful
in hierarchical diagrams where if you want to send an element to the front, you don't want its children (embedded cells)
to be hidden behind that element. If opt.foregroundEmbeds
is false
,
the original order within the group is preserved, allowing children to remain behind their parents.
Set opt.breadthFirst
to false
to index the elements using Depth-first search (DFS).
All elements have a z
property defining their z-level in the graph. This z
property
can even be set directly by element.set('z', 123)
. This change will be automatically handled by the joint.dia.Paper
object associated with the joint.dia.Graph
object this element is part of and all the SVG elements will get resorted so that
their position in the DOM reflects the z
level.
container.embed(el1);
container.embed(el2);
container.toFront({ deep: true });
element.toJSON()
Return a copy of the element's attributes for JSON serialization. This can be used for persistance or serialization. Note that this method doesn't return a JSON string but rather an object that can be then serialized to JSON with JSON.stringify()
.
element.transition(path, value [, opt])
Allows to change the element's property gradually over a period of time. This method lets you specify what property to change (path
), when the transition will start (options.delay
), how long the transition will last (options.duration
), how the transition will run (options.timingFunction
), and how to interpolate the property value (options.valueFunction
).
element.transition('position/x', 250, {
delay: 100,
duration: 500,
timingFunction: function(t) { return t*t; },
valueFunction: function(a, b) { return function(t) { return a + (b - a) * t }}
});
// will start changing the element's x-coordinate in 100ms, for period of 500ms.
JointJS comes pre-built with some common timing and interpolating functions. The timing functions are defined in the joint.util.timing
namespace and the interpolating functions in the joint.util.interpolate
namespace. The predefined timing functions are:
linear
quad
cubic
inout
exponential
bounce
and the predefined interpolating functions are:
number
object
hexColor
unit
element.transition('attrs/text/font-size', '1em', {
valueFunction: joint.util.interpolate.unit,
timingFunction: joint.util.timing.bounce
});
// will start changing the current font size value to 1em in the bounce fashion.
element.translate(tx, [ty], [opt])
Translate an element by tx
pixels in x axis and ty
pixels in y axis.
ty
is optional in which case the translation in y axis will be considered zero.
The optional options object opt
can be used to pass additional parameters to the event handlers
listening on 'change:position'
events. opt.transition
can be used to initiate an animated
transition rather than a sudden move of the element. See joint.dia.Element:transition for more info
on transitions. If opt.restrictedArea
is set, the translation of the element will be restricted to that area only.
The restrictedArea
is an object of the form { x: Number, y: Number, width: Number, height: Number }
.
This is useful, e.g. if you want to restrict the translation of an embedded element within its parent. The only thing
you have to do in this case is to pass the bounding box of the parent element to the restrictedArea
option:
myElement.translate(50, 50, { restrictedArea: graph.getCell(myElement.get('parent')).getBBox() })
The code above makes sure that the element myElement
never crosses the bounding box of its parent element.
note that this also works if the element myElement
has other embedded elements. In other words, the
bounding box of the myElement
that is used to calculate the restriction is the total bounding box,
including all its children (in case they are sticking out
).
element.unembed(cell, [opt])
element.unembed(cells, [opt])
Free up an embedded cell or an array of cells from its parent element.
The view for the joint.dia.Element model. It inherits from joint.dia.CellView and is responsible for:
To find the view associated with a specific element (model), use the findViewByModel method of the paper.
var elementView = paper.findViewByModel(element);
elementView.addTools(toolsView)
Add the provided toolsView
(of the joint.dia.ToolsView
type) to the element view.
Adding a tools view to an element view is the last (third) step in the process of setting up element tools on an element view:
// 1) creating element tools
var boundaryTool = new joint.elementTools.Boundary();
var removeButton = new joint.elementTools.Remove();
// 2) creating a tools view
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [boundaryTool, removeButton]
});
// 3) attaching to an element view
var elementView = element.findView(paper);
elementView.addTools(toolsView);
Every element view we want to attach to requires its own tools view object (ToolsView
objects are automatically reassigned to the last element view they are added to). Similarly, every tools view we create requires its own set of tools (ToolView
objects are automatically reassigned to the last toolsView.tools
array they were made part of).
The element tools are added in the visible state. Use the elementView.hideTools
function if this behavior is not desirable (e.g. if you want the element tools to appear in response to user interaction). Example:
elementView.addTools(toolsView);
elementView.hideTools();
paper.on('element:mouseenter', function(elementView) {
elementView.showTools();
});
paper.on('element:mouseleave', function(elementView) {
elementView.hideTools();
});
elementView.findPortNode(portId)
Return the port DOM node of this ElementView that is identified by portId
(i.e. an SVGElement within this.el
which has 'port': portId
, an SVGElement referenced by portRoot
selector).
If the ElementView does not have a port identified by portId
, return null
.
elementView.findPortNode(portId, selector)
Return an SVGElement referenced by selector of the port identified by portId. If there is no port with portId or no SVGElement|HTMLElement was found by the selector, null
is returned.
The available selectors
are defined by the markup
attribute of the port from which the port was built.
elementView.findPortNodes(portId, groupSelector)
Return an array of SVGElement|HTMLElement referenced by groupSelector of the port identified by portId. If there is no port with portId or no SVGElement|HTMLElement was found by the groupSelector, an empty array is returned.
The available groupSelectors
are defined by the markup
attribute of the port from which the port was built.
elementView.getBBox([opt])
Return a bounding box of the element view.
If opt.useModelGeometry
option is set to true
, the resulting bounding box is calculated based on the dimensions of the element model. (This means that SVG sub elements sticking out
of the element are excluded.) This behavior is similar to the element.getBBox
function – but the elementView
function transforms the bounding box to match joint.dia.Paper
translation and scaling.
elementView.getNodeBBox(magnet)
Return the bounding box of the SVGElement provided as magnet
(element/subelement/port of this element view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
elementView.getNodeUnrotatedBBox(magnet)
Return the unrotated bounding box of the SVGElement provided as magnet
(element/subelement/port of this element view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
elementView.hasTools()
Return true
if this element view has a tools view attached.
elementView.hasTools(name)
Return true
if this element view has a tools view of the provided name
attached.
elementView.hideTools()
Hide all tools attached to this element view.
elementView.removeTools()
Remove the tools view attached to this element view.
elementView.showTools()
Show all tools attached to this element view.
joint.dia.Graph
is the model holding all cells (elements and links) of the diagram.
It inherits from mvc.Model.
The collection of all the cells is stored in the property cells
as an mvc.Collection.
The graph is a powerful data model behind all JointJS diagrams. It not only provides efficient storage for directed graphs, but also offers useful algorithms for traversing the graphs.
In order for JointJS to find the correct constructor for your cell, the graph
option cellNamespace
must be provided in
its constructor function when a graph is instantiated. Built-in shapes are usually located in the joint.shapes
namespace, so this is
a common namespace to use. It's possible to add custom shapes to this namespace, or alternatively, you may like to use a different namespace
completely.
For example, if joint.shapes
is provided as the value of cellNamespace
, and a cell is of type 'custom.Element'
,
then the graph looks up the joint.shapes.custom.Element
model when deserializing a graph from JSON format. If the graph is instantiated
as e.g. const graph = new joint.dia.Graph({}, { cellNamespace: myCustomNamespace })
, then the graph
will read the model
definition from the myCustomNamespace.custom.Element
object instead. This option is often used in combination with the
cellViewNamespace
option on the joint.dia.Paper object.
The following list contains events that you can react on:
change
- generic event triggered for any change in the graphadd
- triggered when a new cell is added to the graphremove
- triggered when a cell is removed from the graphgraph.on('add', function(cell) {
alert('New cell with id ' + cell.id + ' added to the graph.')
})
The JointJS graph JSON representation has the following format:
{
cells: [// Array of cells (ie. links and elements).
{
id: '3d90f661-fe5f-45dc-a938-bca137691eeb',// Some randomly generated UUID.
type: 'basic.Rect',
attrs: {
'stroke': '#000'
},
position: {
x: 0,
y: 50
},
angle: 90,
size: {
width: 100,
height: 50
},
z: 2,
embeds: [
'0c6bf4f1-d5db-4058-9e85-f2d6c74a7a30',
'cdbfe073-b160-4e8f-a9a0-22853f29cc06'
],
parent: '31f348fe-f5c6-4438-964e-9fc9273c02cb'
// ... and some other, maybe custom, data properties
}
]
}
graph.addCell(cell[, opt])
Add a new cell to the graph. If cell
is an array, all the cells in the array will be added to the graph.
Any additional option or custom property provided in the options object will be accessible in the callback function of
the graph add event.
If opt.dry
is set to true
, the graph reference is not stored on the cell
after it's added.
If opt.async
is set to false
, this ensures the cell
is rendered synchronously.
If opt.sort
is set to false
, the cell
will be added at the end of the collection.
const rect = new joint.shapes.standard.Rectangle({
position: { x: 100, y: 100 },
size: { width: 90, height: 30 },
attrs: { label: { text: 'my rectangle' } }
});
const rect2 = rect.clone();
const link = new joint.shapes.standard.Link({ source: { id: rect.id }, target: { id: rect2.id } });
const graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
graph.addCell(rect).addCell(rect2).addCell(link);
graph.addCells(cells[, opt])
graph.addCells(cell, cell, ..[, opt])
Add new cells to the graph. This is just a convenience method that wraps the addCell method.
graph.bfs(element, iteratee [, opt])
Traverse the graph using the Breadth-first search algorithm starting at element
(note the element itself will be visited too). iteratee
is a function of the form function(element, distance) {}
that will be called with the currently visited element and distance of that element from the root element of the search (the element
passed to bfs()
). If iteratee
explicitely returns false
, the search stops.
The following image shows the order in which elements are traversed in the graph:
Note that the bfs()
algorithm is not only capable of traversing tree graphs but it can traverse any directed graph too.
It is smart enough not to traverse an element that was already visited.
If opt.inbound
is true
, reverse the search direction (it's like reversing all the link directions, i.e. swaping their source
and target
).
If opt.outbound
is true
, search follows the link directions. Calling bfs()
with opt.outbound
set to true
is the most common case (graph is traversed following the direction of links).
If none of opt.inbound
and opt.outbound
are used or both options are set to true
, the graph is traversed in both directions (very rare use case).
If opt.deep
is true
, the traversal takes into account embedded elements too. This option has the usual meaning as in other methods were deep
option is used. For example, in a hierarchy A (top level element), A1 (embedded in A), B (top level element), where A is not directly connected to B but its embedded element is (there is a link from A1 ----> B), bfs(A)would not visit B but bfs(A, function() {}, { deep: true }) would.
graph.clear([options)
Remove all the cells from the graph. options
object can optionally contain additional data that is passed over to the event listeners of the graph cells remove event.
graph.cloneCells(cells)
Clone all the cells (elements and/or links) from the cells
array and return an
object that maps the original cell ID to the clone (i.e. an object of the form { [original cell ID]: [clone] }
).
The reason why this object is returned instead of an array of clones is that it is very useful to know which object the clone
was created for.
The number of clones returned equals cells.length
. This function does not simply
clone all the cells but it also reconstructs all the source/target and parent/embed references
within cells
. This is very useful. For example, for a graph A --- L ---> B
,
cloneCells([A, L, B])
returns { A.id: A2, L.id: L2, B.id: B2 }
resulting
in a graph A2 --- L2 ---> B2
, i.e. the source and target of the link L2
is changed to point to A2
and B2
(in contrast to just looping over cells
and calling cell.clone()
on each item).
graph.cloneSubgraph(cells [, opt])
Clone the whole subgraph, including all the connected links whose source/target is in the subgraph.
This is equivalent to calling graph.cloneCells(graph.getSubgraph(cells))
.
If opt.deep
is true
, take into account embedded cells of the subgraph cells.
Return an object of the form { [original cell ID]: [clone] }
.
graph.dfs(element, iteratee [, opt])
Traverse the graph using the Depth-first search algorithm starting at element
(note the element itself will be visited too). iterate
is a function of the form function(element, distance) {}
that will be called with the currently visited element and distance of that element from the root element of the search (the element
passed to dfs()
). If iteratee
explicitely returns false
, the search stops.
The following image shows the order in which elements are traversed in the graph:
Note that the dfs()
algorithm is not only capable of traversing tree graphs but it can traverse any directed graph too. It is smart enough not to traverse an element that was already visited.
If opt.inbound
is true
, reverse the search direction (it's like reversing all the link directions, i.e. swaping their source
and target
).
If opt.outbound
is true
, search follows the link directions. Calling dfs()
with opt.outbound
set to true
is the most common case (graph is traversed following the direction of links).
If none of opt.inbound
and opt.outbound
are used or both options are set to true
, the graph is traversed in both directions (very rare use case).
If opt.deep
is true
, the traversal takes into account embedded elements too. This option has the usual meaning as in other methods were deep
option is used. For example, in a hierarchy A (top level element), A1 (embedded in A), B (top level element), where A is not directly connected to B but its embedded element is (there is a link from A1 ----> B), dfs(A) would not visit B but dfs(A, function() {}, { deep: true }) would.
graph.disconnectLinks(element)
Disconnect all the associated links with the element
.
graph.findModelsFromPoint(point)
Find elements (instance of joint.dia.Element
) under a certain point in the graph. point
is an object with x
and y
properties. Returns an
array of elements whose bounding box contains point
. Note that there can be more then one element as elements might overlap.
graph.findModelsInArea(rect)
Find elements (instance of joint.dia.Element
) in a certain area in the graph. rect
is an object with x
, y
, width
and height
properties. Returns an
array of elements whose bounding box top/left coordinate falls into the rect
rectangle.
graph.findModelsUnderElement(element [, opt])
Find all the elements (instances of joint.dia.Element
) that are located below element
.
opt.searchBy
parameter optionally determines what it means for an element to be below another element.
Possible values are 'bbox'
(default), 'center'
, 'origin'
, 'corner'
, 'topRight'
,
and 'bottomLeft'
.
graph.fromJSON(jsonObject, [options])
Load a graph from a JSON object (not string). Used in conjunction with the graph.toJSON()
function.
The options
object may contain additional data that is passed over to graph change event listeners.
Note that this method does not expect a JSON string but rather an object in the JSON format. Use JSON.parse(jsonString)
if you need to convert a JSON string into the object form:
graph.fromJSON(JSON.parse(jsonString));
Example of storing stringified JSON objects:
var jsonString = JSON.stringify(graph.toJSON());
// ... send jsonString to the server
// store jsonString to localStorage or do whatever you want
// later on ...
graph.fromJSON(JSON.parse(jsonString));
Example of storing JSON objects directly:
var jsonObject = graph.toJSON();
// ... send jsonObject to the server
// store jsonObject (e.g. in a non-relational database)
// later on ...
graph.fromJSON(jsonObject)
graph.getBBox()
Returns the bounding box (g.Rect) that surrounds all cells in the graph. It returns null
if the graph is empty.
var bbox = graph.getBBox().inflate(10);
graph.getCell(id)
Get a cell from the graph by its id
.
graph.getCells()
Return an array of all elements and links in the graph. The cells are sorted by their z
index (the smallest z
being first).
graph.getCellsBBox(cells[, opt])
Returns the bounding box (g.Rect) that surrounds all the given cells.
// Get the bounding box of all `el1` successors and their embeds
var bbox = graph.getCellsBBox(graph.getSuccessors(el1), { deep: true });
graph.getCommonAncestor(...cells)
Return the common ancestor of all the cells passed as arguments. For example, if an element B
is embedded in an element A
and an element C
is also embedded in the element A
, graph.getCommonAncestor(B, C)
returns the element A
. This also works on an arbitrary deep hierarchy.
graph.getConnectedLinks(element [, opt])
Get all links connected with element
.
If opt.inbound === true
, return only inbound connected links. Conversely, if opt.outbound === true
, return only outbound connected links. If both of these options are left undefined, or if both of them are set to true
, return both inbound and outbound links.
By default, this function returns only immediate (shallow
) inbound and outbound links - no recursion. (Note that connections from element
to embedded child elements, and connections to element
from embedding parent elements count as shallow
, too - they too are returned.)
If opt.deep === true
, return all outside links that connect with element
or any of its descendants (descendants
meaning elements that are embedded or deeply embedded within element
). The inbound
and outbound
options can still be applied on top of this option.
Note that the specification of opt.deep
excludes links that connect two descendants of element
(enclosed
links). If you do need to find all links connected with and/or enclosed within element
, you should use opt.deep === true
alongside an additional option: opt.includeEnclosed === true
.
If opt.indirect === true
, also return links that can only be considered connected to element
if we go against the flow
of directed links at link-link connections.
Example use:
var links = graph.getConnectedLinks(element); // inbound and outbound
var links = graph.getConnectedLinks(element, { outbound: true });
var links = graph.getConnectedLinks(element, { deep: true }); // inbound and outbound
var links = graph.getConnectedLinks(element, { inbound: true, deep: true });
var links = graph.getConnectedLinks(element, { outbound: true, deep: true, includeEnclosed: true });
var links = graph.getConnectedLinks(element, { indirect: true });
graph.getElements()
Return an array of all elements in the graph. The elements are sorted by their z
index (the smallest z
being first).
graph.getFirstCell()
Get the first cell (element or link) in the graph. The first cell is defined as the cell
with the lowest z
property (the cell most in the back, see the Presentation section of joint.dia.Element).
graph.getLastCell()
Get the last cell (element or link) in the graph. The last cell is defined as the cell
with the highest z
property (the cell most in the front, see the Presentation section of joint.dia.Element).
graph.getLinks()
Return an array of all links in the graph. The links are sorted by their z
index (the smallest z
being first).
graph.getNeighbors(element [, opt])
Get all the neighbors of element
in the graph. Neighbors are all the elements connected to element
via either an inbound or an outbound link.
Accepts several options, which may be provided inside an opt
object:
deep
- also return all the neighbors of all the elements embedded inside element
.inbound
- return only inbound neighbors (neighbors connected with a link whose target
is the element
).outbound
- return only outbound neighbors (neighbors connected with a link whose source
is the element
).indirect
- in addition to standard rules (including deep
/outbound
/inbound
modifications), also return the elements that can only be considered neighbors of element
if we go against the flowof directed links at link-link connections.
graph.getPredecessors(element [, opt])
Return an array of all the predecessors of element
. By default, Depth-first search algorithm is used (important for the order of returned elements).
If opt.breadthFirst
is set to true
, use Breadth-first search algorithm instead.
If opt.deep
is set to true
, take into account embedded elements too (see dfs() for details).
graph.getSinks()
Return an array of all the leafs of the graph.
Time complexity: O(|V|)
.
graph.getSources()
Return an array of all the roots of the graph.
Time complexity: O(|V|)
.
graph.getSubgraph(cells [, opt])
Return an array of cells that result from finding elements/links that are connected to any of the cells in the cells
array.
This function loops over cells
and if the current cell is a link, it collects its source/target elements; if it is an element,
it collects its incoming and outgoing links if both the link ends (source/target) are in the cells
array.
For example, for a single element, the result is that very same element.
For two elements connected with a link: A --- L ---> B
, the result of getSubgraph([A, B])
is [A, L, B]
and the result of getSubgraph([L])
is also [A, L, B]
.
If opt.deep
is true
take into account all the embedded cells too when finding neighboring links/elements.
graph.getSuccessors(element [, opt])
Return an array of all the successors of element
.
By default, a Depth-first search algorithm is used (important for the order of returned elements).
If opt.breadthFirst
is set to true
, use a Breadth-first search algorithm
instead.
Generally, getSuccessors
cares about the direction of the links. It follows links from their source to target only. The
resulting array contains the elements that you visit if you follow the directed links. The links are simply navigated, and embedding
is not considered.
In the following image, the successors of A
are C
and B
. Embedding is not taken into account.
If opt.deep
is set to true
, embedded elements are taken into account too
(see dfs() for details). That means elements connected to any of the descendants are also
successors.
In the following image, if { deep: false }
, the only successor of A
is D
. Embedding is not
taken into account. If { deep: true }
, and B
is embedded in A
, that means the successors of
A
are C
and D
.
graph.isNeighbor(elementA, elementB [, opt])
Return true
if elementB
is a neighbor of elementA
. A neighbor of an element is another element connected to it via an inbound and/or outbound link.
Accepts several options, which may be provided inside an opt
object:
deep
- return true
also if elementB
is a neighbor of an element embedded in elementA
.outbound
- return true
only if elementB
is a succeeding neighbor of elementA
. For example, if elementB
is connected to a directed link behindthe connection of
elementA
.inbound
- return true
only if elementB
is a preceding neighbor elementA
. For example, if elementB
is connected to a directed link ahead ofthe connection of
elementA
.indirect
- in addition to standard rules (including deep
/outbound
/inbound
modifications), also return true
if elementB
can only be considered a neighbor of elementA
if we go against the flowof directed links at link-link connections.
graph.isPredecessor(elementA, elementB)
Return true
if elementB
is a predecessor of elementA
.
graph.isSink(element)
Return true
if element
is a leaf, i.e. there is no link coming out of the element.
Time complexity: O(1)
.
graph.isSource(element)
Return true
if element
is a root, i.e. there is no link that targets the element.
Time complexity: O(1)
.
graph.isSuccessor(elementA, elementB)
Return true
if elementB
is a successor of elementA
.
graph.maxZIndex()
Get the highest Z value in the graph (the value of the cell on front).
graph.minZIndex()
Get the lowest Z value in the graph (the value of the cell on the back).
graph.removeCells(cells[, opt])
graph.removeCells(cell, cell, ..[, opt])
Removes the given cells from the graph.
graph.removeLinks(element)
Remove all the associated links with the element
.
graph.resetCells(cells[, opt])
graph.resetCells(cell, cell, ..[, opt])
Reset cells in the graph. Update all the cells in the graph in one bulk. This is a more efficient method of adding cells to the graph if you want to replace all the cells in one go. The options
object can optionally contain additional data that is passed over to the event listeners of the graph reset event.
graph.toJSON()
Return an object representation of the graph, which can be used for persistence or serialization. Use the graph.fromJSON()
function to load a previously converted graph.
Note that this method does not return a JSON string but rather an object that can then be serialized to JSON with JSON.stringify(jsonObject)
:
var jsonString = JSON.stringify(graph.toJSON());
graph.translate(tx, ty [, opt])
Translate all cells in the graph by tx
and ty
pixels. It uses the dia.Element.translate()
and dia.Link.translate()
methods internally.
The base class for highlighters. The HighlighterView class does not implement any particular visual emphasis. It takes care of the management of the instances and introduce mechanisms for displaying, updating and cleaning arbitrary markings added to the SVGElements of cellViews.
The class is not meant to be initialized with the constructor.
HighlighterView.add(cellView, selector, id, options)
Create an instance of the highlighter and add it to the cellView.
It returns an instance of the HighlighterView.
If a highlighter with the same id exists on the CellView, it is removed and a new highlighter is added and returned. You can check if a highlighter already exists by calling HighlighterView.get().
Type | Description | Example |
---|---|---|
String | A selector from the cell's markup. |
|
Object | An object with property selector, which is a selector from the (cell/label/port) markup. There is additional property port (id of a port) available for dia.ElementView. And property label (index of a label) is available for dia.LinkView. If no selector property is provided, the root is used. |
|
SVGElement | An SVGElement, the descendant of cellView.el or the root itself. |
|
HighlighterView.get(cellView)
Return an array of highlighters on the cellView. Only highlighters, which are instances of this class are returned.
// Extend the Base Highlighter Class
const ChildHighlighterView = joint.dia.Highlighter.extend({});
const h1 = joint.dia.HighlighterView.add(cellView, 'root', 'id1');
const c1 = ChildHighlighterView.add(cellView, 'root', 'id2');
const parentHighlighters = joint.dia.Highlighters.get(cellView);
assert.ok(parentHighlighters.includes(h1));
assert.ok(parentHighlighters.includes(c1));
const childHighlighters = ChildHighlighterView.get(cellView);
assert.notOk(parentHighlighters.includes(h1));
assert.ok(parentHighlighters.includes(c1));
HighlighterView.get(cellView, id)
Return the highlighter with given id on the cellView. The highlighter returned must be an instance of this class. If such a highlighter does not exist, null
is returned.
// Extend the Base Highlighter Class
const ChildHighlighterView = joint.dia.Highlighter.extend({});
const h1 = joint.dia.HighlighterView.add(cellView, 'root', 'h1');
const c1 = ChildHighlighterView.add(cellView, 'root', 'c1');
assert.equal(h1, joint.dia.Highlighters.get(cellView, 'h1));
assert.equal(c1, joint.dia.Highlighters.get(cellView, 'c1));
// There is no instance of ChildHighlighterView on the CellView
assert.equal(null, ChildHighlighterView.get(cellView, 'h1));
assert.equal(c1, ChildHighlighterView.get(cellView, 'c1));
HighlighterView.remove(cellView)
Remove all the highlighters from the cellView. Only highlighters, which are instances of this class are removed.
HighlighterView.remove(cellView, id)
Remove the highlighter with given id from the cellView. The highlighter which is not an instance of this class is not removed.
HighlighterView.removeAll(paper)
Remove all the highlighters on paper. Only highlighters, which are instances of this class are removed.
HighlighterView.removeAll(paper, id)
Remove all the highlighters with given id on the paper. Only highlighters, which are instances of this class are removed.
highlighter.highlight(cellView, node)
Mark/Emphasize the node (SVGElement) of the cellView. e.g. render a rectangle above the
HighlighterView.prototype.MOUNTABLE
If the property is true, the highlighter view is attached to the DOM.
If the property is false, it is not attached upon highlight.
The default is true.
const ChildView = joint.dia.HighlighterView.extend({
// HighlighterView `el` is not meant to be appended
MOUNTABLE: false,
highlight(cellView, node) {
node.setAttribute('my-attribute', String(cellView.model.get('highlightAttribute')));
},
unhighlight(cellView, node) {
node.removeAttribute('my-attribute');
}
});
highlighter.unhighlight(cellView, node)
Remove marks made by highlight() from the node (SVGElement) of the cellView.
HighlighterView.prototype.UPDATABLE
If the property is true, the highlighter is updated (highlight() function is called) every time when the related cellView requires an update.
If the property is false, the highlight() method is called only once when the highlighter is added.
The default is true.
const ChildView = joint.dia.HighlighterView.extend({
MOUNTABLE: false,
UPDATABLE: false,
highlight(_cellView, node) {
node.classList.add('my-class');
},
unhighlight(_cellView, node) {
node.classList.remove('my-class');
}
});
HighlighterView.prototype.UPDATE_ATTRIBUTES
The highlighter is updated (highlight() function is called) every time any of the attributes from the list change.
The value could be an array of strings or a function returning an array of strings.
The default is [].
const ColorHighlighter = joint.dia.HighlighterView.extend({
UPDATE_ATTRIBUTES: ['color'],
highlight(cellView, node) {
node.style.fill = cellView.model.get('color');
},
unhighlight(_cellView, node) {
node.style.fill = '';
}
});
ColorHighlighter.add(rectangle.findView(paper), 'body', 'color-highlighter');
rectangle.set('color', 'gray'); // will call highlight()
layer - the stacking context of the highlighter. Applicable for mountable highlighters only.
null |
Render the highlighter above the cell. It can be hidden by a cell with a higher z index. |
"back" |
Render the highlighter behind all the cells. |
"front" |
Render the highlighter in front of all the cells. |
z - the stacking order (z-index) of the highlighter in the given stacking context. Applicable for mountable highlighters only.
The basic model for diagram links. It inherits from joint.dia.Cell with a few additional properties and methods specific to links. For a quick introduction to elements, see our tutorial.
Links' properties may be split into several groups according to their function:
Links have two crucial properties: source
and target
. They define the starting point and the end point of the link. They can be defined with a Cell id (optionally, with additional subelement/magnet/port reference) or with a Point:
// `shapes.standard.Link` inherits from `dia.Link` (`dia.Link` is an abstract class that has no SVG markup defined)
var link1 = new joint.shapes.standard.Link({
source: { id: sourceId },
target: { id: targetId, port: portId }
});
var link2 = new joint.shapes.standard.Link({
source: { id: sourceId },
target: { x: 100, y: 100 }
});
The source
and target
properties accept additional modifier properties that modify the actual position of the link end: anchor
/linkAnchor
, and connectionPoint
.
Additionally, the path of the link is determined by its vertices
, and the applied router
and connector
. All these properties are described in more detail in link geometry documentation.
Each joint.dia.Link
defines its own SVG markup
which is then used by joint.dia.LinkView
to render the link to the paper.
For instance, the joint.shapes.standard.Link
(which inherits from joint.dia.Link
) defines its markup using the JSON array notation as follows:
markup: [{
tagName: 'path',
selector: 'wrapper',
attributes: {
'fill': 'none',
'cursor': 'pointer',
'stroke': 'transparent',
'stroke-linecap': 'round'
}
}, {
tagName: 'path',
selector: 'line',
attributes: {
'fill': 'none',
'pointer-events': 'none'
}
}]
As we can see, the joint.shapes.standard.Link
shape consists of two subelements
: one SVGPathElement named 'wrapper'
and one SVGPathElement named 'line'
. The attrs
object refers to the subelements' names (selectors
) to provide SVG attributes to these constituent SVGElements.
The keys of the attrs
object are selectors
that match subelements defined in the link's markup
(see above). The values of this object are special JointJS attributes or native SVG attributes that should be set on the selected subelements. (A list of native SVG attributes and their descriptions can be found online, e.g. on MDN.)
For example, in order to set a red stroke color on a subelement called 'line'
, the attrs
object would contain:
line: { stroke: 'red' }
If you simply need to change a value of an attribute, it is not recommended to modify the attrs
object of the link directly. You should use the link.attr()
method instead. For example, to set the attributes according to the above example, you would write:
link.attr({
line: { stroke: 'red' }
});
We can use the joint.shapes.standard.Link
type (which inherits from joint.dia.Link
) as an example. The attrs
object in its definition is provided below:
attrs: {
line: {
connection: true,
stroke: '#333333',
strokeWidth: 2,
strokeLinejoin: 'round',
targetMarker: {
'type': 'path',
'd': 'M 10 -5 0 0 10 5 z'
}
},
wrapper: {
connection: true,
strokeWidth: 10,
strokeLinejoin: 'round'
}
}
Notice that the object makes use of special JointJS attributes (e.g. connection
, targetMarker
) on top of native SVG attributes (e.g. stroke
, strokeWidth
). All of these special attributes
are listed in the attributes section of this documentation. You should also refer to our tutorial on special attributes.
In the context of links, the most important special attribute is connection
. It specifies that the SVGPathElement(s) in question should follow the path of the Link's model, as provided by the interplay of link geometry methods.
Attributes defined directly inside link markup
are evaluated only once at CellView creation, while attributes defined in the attrs
object are evaluated on every model change. JointJS special attributes usually depend on the current state of the model, which means that they should be defined in the attrs
object (alongside any SVG attributes that you expect to be modified during the runtime of your application).
The z
property specifies the stack order of the element in the SVG DOM. An element with a higher z
value will be rendered in front of an element with a lower z
value. (This also applies to Elements.)
You may provide an array of labels to the link through the labels
property. Each label can have its own markup
, size
, attrs
, and position
objects specified. The values in those objects take precedence over any defaults which may apply on the label.
To avoid excessive repetition, you can provide a defaultLabel
property to the link, to set the markup
, size
, attrs
, and position
objects which should be applied to all labels on the link. The properties from defaultLabel
act as a template, which is overwritten / extended by individually-specified label properties as appropriate.
An example of creating a new link instance with both the defaultLabel
and labels
properties specified can be seen below. For more details, see the link labels documentation. You should also refer to our tutorial on link labels.
const link = new joint.shapes.standard.Link({
source: { x: 50, y: 400 },
target: { x: 500, y: 400 },
defaultLabel: {
// applied to all labels on this link:
markup: [
{
tagName: 'rect',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}
],
size: {
// used by `calc()` expressions in `attrs`
width: 150,
height: 30
},
attrs: {
body: {
width: 'calc(w)',
height: 'calc(h)',
// center around label position:
x: 'calc(w/-2)',
y: 'calc(h/-2)',
stroke: 'black',
fill: 'white'
},
label: {
textWrap: {
width: 'calc(w-5)',
height: 'calc(h-5)'
},
// center text around label position:
// (no `x` and `y` provided = no offset)
textAnchor: 'middle',
textVerticalAnchor: 'middle',
fontSize: 16,
fontFamily: 'sans-serif'
}
}
},
labels: [{
// specification of an individual label:
size: { width: 200 }, // partially overwrites `defaultLabel.size`
attrs: {
label: {
text: 'Hello World'
}
},
position: { distance: 0.25 } // overwrites built-in default
}]
});
It is also possible to pass custom properties to the link. These may be useful to identify an individual link model for the purposes of linkView interaction (see LinkView
documentation for more information).
For example, if a custom contextmenu interaction should only be enabled for link1
but not link2
, we could add a custom property customLinkInteractions
to link1
:
var CustomLinkView = joint.dia.LinkView.extend({
contextmenu: function(evt, x, y) {
if (this.model.get('customLinkInteractions')) {
// only links with `customLinkInteractions: true`
this.addLabel(x, y);
}
}
});
var paper = new joint.dia.Paper({
//...
linkView: CustomLinkView,
interactive: function(cellView) {
if (cellView.model.get('customLinkInteractions')) {
// only links with `customLinkInteractions: true`
return true;
}
return { labelMove: false }; // otherwise
}
});
var link1 = new joint.shapes.standard.Link({
//...
customLinkInteractions: true // right-click adds a label
});
var link2 = new joint.shapes.standard.Link({
//...
customLinkInteractions: false // or omit completely
});
Links trigger several special events, detailed in the link events documentation.
It is possible to extend the joint.dia.Link
class to create a custom link. A custom link may override default Link properties to assign its own defaults. These values override built-in defaults, if necessary, and are applied to all instances of the new Link type, unless an individual instance overrides them with its own values. The following Link properties are applicable in this context:
markup
- provide default link markup for all instances of this Link type, specified with a JSON array.attrs
- provide default link attributes for all instances of this Link type. These allow you to change the style and size of SVG elements, identified by their selectors.defaultLabel
- provide default properties (markup, size, attrs, position) for all labels created on an instance of this Link type.The values of these defaults may be important; the linkView.addLabel()
shortcut function is only capable of adding default labels to the link.
Creating custom links is explained in more detail in our tutorial.
Example:
var CustomLink = joint.dia.Link.define('examples.CustomLink', {
defaultLabel: {
markup: [
{
tagName: 'circle',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}
],
size: {
// used by `calc()` expressions in `attrs`
width: 20,
height: 20
},
attrs: {
label: {
text: '%', // default text for all labels
fill: '#ff0000', // default text color for all labels
fontSize: 14,
textAnchor: 'middle',
textVerticalAnchor: 'middle',
pointerEvents: 'none'
},
body: {
// currently, calc() is responsive to `size` property
// uncomment to make calc() responsive to size of 'label':
//ref: 'label', // subelement identified by 'label' selector
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 1,
r: 'calc(d/2 + 2)'
}
},
position: {
// keep built-in default `distance` for all labels (0.5)
offset: {
y: -20 // offset by 20px upwards for all labels
},
args: {
absoluteOffset: true // absolute offset for all labels
}
}
}
});
var link = new CustomLink({
//...
});
To ensure backwards compatibility, the joint.dia.Link
class comes with a private built-in defaultLabel
property. It is reproduced here for reference:
defaultLabel: {
// built-in default markup:
// applied only if neither one of the following is provided:
// - individual label `markup` property
// - `defaultLabel.markup` property
markup: [
{
tagName: 'rect',
selector: 'rect'
}, {
tagName: 'text',
selector: 'text'
}
],
// built-in default attributes:
// applied only if built-in default markup is used
attrs: {
text: {
fill: '#000000',
fontSize: 14,
textAnchor: 'middle',
textVerticalAnchor: 'middle',
pointerEvents: 'none'
},
rect: {
ref: 'text',
fill: '#ffffff',
rx: 3,
ry: 3,
x: 'calc(x)',
y: 'calc(y)',
width: 'calc(w)',
height: 'calc(h)'
}
},
// built-in default position:
// merged with `defaultLabel.position` and individual label `position`
position: {
distance: 0.5
}
}
If custom markup
object is not provided (i.e. there is no class-specific defaultLabel.markup
object, nor any instance-specific defaultLabel.markup
, nor an individual label-specific markup
property), then built-in default label markup
is applied (as reproduced above). Alongside, the built-in default label attrs
object is applied. Note that the built-in default attrs
object is applied as a template in this context, which means that you may enhance it with a custom attrs
object (class-specific / instance-specific / individual label-specific). However, in the interest of keeping your code maintainable and easy to understand, it is very highly recommended that you provide both your own markup
object and your own attrs
object, unless you want to use the built-in default precisely as-is.
The built-in default position
object behaves slightly differently. Regardless of markup
, it is always merged with custom position
objects (class-specific / instance-specific / individual label-specific) - but it has the lowest priority of the four. That is, if at least one of the custom position
objects provides a distance
value, that value will have precedence over the built-in default position.distance
. If no custom position.distance
is provided, then the built-in default is applied (placing labels at midpoints of links).
change
- generic event triggered for any change on the linkchange:source
- triggered when the link changes its sourcechange:target
- triggered when the link changes its targetchange:attrs
- triggered when the link changes its attributeschange:connector
- triggered when the link changes its connector change:router
- triggered when the link changes its routerchange:vertices
- triggered when the link changes its vertices array change:z
- triggered when the link is moved in the z-level (toFront and toBack)transition:start
- triggered when a transition starts.transition:end
- triggered when a transition ends.link.on('change:source', function() { alert('source of the link changed') })
The shape of a link is determined by five properties - source
, target
, vertices
, router
and connector
.
The source
and target
properties have to be provided when creating a Link. Either a Point can be provided (with x
and y
properties) or a Cell (Element or Link, either directly or via an id
property).
link.source(new g.Point(100, 100));
link.source({ x: 100, y: 100 });
link.source(rect);
link.source({ id: rect.id });
link.source(link2);
link.source({ id: link2.id });
If the end is specified as a Cell, a specific subelement on that cell may be identified for use as the link end. The selector
property allows specifying the subelement with a selector string, while the magnet
property uses magnet id, and the port
property (on Elements only) uses the port id.
link.source(rect, {
selector: 'connectorSquare'
});
link.source(link2, {
selector: 'midPointCircle'
});
In any case, we need to obtain a single point from the object provided to the source/target property. That point is found according to additional properties provided to the function. The accepted modifier properties depend on the class of the provided object:
Point
or an object with x
and y
properties - coordinates of the point are used as the coordinates of the link's anchor directly. No modification of any kind. (Ignores anchor/linkAnchor/connectionPoint properties, if provided.)Element
- the precise position of the link's end anchor depends on used anchor
function, with additional optical modifications applied by connectionPoint
function. (Ignores linkAnchor property, if provided.)Link
- the precise position of the link's end anchor depends on provided linkAnchor
function. (Ignores anchor/connectionPoint properties, if provided.)Link
with specified subelement
/magnet
property - same as Element
(see above).The connectionStrategy
paper option is also relevant to mention in this context. It determines what happens to the link end when it is modified due to specific kinds of user interaction.
If the link end (source/target) is an Element (or a subelement/magnet of a Link – but not a Link itself), the precise position of the link end's anchor may be specified by the anchor
property. Every link has two anchors; one at the source end and one at the target end.
A link end anchor is a point inside a given (sub)element that the link path wants to connect to at that source/target end - if it were not obstructed by the body of the element itself (it is the role of connectionPoints to then take the obstructing end element itself into account). Alongside link vertices, source and target anchors determine the basic link path. Then, it is the job of connectionPoints, routers, and connectors to modify that path to make it look good.
The anchor functions reference the end element to find the anchor point - e.g. the center of the end element, or its top-right corner. Notably, anchor functions also allow you to offset found anchor points by a custom distance in both dimensions from the standard position (e.g. 10 pixels to the right and 20 pixels below the center of an end element). Several pre-made anchors are provided inside the JointJS library in the joint.anchors
namespace.
link.source(rect, {
anchor: {
name: 'bottomLeft',
args: {
dx: 20,
dy: -10
}
}
});
If an anchor function is not provided, the defaultAnchor
paper option is used instead. The joint.anchors.center
function is used by default.
If the link end (source/target) is a Link (not an Element or a Link subelement/magnet), the precise position of the link end's anchor may be specified by the linkAnchor
property. If a link anchor method is used, no connectionPoints are applied.
A link end link anchor is a point on another link that this link's path wants to connect to at that source/target end. Alongside link vertices, source and target anchors determine the basic link path.
The link anchor functions reference the end link to find the anchor point - e.g. a point at a given ratio from the start, or the closest point. Several pre-made linkAnchors are provided inside the JointJS library in the joint.linkAnchors
namespace.
link.source(link2, {
linkAnchor: {
name: 'connectionRatio',
args: {
ratio: 0.25
}
}
});
If a link anchor function is not provided, the defaultLinkAnchor
paper option is used instead. The joint.linkAnchors.connectionRatio
function with a value of 0.5
is used by default (i.e. the anchor is placed at the midpoint of a Link by default).
The link end's connection point may be specified by the connectionPoint
property. Every link has two connection points; one at the source end and one at the target end.
A link connection point is the point at which the link path actually ends at, taking the end element into account. This point will always lie on the link path (the line connecting link anchors and vertices together, in order).
The connectionPoints are found by considering intersections between the link path and a desired feature of the end element (e.g. bounding box, shape boundary, anchor). Although connectionPoints are not capable of being offset off the link path (anchors should be used to modify the link path if this is required), they can be offset along the path - e.g. to form a gap between the element and the actual link. Several pre-made connectionPoints are provided in the JointJS library in the joint.connectionPoints
namespace.
link.source(rect, {
connectionPoint: {
name: 'boundary',
args: {
offset: 5
}
}
});
If a connection point function is not provided, the defaultConnectionPoint
paper option is used instead. The joint.connectionPoints.bbox
function is used by default.
Related to anchors and connectionPoints is the connectionStrategy
paper option. It allows you to specify which anchor
and connectionPoint
properties should be assigned to end elements in response to user interaction (e.g. in response to dragging a link arrowhead onto a new element). This setting is necessary because assigned anchor
and connectionPoint
are not preserved when the end element is reassigned by the user - and the paper's defaultAnchor
and defaultConnectionPoint
are used instead.
Several pre-made connectionStrategies are provided inside the JointJS library in the joint.connectionStrategies
namespace, but you may find it necessary to create your own custom connection strategies. This is a paper-level setting; connectionStrategies not assigned on a link-by-link basis.
paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;
If a connection strategy is not provided, the joint.connectionStrategies.useDefaults
function is used by default.
The vertices
array is an array of user-defined points for the link to pass through. Alongside the source and target anchors, the link vertices determine the basic link path. This skeleton path is then used for determining the link route. The vertices can be accessed with the link.vertices()
function and related functions.
link.vertices();
link.vertices([{ x: 100, y: 120 }, { x: 150, y: 60 }]);
Routers take an array of link vertices and transform them into an array of route points that the link should go through. This route is then used for generating the connection SVG path commands. The router
property of a link can be accessed with the link.router()
function.
A collection of pre-made routers is provided inside the JointJS library in the joint.routers
namespace. This includes smart routers
that are able to automatically avoid obstacles (elements) in their way.
link.router('manhattan', {
excludeEnds: ['source'],
excludeTypes: ['myNamespace.MyCommentElement'],
startDirections: ['top'],
endDirections: ['bottom']
});
If a router is not provided, the defaultRouter
paper option is used instead. The joint.routers.normal
function is used by default.
Connectors take an array of link route points and generate SVG path commands so that the link can be rendered. The connector
property of a link can be accessed with the link.connector()
function.
A collection of pre-made connectors is provided inside the JointJS library in the joint.connectors
namespace.
link.connector('rounded', {
raw: true,
radius: 20
});
If a connector is not provided, the defaultConnector
paper option is used instead. The joint.connectors.normal
function is used by default.
Note that the modular architecture of JointJS allows mixing-and-matching connectors with routers as desired; for example, a link may be specified to use the jumpover
connector on top of the manhattan
router:
var link = new joint.shapes.standard.Link();
link.source(rect);
link.target(rect2);
link.router('manhattan');
link.connector('jumpover');
JointJS supports adding labels on links. One link can have multiple labels, and each label can have different properties. For a quick introduction to link labels, see our tutorial
Properties recognized by JointJS are summarized in the following TypeScript-like schema:
{
markup?: string | Array<{
tagName: SVGElement,
selector?: string
}>,
size?: {
width?: number,
height?: number
},
attrs?: {
[key: selector]: {
[key: SVG attribute | JointJS attribute]: any
} | null
},
position?: number | {
distance: number,
offset?: number | { x: number, y: number },
angle?: number,
args?: {
absoluteDistance?: boolean,
reverseDistance?: boolean,
absoluteOffset?: boolean,
keepGradient?: boolean,
ensureLegibility?: boolean
}
}
}
The markup
, size
, attrs
and position
objects defined on individual labels may be supplemented by properties from one of a hierarchy of defaultLabel
objects, each of them following the same schema as above.
defaultLabel
object provided when creating a new link instance.defaultLabel
object provided when defining a custom Link type.defaultLabel
object, if any.If a defaultLabel
object is found in one of the above sources, its markup
, size
, attrs
and position
properties act as a template into which the individually-specified label properties are deep-merged as appropriate. In addition, the properties of the highest-precedence defaultLabel
object may prove to be important on their own; for example, the linkView.addLabel()
shortcut function is only capable of adding a default label to links.
The markup
property specifies the markup of the label. It can be provided in three ways:
'<rect /><text />'
).[{ tagName: 'rect', selector: 'body' }, { tagName: 'text', selector: 'label' }]
).joint.util.svg
ES6 tag template:joint.util.svg`
<rect @selector="body"/>
<text @selector="label"/>
`
Note that the latter two options allow the user to specify custom selectors for the individual SVGElements; these can then be used for targeting elements within the attrs
property.
If no markup
property is provided on the individual label, but a defaultLabel
is found on the link according to the defaultLabel
hierarchy, then the label markup is taken from the defaultLabel.markup
property.
However, if no markup
property is provided on the individual label, and no defaultLabel
is found on the link according to the defaultLabel
hierarchy (as may be the case when extending from out-of-the-box Link types like joint.dia.Link
or joint.shapes.standard.Link
without providing one's own defaultLabel
object), then built-in default Link markup is used to maintain backwards compatibility. That object defines markup
as a JSON array with a <rect>
SVGElement ('rect'
selector) under a <text>
SVGElement ('text'
selector).
The size
property specifies the dimensions of the label. It may be defined as an object with a width
and/or height
properties. These values are then used as the reference size for all calc()
attribute expressions within the link's attrs
object (this may be overwritten for an individual subelement with the ref
property to choose a different subelement as a reference).
If a defaultLabel
object was found on the link (according to the defaultLabel
hierarchy), then the label's own size
properties are deep-merged into it as appropriate.
There is no built-in defaultLabel.size
object; if no defaultLabel
is found, and no size
is provided on the individual label, then no size
is assigned to that label. (That causes calc()
attribute expressions to assign 0
as the value of all their variables - which may be unexpected - unless the expression is used alongside a ref
property.)
object |
|
The attrs
property is an object where the keys are CSS selectors (referring to custom selectors or SVGElements specified in markup
- e.g. the body
selector in the above markup example). They are expected to contain objects that specify native SVG attributes and/or JointJS special attributes (e.g. fill
), alongside the value to be assigned (e.g. 'white'
).
If a defaultLabel
object was found on the link (according to the defaultLabel
hierarchy), then the label's own attrs
properties are deep-merged into it as appropriate. For example, this may prove useful when you just need to change a handful of attributes for one specific label (e.g. attrs: { body: { stroke: 'black' } }
). (Note that selectors defined in defaultLabel.markup
can also be referenced from within a specific label's attrs
.)
If the built-in default markup is applied on an individual label (i.e. no markup
property was provided, and no defaultLabel.markup
was found), then several additional built-in default attributes need to be automatically applied for reasons of backwards compatibility. The individual label's attrs
(if any) are deep-merged with defaultLabel.attrs
(if found) as appropriate, and the resulting object is then deep-merged with the built-in default attrs
as appropriate. (Note that this is the only case in which the absence of one property - markup
/defaultLabel.markup
- influences another property - attrs
/defaultLabel.attrs
.)
Finally, the position
property specifies the position of the label relative to the SVG path of the link. It may be defined as a number or as an object with distance
and optionally offset
and args
properties.
The built-in default position ({ distance: 0.5 }
) is used to maintain backwards compatibility. The individual label's position
(if any) is deep-merged with defaultLabel.position
(if found in the defaultLabel
hierarchy) as appropriate, and the resulting object is then deep-merged with the built-in default position
as appropriate. (Note that the built-in default object is always merged in with the other objects, which is a difference from how the built-in defaults of markup
and attrs
properties are treated.)
number |
|
||||||||||||||||||||||||||||
object |
If
|
link.addTo(graph)
Add the link to the graph
(an instance of joint.dia.Graph
). This is equivalent to calling graph.addCell(link)
.
link.appendLabel(label [, opt])
Add a new label
at the end of the labels
array.
link.attr(attrs, [, opt])
Set presentation attributes (SVG and JointJS attributes) on subelements. This is a method analogous to attr method of joint.dia.Element
. The keys of the attrs
object are selectors (JSON Markup Selector or CSS Selector) matching the SVG element the link consists of. The values are objects containing SVG attributes and their values. attrs
object will be mixed with attrs
property of the link
model. This is a convenient way of rewriting only some of the attributes of the SVG elements. For overwriting all attributes of all SVG elements, use link.set('attrs', attrs)
.
link.attr({
// selector as defined in JSON markup
line: {
stroke: 'red',
targetMarker: {
type: 'circle',
r: 5
}
}
});
element.attr(path, value [, opt])
An alternative call using a string path and a value:
link.attr('line/stroke', 'red');
link.attr(['line', 'targetMarker', 'type'], 'path');
element.attr([path])
Get attribute value defined by a path. If no path provided the whole attrs
object is returned.
var color = link.attr('line/stroke');
var targetMarkerType = link.attr(['line', 'targetMarker', 'type']);
link.clone()
Returns a new instance of the link with identical attributes.
link.connector()
Return a shallow copy of the connector
property of the link.
link.connector(connector [, opt])
Set the connector
of the link.
If the connector
argument is an object, it is expected to have the form { name: connectorName, args?: connectorArgs }
. Here connectorName
is expected to match either the name of a built-in connector or the name of a custom connector.
If the connector
argument is a function, it is expected to define a custom connector with the signature function(sourcePoint, targetPoint, vertices, connectorArgs, linkView)
that returns a string representing the SVG path data that will be used to render the link.
link.connector(connectorName [, connectorArgs, opt])
Set the connector
of the link to have the value { name: connectorName, args: connectorArgs }
.
The connectorName
string is expected to match either the name of a built-in connector or the name of a custom connector. The connectorArgs
parameter is optional.
link.disconnect()
Disconnect the link from its source
and target
elements. The source
and target
then become a point at [0,0]
.
link.findView(paper)
Find view (joint.dia.LinkView
) for the link model in the paper
. This is a shortcut to the equivalent call paper.findViewByModel(link)
.
link.getAncestors()
Return an array of all the ancestors of this link starting from the immediate parent all the way up to the most distant ancestor.
link.getBBox()
Return the bounding box of the link model, represented as a g.Rect
.
This method creates the bounding box from the link polyline (see the link.getPolyline
function). This simplified heuristic may cause portions of curved links to protrude out of the reported bounding box. See the documentation of the linkView.getBBox
function for details about the differences.
link.getPolyline()
Return a polyline that approximates the shape of the link. The polyline is created from the two link endpoints, with link vertices in between.
link.getSourceCell()
Return the source cell (element or link) of the link, or null
if the element's source is a point.
link.getSourceElement()
Return the source element of the link or null
if there is none.
link.getSourcePoint()
Return the source point of the link.
link.getTargetCell()
Return the target cell (element or link) of the link, or null
if the element's target is a point.
link.getTargetElement()
Return the target element of the link or null
if there is none.
link.getTargetPoint()
Return the target point of the link.
link.getTransitions()
Return an array of all active transitions (their paths).
link.hasLabels()
Return true
if the link has at least one label. Return false
otherwise.
link.hasLoop([opt])
Return true
if this link is a loop link
. In a loop link
source
and target
are equal.
If opt.deep
is true
, the notion of a loop link
is extended to a deep hierarchy. For example, if the link connects a parent element with one of its embedded elements, the link is considered a loop link
, as well.
link.insertLabel(index, label [, opt])
Add a new label
at index
. Pass index -1
to add the label at the end of the labels array.
link.insertVertex(index, vertex [, opt])
Add a new vertex
at index
. Pass index -1
to add the vertex at the end of the vertices array.
link.isElement()
Always returns false
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isElement()
is equivalent to cell instanceof joint.dia.Element
. Example:
var cell = graph.getCell(myId)
if (cell.isElement()) {
// Do something if the cell is an element.
}
link.isEmbeddedIn(element [, opt])
Return true
if the link is embedded in an element element
.
If opt.deep
is false
, only direct parentage will be checked. opt.deep
is true
by default.
link.isLink()
Always returns true
. The reason the method is here is that both joint.dia.Element
and joint.dia.Link
inherit from joint.dia.Cell
. This method is useful if you don't know what the cell is. Calling cell.isLink()
is equivalent to cell instanceof joint.dia.Link
. Example:
var cell = graph.getCell(myId)
if (cell.isLink()) {
// Do something if the cell is a link.
}
link.label(index)
Return the label at index
.
link.label(index, properties [, opt])
Update properties
of the label at index
. By default, the new properties are merged into the old ones; pass the { rewrite: true }
option along to disregard old properties.
Example usage:
link.label(0, {
markup: [
{
tagName: 'rect',
selector: 'body'
}, {
tagName: 'text',
selector: 'label'
}
],
attrs: {
body: {
fill: 'white' // white background
},
label: {
text: 'my label', // text to show
fill: 'blue' // blue text
}
},
position: {
distance: 0.5, // midway on the connection path
offset: {
x: 10, // 10 local x units to the right
y: -5 // 5 local y units above
},
angle: 45, // rotate by 45 degrees clockwise
args: {
keepGradient: true, // auto-rotate by path slope at distance
ensureLegibility: true // auto-rotate label if upside-down
}
}
});
Note that all labels are stored in an array on the link model under the attribute labels
. Use the link.labels
function to access the array.
link.labels()
Return a shallow copy of labels array.
link.labels(labelsArray [, opt])
Set array of labels on the link.
link.prop(properties)
Set properties, possibly nested, on the element model. This is equivalent to the attr() method but this time for custom data properties.
link.prop('name/first', 'John')
link.prop('name/first') // 'John'
link.prop({ name: { first: 'John' } })
// Nested arrays are supported too:
link.prop('mylist/0/data/0/value', 50)
link.prop({ mylist: [ { data: [ { value: 50 } ] } ] })
As you can see, this is the exact same method as the joint.dia.Element.prop() method.
To overwrite attributes, enable rewrite mode by adding{ rewrite: true }
as the 3rd argument. This differs from the default behaviour which is to merge our properties.
link.prop('custom/state/isVisible', true);
link.prop('custom/state', { isActive: false }, { rewrite: true });
// Output from link.toJSON();
// We can see our attributes have been overwritten
{
"type": "link",
"source": { "x": 10, "y": 10 },
"target": { "x": 100, "y": 100 },
"id": "4ddf9c16-649b-40b3-96b3-7150711da955",
"custom": {
"state": {
"isActive": false
}
},
"attrs": {}
}
When changing model attributes via prop()
or attr()
, some useful information is passed along with the
change event in JointJS. propertyPath
, propertyValue
, and propertyPathArray
are all values
which can be accessed when updating the model. This can prove useful if for some reason you need to listen to a specific
attribute change.
graph.on('change', (cell, opt) => {
console.log(opt);
// --> {propertyPath: 'attrs/line/targetMarker/fill', propertyValue: 'cornflowerblue', propertyPathArray: Array(4)}
if ('attrs' in cell.changed) {
console.log(opt.propertyPathArray, 'was changed');
// --> ['attrs', 'line', 'targetMarker', 'fill'] 'was changed'
}
});
link.prop('attrs/line/targetMarker/fill', 'cornflowerblue');
Advanced: Pass { isolate: true }
if the property change does not affect the connected links. Typically, changing the link color has zero effect on attached links. By default, the link and all connected links are updated.
link.attr(['line', 'stroke'], 'red', { isolate: true });
link.remove()
Remove the link from the graph.
link.removeAttr(path [, opt])
Remove a previously set attribute from the link. path
can either be a string that specifies the path to the (possibly nested) attribute to be removed, or an array of more paths. The associated link view makes sure the link gets re-rendered properly.
If opt
is passed, it can contain data that is passed over to the event listeners for the change:attrs
event triggered on the link itself and also on the graph the link is in.
link.removeLabel(index [, opt])
Remove label
at index
. Pass index -1
to remove the last label of the labels array.
link.removeVertex(index [, opt])
Remove vertex
at index
. Pass index -1
to remove the last vertex of the vertices array.
link.reparent()
Automatically find and set the best parent element for the link so that when the parent element is moved, the link and all its vertices are moved too. The best parent is determined as the common ancestor of the source and target elements of the link. Useful for hierarchical diagrams. See the DEVS demo on how this can be used.
link.router()
Return a shallow copy of the router
property of the link.
link.router(router [, opt])
Set the router
of the link.
If the router
argument is an object, it is expected to have the form { name: routerName, args?: routerArgs }
. Here routerName
is expected to match either the name of a built-in router or the name of a custom router.
If the router
argument is a function, it is expected to define a custom router with the signature function(vertices, routerArgs, linkView)
that returns an array of route points.
link.router(routerName [, routerArgs, opt])
Set the router
of the link to have the value { name: routerName, args: routerArgs }
.
The routerName
string is expected to match either the name of a built-in router or the name of a custom router. The routerArgs
parameter is optional.
link.scale(sx, sy, origin [, opt])
Scales the link's points (vertices) relative to the given origin.
link.source()
Return a shallow copy of the source
property of the link.
If the beginning of the link is connected to an element, an object in the format { id: elementID }
is returned. If the beginning of the link is specified as a point instead (the link is pinned
to the paper at that point), an object in the format { x: sourceX, y: sourceY }
is returned. Furthermore, any additional arguments of the source are returned alongside those properties.
If you need to be sure that a Cell is returned (and not a Point), use the link.getSourceCell
function instead. If you need an Element, use the link.getSourceElement
function.
link.source(source [, opt])
Set the source
of the link.
If the link is to be connected to an element, send an object in the format { id: element.id }
(or a joint.dia.Element
object). If the link is to be pinned to the paper, send an object in the format { x: sourceX, y: sourceY }
(or a g.Point
object).
link.source(rect);
link.source({ id: rect.id });
link.source(new g.Point(100, 100));
link.source({ x: 100, y: 100 });
Additional options may by provided to further specify the behavior of link at the source:
link.source(rect, {
selector: 'body',
anchor: {
name: 'bottomLeft',
args: {
dx: 20,
dy: -10
}
}
});
More information can be found in link source documentation.
link.stopTransitions([path])
Stops all running transitions. If parameter path
is provided, it will stop only transitions specified by this path.
link.target()
Return a shallow copy of the target
property of the link.
If the ending of the link is connected to an element, an object in the format { id: elementID }
is returned. If the ending of the link is specified as a point instead (the link is pinned
to the paper at that point), an object in the format { x: targetX, y: targetY }
is returned. Furthermore, any additional arguments of the target are returned alongside those properties.
If you need to be sure that a Cell is returned (and not a Point), use the link.getTargetCell
function instead. If you need an Element, use the link.getTargetElement
function.
link.target(target [, opt])
Set the target
of the link.
If the link is to be connected to an element, send an object in the format { id: element.id }
(or a joint.dia.Element
object). If the link is to be pinned to the paper, send an object in the format { x: targetX, y: targetY }
(or a g.Point
object).
link.target(rect);
link.target({ id: rect.id });
link.target(new g.Point(100, 100));
link.target({ x: 100, y: 100 });
Additional options may by provided to further specify the behavior of link at the target:
link.target(rect, {
selector: 'body',
anchor: {
name: 'bottomLeft',
args: {
dx: 20,
dy: -10
}
}
});
More information can be found in link target documentation.
link.toBack()
Move the link so it is behind all other cells (elements/links). This is a method analogous to toBack method of Joint.dia.Element
.
link.toFront()
Move the element so it is on top of all other cells (elements/links). This is a method analogous to toFront method of Joint.dia.Element
.
link.toJSON()
Return a copy of the link's attributes for JSON serialization. This is a method analogous to toJSON method of Joint.dia.Element
.
link.transition(path, value [, opt])
Allows changing of the link properties gradually over a period of time. This is method is analogous to the transition method of joint.dia.Element
.
link.transition('target', { x: 250, y: 250 }, {
delay: 100,
duration: 500,
timingFunction: joint.util.timing.bounce,
valueFunction: joint.util.interpolate.object
});
// will start changing the link target coordinates in 100ms, for period of 500ms and performing a bounce effect.
link.translate(tx, ty [, opt])
Translate the link vertices (and source and target if they are points) by tx
pixels in the x-axis and ty
pixels in the y-axis.
If opt
object is passed, it can contain data that is passed over the the event listeners for the change event on the link or graph.
link.vertex(index)
Return the vertex at index
.
link.vertex(index, vertex [, opt])
Update the vertex at index
with value of vertex
. By default the new value is merged into the old value. Pass the { rewrite: true }
option along to disregard the old value.
A vertex has only two properties, x
and y
. It may be defined as a g.Point object.
Example vertex:
link.vertex(0, {
x: 100,
y: 200
});
Note that all vertices are stored in an array on the link model under the attribute vertices
. Use the link.vertices
function to access the array.
link.vertices()
Return a shallow copy of vertices array.
link.vertices(verticesArray [, opt])
Set array of vertices on the link.
The view for the joint.dia.Link model. It inherits from joint.dia.CellView and is responsible for:
To find the view associated with a specific link model, use the paper.findViewByModel()
method:
const linkView = paper.findViewByModel(link);
// const linkView = link.findView(paper); // alternatively
It is possible to use a custom default link view for all your links in a paper. This can be set up via the linkView
option on the paper object.
A custom LinkView type may also override default LinkView event handlers, or provide new ones. It may be necessary to modify the interactive
paper option to prevent interference from builtin event handlers.
Example:
const CustomLinkView = dia.LinkView.extend({
// custom interactions:
pointerdblclick: function(evt, x, y) {
this.addVertex(x, y);
},
contextmenu: function(evt, x, y) {
this.addLabel(x, y);
}
});
const paper = new dia.Paper({
//...
linkView: CustomLinkView,
});
linkView.addLabel(x, y [, angle, opt])
Add a new default label to the link at the (x,y) coordinates provided. See also the link.appendLabel()
function.
linkView.addLabel(point [, angle, opt])
Add a new default label to the link at the coordinates specified by point
. See also the link.appendLabel()
function.
In either case, this method uses the linkView.getLabelPosition()
function to determine the new label's position
. By default, position.distance
is recorded relative to connection length (as a number in the [0,1]
range), and position.offset
is set relative to the connection (as a number). This behavior may be changed by providing an opt
object with some of the accepted boolean flags:
absoluteDistance: true
records distance
absolutely (as distance from beginning of link)reverseDistance: true
switches distance
to be calculated from end of link, if absoluteDistance
absoluteOffset: true
records offset
absolutely (as x
and y
from connection)The angle
parameter, if provided, is saved as position.angle
attribute inside the returned object. Two additional flags, which may be passed in the opt
object, provide more control over label rotation:
keepGradient: true
- adjust the rotation of the label to match the angle of incline of the path at position.distance
ensureLegible: true
- if the label text ends up being upside-down, rotate the label by additional 180 degrees to ensure that the text stays legible, if keepGradient
The opt
object passed to the label is recorded as label.position.args
. The label uses these options during subsequent labelMove interactions.
This function is useful within custom linkView
event listener definitions:
var CustomLinkView = joint.dia.LinkView.extend({
contextmenu: function(evt, x, y) {
this.addLabel(x, y, 45, {
absoluteDistance: true,
reverseDistance: true, // applied only when absoluteDistance is set
absoluteOffset: true,
keepGradient: true,
ensureLegibility: true // applied only when keepGradient is set
});
}
});
var paper = new joint.dia.Paper({
// ...
linkView: CustomLinkView
});
linkView.addTools(toolsView)
Add the provided toolsView
(of the joint.dia.ToolsView
type) to the link view.
Adding a tools view to a link view is the last (third) step in the process of setting up link tools on a link view:
// 1) creating link tools
var verticesTool = new joint.linkTools.Vertices();
var segmentsTool = new joint.linkTools.Segments();
var boundaryTool = new joint.linkTools.Boundary();
// 2) creating a tools view
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [verticesTool, segmentsTool, boundaryTool]
});
// 3) attaching to a link view
var linkView = link.findView(paper);
linkView.addTools(toolsView);
Every link view we want to attach to requires its own tools view object (ToolsView
objects are automatically reassigned to the last link view they are added to). Similarly, every tools view we create requires its own set of tools (ToolView
objects are automatically reassigned to the last toolsView.tools
array they were made part of).
The link tools are added in the visible state. Use the linkView.hideTools
function if this behavior is not desirable (e.g. if you want the link tools to appear in response to user interaction). Example:
linkView.addTools(toolsView);
linkView.hideTools();
paper.on('link:mouseenter', function(linkView) {
linkView.showTools();
});
paper.on('link:mouseleave', function(linkView) {
linkView.hideTools();
});
Note that the above example may not work as expected if toolsView
includes the SourceArrowhead
tool and/or the TargetArrowhead
tool - the link tools view might not be hidden when the link is reconnected to a topic. See our link tools tutorial for more information.
linkView.addVertex(x, y)
Add a new default vertex to the link at the coordinates provided, and let the linkView automatically determine the index of the new vertex in the vertices array. If you need to add the vertex at a custom index, use the link.addVertex()
function instead.
This method uses the linkView.getVertexIndex()
function to determine the index of the new vertex in the vertices
array. The linkView checks the distance of vertices in the link.vertices
array from the beginning of path and compares it to the distance of the added vertex. The new vertex is inserted before the first farther vertex in the vertices
array.
This function is useful within custom linkView
event listener definitions. The following example adds a new vertex on a double click event, instead of a pointerdown event (which is the default behavior):
var CustomLinkView = joint.dia.LinkView.extend({
pointerdblclick: function(evt, x, y) {
this.addVertex(x, y);
}
});
var paper = new joint.dia.Paper({
// ...
linkView: CustomLinkView
});
linkView.findLabelNode(index)
Return the root SVGElement of the label at given index. If label at index doesn't exist, null
is returned.
linkView.findLabelNode(index, selector)
Return an SVGElement|HTMLElement referenced by selector of the label at given index. If there is no label at index or no DOM node was found by the selector, null
is returned.
The available selectors
are defined by the markup
attribute of the label from which the label was built.
linkView.findLabelNodes(index, groupSelector)
Return an array of SVGElement|HTMLElement referenced by groupSelector of the label at given index. If there is no label at index or no DOM node was found by the groupSelector, an empty array is returned.
The available groupSelectors
are defined by the markup
attribute of the label from which the label was built.
linkView.getBBox([opt])
Return a bounding box of the link view.
If opt.useModelGeometry
option is set to true
, the resulting bounding box is calculated based on the dimensions of the link model. (This means that a simplified polyline is used and portions of curved links may be sticking out
of the reported bounding box.) This behavior is similar to the link.getBBox
function – but the linkView
function transforms the bounding box to match joint.dia.Paper
translation and scaling.
linkView.getClosestPoint(point)
Return the point on the connection that lies closest to point
.
linkView.getClosestPointLength(point)
Return the length of the connection up to the point that lies closest to point
.
linkView.getClosestPointRatio(point)
Return the ratio (normalized length) of the connection up to the point that lies closest to point
. The returned value lies in the interval [0,1]
(inclusive).
linkView.getConnection()
Return a geometric representation of the connection (instance of g.Path).
linkView.getConnectionLength()
Return a cached total length of the connection in pixels.
linkView.getConnectionSubdivisions()
Return a cached array of segment subdivision arrays of the connection. (See g.Path.prototype.getSegmentSubdivisons()
documentation for more information.)
linkView.getLabelCoordinates(labelPosition)
Return the x
and y
coordinates based on the provided labelPosition
object.
See link.label()
documentation for more information about the position
object.
An object in the format { x: number, y: number }
is returned.
linkView.getLabelPosition(x, y [, angle, opt])
Return a label position
object based on the x
and y
coordinates provided.
The function translates the provided coordinates and angle
into an object with three fields:
distance
- the distance (following the line) of the point on the line that is closest to point x,y
.offset
- the distance between the closest point and the point x,y
.angle
- the angle of the label relative to the connection line, as determined by the angle
parameter, or 0
if angle
was not specified.
By default, position.distance
is calculated as relative to connection length (as a number in the [0,1]
range that records the length ratio), and position.offset
is calculated as relative to the connection (as a number recording the perpendicular distance). The user may change this behavior by providing an opt
object with some of the following accepted boolean flags:
absoluteDistance: true
- record distance
absolutely (as absolute distance from beginning of link, a positive number)reverseDistance: true
- if absoluteDistance: true
, record distance
absolutely from end of link (as a negative number)absoluteOffset: true
- record offset
absolutely (as x
and y
distance from closest point)Please note that if the absoluteOffset
flag is not set, label can only be placed/moved in the area that is reachable by lines perpendicular to the link (that is, the label can never be moved beyond link endpoints).
Two additional flags, which may be passed in the opt
object, provide control over label rotation:
keepGradient: true
- adjust the rotation of the label to match the angle of incline of the path at position.distance
ensureLegible: true
- if the label text ends up being upside-down, rotate the label by additional 180 degrees to ensure that the text stays legible, if keepGradient
The opt
object passed to the label is recorded as label.position.args
. The label uses these options during subsequent labelMove interactions.
An object in the following format is returned:
{
distance: number,
offset: number | { x: number, y: number },
angle: number,
args?: {
absoluteDistance?: boolean,
reverseDistance?: boolean, // applied only when absoluteDistance is set
absoluteOffset?: boolean,
keepGradient?: boolean,
ensureLegible?: boolean // applied only when keepGradient is set
}
}
See link.label()
documentation for more information about the position
object.
This function can be used to add a custom label to the link.labels
array, in situations when the linkView.addLabel()
function is not sufficient. For example:
var CustomLinkView = joint.dia.LinkView.extend({
contextmenu: function(evt, x, y) {
var idx = -1; // add at end of `labels`
var label = {
markup: '<g class="label"><circle /><path /></g>',
attrs: {
circle: {
r: 15,
fill: 'lightgray',
stroke: 'black',
strokeWidth: 2
},
path: {
d: 'M 0 -15 0 -35 20 -35',
stroke: 'black',
strokeWidth: 2,
fill: 'none'
}
},
position: this.getLabelPosition(x, y, 45, {
absoluteOffset: true,
keepGradient: true,
ensureLegible: true
})
}
this.model.addLabel(idx, label);
}
});
var paper = new joint.dia.Paper({
// ...
linkView: CustomLinkView
});
elementView.getNodeBBox(magnet)
Return the bounding box of the SVGElement provided as magnet
(model of this link view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
elementView.getNodeUnrotatedBBox(magnet)
Return the unrotated bounding box of the SVGElement provided as magnet
(model of this link view).
Use the paper.localToPaperRect
function to transform the returned bounding box to match the paper's translation and scaling.
linkView.getPointAtLength(length)
Return the point on the path that lies length
away from the beginning of the connection.
linkView.getPointAtRatio(ratio)
Return the point on the path that lies ratio
(normalized length) away from the beginning of the connection.
linkView.getSerializedConnection()
Return a cached path data of the connection (the value of the 'd'
SVGPathElement attribute).
linkView.getTangentAtLength(length)
Return a line tangent to the path at point that lies length
away from the beginning of the connection.
linkView.getTangentAtRatio(ratio)
Return a line tangent to the path at point that lies ratio
(normalized length) away from the beginning of the connection.
linkView.getVertexIndex(x, y)
Return the vertices
array index at which to insert a new vertex with the provided x
and y
coordinates.
The linkView finds the point on the connection that lies closest to the point x,y
. Then, the linkView iterates over the vertices in the link.vertices
array until one is found that lies farther than the identified closest point. The index of the farther point is returned.
The returned index can be used as the first argument of the link.addVertex
function.
linkView.hasTools()
Return true
if this link view has a tools view attached.
linkView.hasTools(name)
Return true
if this link view has a tools view of the provided name
attached.
linkView.hideTools()
Hide all tools attached to this link view.
linkView.removeRedundantLinearVertices([opt])
Remove any redundant vertices from the model's vertices
array. Return the number of removed vertices.
A vertex is considered redundant
if it lies on the straight line formed by the vertex that immediately precedes it and the vertex that immediately follows it. (Intuitively speaking, a vertex is considered redundant
if its removal from the link does not change the shape of the link.)
linkView.removeTools()
Remove the tools view attached to this link view.
linkView.requestConnectionUpdate()
Schedule an update of the connection (anchors, connection points, route and connector) of this link view in the next animation frame (for paper async
mode) or run immediately (for paper sync
mode). Useful for links with an automatic router (such as manhattan
) if the route has to be recalculated due to another element in the graph changing its position.
linkView.sendToken(token [, opt, callback])
Send a token along the link. token
is an SVG element (or Vectorizer element) that will be animated along the link path for opt.duration
milliseconds (default is 1000ms). The callback
function will be called once the token reaches the end of the link path.
opt.direction
specifies whether the animation should be played forwards ('normal'
- from the link source to target, the default) or backwards ('reverse'
).
Use opt.connection
to specify the SVGPathElement for the token to follow. It expects a string selector, e.g. '.connection'
.
// Send an SVG circle token along the link.
var vCircle = V('circle', { r: 7, fill: 'green' });
link.findView(paper).sendToken(vCircle, { duration: 500, direction: 'reverse' }, function() {
console.log('animation end');
});
Note that in the code above, we use the Vectorizer mini-library to create the SVG circle
element.
See the Petri Net simulator demo for a full working example.
linkView.showTools()
Show all tools attached to this link view.
linkView.sourceAnchor
A prototype variable. Contains a g.Point that records the position of the source anchor of the link, as determined by the anchor function specified on the link's source.
linkView.sourceBBox
A prototype variable. Contains a g.Rect that records the position and dimensions of the bounding box of the source magnet (element/subelement/port), as determined by the link's source definition.
If the source is defined as a point, a g.Rect is returned that records the position of the point and has the dimensions (1,1).
linkView.sourcePoint
A prototype variable. Contains a g.Point that records the position of the source connection point of the link, as determined by the connection point function specified on the link's source.
linkView.targetAnchor
A prototype variable. Contains a g.Point that records the position of the target anchor of the link, as determined by the anchor function specified on the link's target.
linkView.targetBBox
A prototype variable. Contains a g.Rect that records the position and dimensions of the bounding box of the target magnet (element/subelement/port), as determined by the link's target definition.
If the target is defined as a point, a g.Rect is returned that records the position of the point and has the dimensions (1,1).
linkView.targetPoint
A prototype variable. Contains a g.Point that records the position of the target connection point of the link, as determined by the connection point function specified on the link's target.
joint.dia.Paper is the view for the joint.dia.Graph
model.
It inherits from mvc.View.
Accepts an options
object in its constructor with numerous settings.
When a paper is associated with a graph, the paper makes sure that all the cells added to the graph are automatically rendered.
var graph = new joint.dia.Graph
var paper = new joint.dia.Paper({
el: $('#paper'),
width: 600,
height: 400,
gridSize: 10,
model: graph
});
var rect = new joint.shapes.basic.Rect({
position: { x: 50, y: 70 },
size: { width: 100, height: 40 }
});
graph.addCell(rect);
Paper automatically handles this change and renders the rectangle to the SVG document that it internally holds.
The following list contains events that you can react on in a paper:
pointerdblclick |
Triggered when pointer is double-clicked on a target (a The callback function is passed
|
|||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
pointerclick |
Triggered when pointer is left-clicked on a target without pointer movement (a The callback function is passed
|
|||||||||||||
contextmenu |
Triggered when pointer is right-clicked on a target. This trigger is fired on a The callback function is passed
|
|||||||||||||
pointerdown |
Triggered when pointer is pressed down on a target (a The callback function is passed
|
|||||||||||||
pointermove |
Triggered when pointer is moved over a target while pressed down (a The callback function is passed
|
|||||||||||||
pointerup |
Triggered when pointer is released on a target after being pressed down (a The callback function is passed
Calling |
|||||||||||||
mouseover |
Triggered when pointer begins to hover directly over a target. The callback function is passed
|
|||||||||||||
mouseout |
Triggered when pointer ceases to hover directly over a target. The callback function is passed
|
|||||||||||||
mouseenter |
Triggered when pointer enters the area above a target. The callback function is passed
|
|||||||||||||
mouseleave |
Triggered when pointer leaves the area above a target. The callback function is passed
|
|||||||||||||
mousewheel |
Triggered when mouse wheel is rotated while pointer is on a target. The callback function is passed
|
|||||||||||||
pan |
Triggered when a pan event is emitted while the pointer is on top of the target. Pan events are similar to mousewheel events, but while mousewheel events cover only the Y axis, pan events cover both X and Y axis. This is usually possible with touchpads / trackpad devices. The callback function is passed
|
|||||||||||||
pinch |
Triggered when a pinch event is emitted while the pointer is on top of the target. This is usually possible with touchpads / trackpad devices. The callback function is passed
|
|||||||||||||
magnet |
Triggered when interacting with a magnet target. The callback function is passed
|
|||||||||||||
cell:highlight |
Triggered when the The callback function is invoked with the following arguments:
|
|||||||||||||
cell:unhighlight |
Triggered when the The callback function is invoked with the following arguments:
|
|||||||||||||
cell:highlight:invalid |
Triggered when a highlighter makes an attempt to highlight a node, that doesn't exist on the CellView (e.g. a port was highlighted and now it is removed). The callback function is passed
|
|||||||||||||
link:connect |
Triggered when a link is connected to a cell. The event is triggered after the user reconnects a link. The callback function is passed |
|||||||||||||
link:disconnect |
Triggered when a link is disconnected from a cell. The event is triggered after the user reconnects a link. The callback function is passed |
|||||||||||||
link:snap:connect |
Triggered when a link is connected to a cell. The event (or multiple events) can be triggered while the user is reconnecting a link and snapLinks option is enabled. The callback function is passed |
|||||||||||||
link:snap:disconnect |
Triggered when a link is disconnected from a cell. The event (or multiple events) can be triggered while the user is reconnecting a link and snapLinks option is enabled. The callback function is passed |
|||||||||||||
render:done |
Triggered when all scheduled updates are done (i.e. all scheduled batches have finished). |
|||||||||||||
[custom] |
Custom cell event can be triggered on pointerdown with Event attribute. Calling |
An example of a simple blank:pointerdown
event listener:
paper.on('blank:pointerdown', function(evt, x, y) {
alert('pointerdown on a blank area in the paper.')
})
Consecutive pointerdown
, pointermove
and pointerup
events can share information through the evt.data
object:
// Create a new link by dragging
paper.on({
'blank:pointerdown': function(evt, x, y) {
const link = new joint.shapes.standard.Link();
link.set('source', { x, y });
link.set('target', { x, y });
link.addTo(this.model);
evt.data = { link, x, y };
},
'blank:pointermove': function(evt, x, y) {
evt.data.link.set('target', { x, y });
},
'blank:pointerup': function(evt) {
var target = evt.data.link.get('target');
if (evt.data.x === target.x && evt.data.y === target.y) {
// remove zero-length links
evt.data.link.remove();
}
}
});
paper.checkViewport()
For every view in the paper, determine if it fits into the viewport specified by paper.options.viewport
function. Views that fit (views for which that function returns true
) are attached to the DOM; views that do not are detached.
While async
papers do this automatically, synchronous papers require an explicit call to this method for this functionality to be applied. To show all views again, use paper.dumpViews()
.
paper.checkViewport(opt)
If opt.viewport
is provided, it is used as the callback function instead of paper.options.viewport
. The format of the callback method is described in the option's documentation, as are examples of usage.
In async
papers, providing opt.viewport
causes viewport to be temporarily recalculated according to the provided callback function. This may be useful in situations where you need to show different views depending on context (e.g. viewing a section of the diagram vs. printing all of it).
paper.clientOffset()
Returns coordinates of the paper viewport, relative to the application's client area.
paper.clientToLocalPoint(p)
Transform client coordinates represented by point p
to the paper local coordinates.
This is especially useful when you have a mouse event object and want coordinates
inside the paper that correspond to event clientX
and clientY
point.
var localPoint1 = paper.clientToLocalPoint({ x: evt.clientX, y: evt.clientY });
// alternative method signature
var localPoint2 = paper.clientToLocalPoint(evt.clientX, evt.clientY);
paper.clientToLocalRect(rect)
Transform the rectangle rect
defined in the client coordinate system to the local coordinate system.
var bcr = paper.svg.getBoundingClientRect();
var localRect1 = paper.clientToLocalRect({ x: bcr.left, y: bcr.top, width: bcr.width, height: bcr.height });
// alternative method signature
var localRect2 = paper.clientToLocalRect(bcr.left, bcr.top, bcr.width, bcr.height);
// Move the element to the center of the paper viewport.
var localCenter = localRect1.center();
var elSize = element.size();
element.position(localCenter.x - elSize.width, localCenter.y - elSize.height);
paper.defineFilter(filterDefinition)
Define an SVG filter for later reuse within the paper. The method returns the filter id and the filterDefinition
must have the following form:
{
name: <name of the filter>,
args: <filter arguments>
}
Where name
is the name of the filter. See below for the list of built-in filters. args
is an object containing filter parameters. These parameters are dependent on the filter used and are described in the list below as well. Example usage:
var filterId = paper.defineFilter({
name: 'dropShadow',
args: {
dx: 2,
dy: 2,
blur: 3
}
});
svgNode.setAttribute('filter', 'url(#' + filterId + ');
The following is the list of built-in filters. All these filters are defined in the joint.util.filter
namespace. This namespace can be extended simply by adding a new method to it with one argument, an object with filter parameters, returning a string representing the SVG filter definition.
blur
x
- horizontal blury
- vertical blur [optional, if not defined y
is the same as x
]dropShadow
dx
- horizontal shiftdy
- vertical shiftblur
- blurcolor
- coloropacity
- opacitygrayscale
amount
- the proportion of the conversion. 1
is completely grayscale. 0
leaves the element unchanged.sepia
amount
- the proportion of the conversion. 1
is completely sepia. 0
leaves the element unchanged.saturate
amount
- the proportion of the conversion. 0
is completely un-saturated. 1
leaves the element unchanged.hueRotate
angle
- the number of degrees around the color circle the input samples will be adjustedinvert
amount
- the proportion of the conversion. 1
is completely inverted. 0
leaves the element unchanged.brightness
amount
- the proportion of the conversion. 0
makes the element completely black. 1
leaves the element unchanged.contrast
amount
- the proportion of the conversion. 0
makes the element completely black. 1
leaves the element unchanged.paper.defineGradient(gradientDefinition)
Define an SVG gradient for later reuse within the paper. The method returns the gradient id and the gradientDefinition
must be an object with the following properties:
Name | Type | Description |
---|---|---|
type | 'linearGradient' | 'radialGradient' |
The linearGradient or radialGradient type of the gradient. |
id | string | (optional) - A unique identifier of the gradient. The id is generated if none provided. |
attrs | object | (optional) - Additional SVG attributes for the SVGGradientElement. |
stops | array | An array of stops. |
Where a stop
is an object with the following properties:
Name | Type | Description |
---|---|---|
color | string | Indicates what color to use at that gradient stop. |
offset | number | string | The offset of the gradient stop. |
opacity | number | (optional) - A number in the [0..1] range representing the transparency of the stop color. |
const gradientId = paper.defineGradient({
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#E67E22' },
{ offset: '20%', color: '#D35400' },
{ offset: '40%', color: '#E74C3C' },
{ offset: '60%', color: '#C0392B' },
{ offset: '80%', color: '#F39C12' }
]
});
svgNode.setAttribute('fill', `url(#${gradientId})`);
For an introduction to gradients, please refer to the tutorial on Filters and Gradients.
paper.defineMarker(markerDefinition)
Define an SVG marker for later reuse within the paper. The method returns the marker id. The markerDefinition
parameter can be an object with the following properties:
Name | Type | Description |
---|---|---|
id | string | (optional) - A unique identifier of the marker. The id is generated if none provided. |
attrs | object | (optional) - Additional SVG attributes for the SVGMarkerElement. |
markup | string | array | JSON or string markup of the marker content. |
The coordinate system for defining path data within marker
is as follows:
Point (x,y ) |
Relative position |
---|---|
0,0 |
At marker-start , marker-end or marker-mid point, as appropriate. |
n,0 |
If n > 0 , position along path direction (i.e. start -> end). |
If n < 0 , position opposite to path direction (i.e. start <- end). |
|
0,m |
If m > 0 , position to the right of path direction. |
If m < 0 , position to the left of path direction. |
An example of JSON markup with id
specified:
paper.defineMarker({
id: 'my-marker',
markup: [{
tagName: 'circle',
attributes: {
'cx': '6',
'cy': '0',
'r': '12',
'fill': '#7b5cce'
}
}, {
tagName: 'polygon',
attributes: {
'points': '0,0 6,6 12,0 6,-6',
'fill': '#d63865',
'stroke': '#fff'
}
}]
});
// Draw the shape at the start of a path
svgPathNode.setAttribute('marker-start', `url(#my-marker)`);
The same example in string markup:
paper.defineMarker({
id: 'my-marker',
markup: `
<circle cx="6" cy="0" r="12" fill="#7b5cce" />
<polygon points="0,0 6,6 12,0 6,-6" fill="#d63865" stroke="#fff" />
`
});
// Draw the shape at the end of a path
svgPathNode.setAttribute('marker-start', `url(#my-marker)`);
Alternatively, defining a marker is possible via a flat object with the following properties:
Name | Type | Description |
---|---|---|
id | string | (optional) - A unique identifier of the marker. The id is generated if none provided. |
type | string | (optional) - The type of the SVGElement to generate to represent the marker ('path' , 'circle' , 'ellipse' , 'rect' , 'polyline' and 'polygon' ). Default is 'path' . |
[SVGAttribute] | any | (optional) - A presentation SVG attribute (e.g fill: 'red' ) to be applied on the generated SVGElement (see type ). |
The coordinate system for defining path data is the same as above.
An example of specifying marker content via a flat object and referring to it in code via its automatically-generated id
:
const markerId = paper.defineMarker({
type: 'path', // SVG Path
fill: '#666',
stroke: '#333',
d: 'M 10 -10 0 0 10 10 Z'
});
// Draw an arrow at the start and the end of a path
svgPathNode.setAttribute('marker-start', `url(#${markerId})`);
svgPathNode.setAttribute('marker-end', `url(#${markerId})`);
paper.definePattern(patternDefinition)
Define an SVG pattern for later reuse within the paper. The method returns the pattern id and the patternDefinition
must be an object with the following properties:
Name | Type | Description |
---|---|---|
id | string | (optional) - A unique identifier of the pattern. The id is generated if none provided. |
attrs | object | (optional) - Additional SVG attributes for the SVGGradientElement. |
markup | string | array | String or JSON Markup of the pattern content. |
const patternId = paper.definePattern({
attrs: {
width: 10,
height: 10
},
markup: [{
tagName: 'polygon',
attributes: {
points: '0,0 2,5 0,10 5,8 10,10 8,5 10,0 5,2'
}
}]
});
svgNode.setAttribute('fill', `url(#${patternId})`);
paper.drawBackground(opt);
Sets the paper background defined by the opt
object. Please see the background paper option for available configuration.
paper.dumpViews([opt])
Add all paper views into the DOM and update them to make sure that the views reflect the cells' models.
Several extra arguments may be provided in the opt
object:
batchSize | number | For async papers, how many views should there be per one asynchronous process? Default is 1000 . |
|||||||||
progress | function | Callback function that is called whenever a batch is finished processing. The callback function is provided with three arguments:
|
|||||||||
viewport | function | Callback function to determine whether a given view should be added to the DOM. By default, paper.options.viewport is used, but you may provide a different callback function. The format of the callback method is described in the option's documentation, as are examples of usage.
|
paper.findView(element)
Find a view (instance of joint.dia.ElementView
or joint.dia.LinkView
) associated with a DOM element in the paper. element
can either be a DOM element, or a CSS selector.
Sometimes, it is useful to find a view object for an element in the DOM. This method finds the closest view for any subelement of a view element.
paper.findViewByModel(model)
Find a view (instance of joint.dia.ElementView
or joint.dia.LinkView
) associated with a model. model
can either be an instance of joint.dia.Element
or joint.dia.Link
.
paper.findViewsFromPoint(point)
Find views (instance of joint.dia.ElementView
) under a certain point in the paper. point
is an object with x
and y
properties. Returns an
array of views whose bounding box contains point
. Note that there can be more then one views as views might overlap. Note there is a difference between
this method and the joint.dia.Graph:findModelsFromPoint. A bounding box of a view can be different than the area computed by an element model position
and size
.
For example, if a <text>
SVG element in the shape is positioned relatively and shifted down below the normal shape area (e.g. using the JointJS special attributes), the bounding box
of the view will be bigger than that of the model.
paper.findViewsInArea(rect [, opt])
Find views (instance of joint.dia.ElementView
) in a certain area in the paper.
rect
is an object with x
, y
, width
and height
properties.
Return an array of views whose bounding box intersects the rect
rectangle.
If opt.strict
is true
, return an array of views whose bounding box is contained within the rect
rectangle (i.e. not only intersects it).
paper.fitToContent([opt])
Expand or shrink the paper to fit the content inside it. The method returns the area (g.Rect
) of the paper after fitting in local coordinates.
The list of all available options can be found in the documentation of the paper.getFitToContentArea
function. You can try many of these options interactively in the paper demo.
This method might internally trigger the "resize"
and "translate"
events. These can be handled by listening on the paper object (paper.on('resize', myHandler)
).
paper.fitToContent([gridWidth, gridHeight, padding, opt])
Deprecated usage. All parameters are expected to be passed inside the opt
object.
paper.freeze()
Freeze an async
paper. In this state, the paper does not automatically re-render upon changes in the graph. This is useful when adding large numbers of cells.
Use paper.unfreeze()
to take the paper back out of the frozen state and to reflect the changes made in the graph in the meantime (if any).
paper.getArea()
Return a rectangle representing the area of the paper, in local units (without transformations).
Creates a rectangle object with { x: 0, y: 0 }
and with width
and height
values according to the paper.getComputedSize()
function. Then uses the paper.paperToLocalRect()
function to transform that rectangle into local units.
paper.getComputedSize()
Return an object containing width
and height
properties. It resolves any computation these property values may contain (e.g resolves "100%"
to the actual client width).
paper.getContentArea()
Return a rectangle representing the area occupied by paper content, in local units (without transformations).
If opt.useModelGeometry
is set (default is false
), occupied area is calculated from the dimensions of component cells' models (not views). This option is useful whenever one or more of the paper cells are not in the DOM (e.g. during asynchronous calls). However, the method uses a simplified heuristic that is different from the one used by the browser; the mapping between dimensions of cell views and dimensions of cells models is not necessarily one-to-one. (Most notably, ports are not included in model-reported occupied areas. In addition, the area occupied by links is constructed from link vertices, which may exclude portions of protruding Curveto and Arcto segments.)
paper.getContentBBox(opt)
Return the bounding box of the content inside the paper, in client units (as it appears on the screen).
If opt.useModelGeometry
is set (default is false
), the bounding box is calculated from the dimensions of component cells' models (not views). This option is useful whenever one or more of the paper cells are not in the DOM (e.g. during asynchronous calls). However, the method uses a simplified heuristic that is different from the one used by the browser; the mapping between dimensions of cell views and dimensions of cells models is not necessarily one-to-one. (Most notably, ports are not included in model-reported bounding boxes. In addition, the bounding boxes of links are constructed from link vertices, which may exclude portions of protruding Curveto and Arcto segments.)
paper.getFitToContentArea([opt])
Return the bounding box in the local coordinate system based on the position and size of the current content and options provided.
The function accepts an object with the following options:
opt.gridWidth
and opt.gridHeight
– snap the resulting width and height of the paper to a grid defined by these dimensions.opt.padding
– additional padding around the resulting paper. It may be specified as a number, in which case it represents the padding width on all sides of the paper. It may be an object of the form { top?: [number], right?: [number], bottom?: [number], left?: [number], vertical?: [number], horizontal?: [number] }
.opt.allowNewOrigin
– should the origin of the resulting paper be adjusted to match the origin of the paper content? In addition to no value being set, three values are recognized by this option: 'positive'
, 'negative'
, 'any'
.
'positive'
– the method only recognizes the content at positive coordinates and puts the origin of resulting paper at the origin of the content. (Still, if the content starts at negative coordinates in an axis, the resulting paper's origin will be assigned 0
in that axis.)'negative'
– the method only recognizes the content at negative coordinates and puts the origin of resulting paper at the origin of the content. (However, if the content starts at positive coordinates in an axis, the resulting paper's origin will be assigned 0
in that axis.)'any'
– the method recognizes all of the paper content. The origin of the resulting paper will be at the origin of the content.opt.allowNegativeBottomRight
- can the bottom-right corner of the resulting paper have negative coordinates? By default, the paper area always contains point (0,0). i.e. false
.
// Resize the paper to match the tight bounding box of the content regardless of its position.
paper.fitToContent({
allowNewOrigin: 'any',
allowNegativeBottomRight: true
});
opt.minWidth
and opt.minHeight
– define the minimum width and height of the resulting paper after fitting it to content.opt.maxWidth
and opt.maxHeight
– define the maximum width and height of the resulting paper after fitting it to content.opt.useModelGeometry
– should paper content area be calculated from cell models instead of views? The default is false
. See the documentation of the paper.getContentArea
function for more details.opt.contentArea
– an object of the form { x: [number], y: [number], width: [number], height: [number] }
is the area representing the content bounding box in the local coordinate system that paper should be fitted to. By default opt.contentArea
is paper.getContentArea(opt)
.paper.hasScheduledUpdates()
Return true
if any changes were made to the graph which are not yet reflected in the paper. (Call the updateViews
function to update these views.) Return false
otherwise.
paper.hideTools()
Hide all tools attached to all element and link views on this paper.
paper.isDefined(graphicalObjectId)
Return true
if there is a graphical object (gradient, filter, marker) with graphicalObjectId
already defined within the paper. Return false
otherwise.
paper.isFrozen()
Return true
if the paper is currently frozen
. Return false
otherwise.
paper.localToClientPoint(p)
Transform the point p
defined in the local coordinate system to the client coordinate system.
var rightMidPoint = element.getBBox().rightMiddle();
var clientPoint1 = paper.localToClientPoint(rightMidPoint);
// alternative method signature
var clientPoint2 = paper.localToClientPoint(rightMidPoint.x, rightMidPoint.y);
// Draw an HTML rectangle next to the element.
var div = document.createElement('div');
div.style.position = 'fixed';
div.style.background = 'red';
div.style.left = clientPoint1.x + 'px';
div.style.top = clientPoint1.y + 'px';
div.style.width = '40px';
div.style.height = '40px';
div.style.marginLeft = '10px';
div.style.marginTop = '-20px';
document.body.appendChild(div);
paper.localToClientRect(rect)
Transform the rectangle rect
defined in local coordinate system to the client coordinate system.
var bbox = element.getBBox();
var clientRect1 = paper.localToClientRect(bbox);
// alternative method signature
var clientRect2 = paper.localToClientRect(bbox.x, bbox.y, bbox.width, bbox.height);
// Draw an HTML rectangle above the element.
var div = document.createElement('div');
div.style.position = 'fixed';
div.style.background = 'red';
div.style.left = clientRect1.x + 'px';
div.style.top = clientRect1.y + 'px';
div.style.width = clientRect1.width + 'px';
div.style.height = clientRect1.height + 'px';
paper.el.appendChild(div);
paper.localToPagePoint(p)
Transform the point p
defined in the local coordinate system to the page coordinate system.
var rightMidPoint = element.getBBox().rightMiddle();
var pagePoint1 = paper.localToPagePoint(rightMidPoint);
// alternative method signature
var pagePoint2 = paper.localToPagePoint(rightMidPoint.x, rightMidPoint.y);
// Draw an HTML rectangle next to the element.
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.background = 'red';
div.style.left = pagePoint1.x + 'px';
div.style.top = pagePoint1.y + 'px';
div.style.width = '40px';
div.style.height = '40px';
div.style.marginLeft = '10px';
div.style.marginTop = '-20px';
document.body.appendChild(div);
paper.localToPageRect(rect)
Transform the rectangle rect
defined in local coordinate system to the page coordinate system.
var bbox = element.getBBox();
var pageRect1 = paper.localToPageRect(bbox);
// alternative method signature
var pageRect2 = paper.localToPageRect(bbox.x, bbox.y, bbox.width, bbox.height);
// Draw an HTML rectangle above the element.
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.background = 'red';
div.style.left = pageRect1.x + 'px';
div.style.top = pageRect1.y + 'px';
div.style.width = pageRect1.width + 'px';
div.style.height = pageRect1.height + 'px';
document.body.appendChild(div);
paper.localToPaperPoint(p)
Transform the point p
defined in the local coordinate system to the paper coordinate system.
var rightMidPoint = element.getBBox().rightMiddle();
var paperPoint1 = paper.localToPaperPoint(rightMidPoint);
// alternative method signature
var paperPoint2 = paper.localToPaperPoint(rightMidPoint.x, rightMidPoint.y);
// Draw an HTML rectangle next to the element.
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.background = 'red';
div.style.left = paperPoint1.x + 'px';
div.style.top = paperPoint1.y + 'px';
div.style.width = '40px';
div.style.height = '40px';
div.style.marginLeft = '10px';
div.style.marginTop = '-20px';
paper.el.appendChild(div);
paper.localToPaperRect(rect)
Transform the rectangle rect
defined in the local coordinate system to the paper coordinate system.
var bbox = element.getBBox();
var paperRect1 = paper.localToPaperRect(bbox);
// alternative method signature
var paperRect2 = paper.localToPaperRect(bbox.x, bbox.y, bbox.width, bbox.height);
// Draw an HTML rectangle above the element.
var div = document.createElement('div');
div.style.position = 'absolute';
div.style.background = 'red';
div.style.left = paperRect1.x + 'px';
div.style.top = paperRect1.y + 'px';
div.style.width = paperRect1.width + 'px';
div.style.height = paperRect1.height + 'px';
paper.el.appendChild(div);
paper.matrix()
When called with no parameter, the method returns the current transformation matrix (instance of SVGMatrix) of the paper.
const { a: sx, b: sy, e: tx, f: ty } = paper.matrix();
paper.matrix(SVGMatrix, [data])
It sets the new viewport transformation based on the SVGMatrix
otherwise.
paper.matrix({ a: 2, b: 0, c: 0, d: 2, e: 0, f: 0 }); // scale the paper twice
paper.matrix(V.createSVGMatrix().translate(100, 100)); // translate the paper by 100, 100
When a new transformation is set the following events (in this order) are triggered.
"scale"
event with the new scale and the data
object if it was specified when the method was called."translate"
event with the new translation and the data
object if it was specified when the method was called."transform"
event with the new transformation matrix and the data
object if it was specified when the method was called.paper.on('scale', (sx, sy, data) => console.log(sx, sy, data.foo));
paper.on('translate', (tx, ty, data) => console.log(tx, ty, data.foo));
paper.on('transform', (matrix, data) => console.log(matrix, data.foo));
paper.matrix({ a: 2, b: 0, c: 0, d: 2, e: 10, f: 10 }, { foo: 'bar' }); // will trigger all events
paper.pageOffset()
Returns coordinates of the paper viewport, relative to the document.
paper.pageToLocalPoint(p)
Transform the point p
defined in the page coordinate system to the local coordinate system.
paper.on('blank:pointerup', function(evt) {
var pagePoint = g.Point(evt.pageX, evt.pageY);
var localPoint1 = this.pageToLocalPoint(pagePoint);
// alternative method signature
var localPoint2 = this.pageToLocalPoint(evt.pageX, evt.pageY);
// Move the element to the point, where the user just clicks.
element.position(localPoint1.x, localPoint1.y);
});
paper.pageToLocalRect(rect)
Transform the rectangle rect
defined in the page coordinate system to the local coordinate system.
var x, y;
paper.on('blank:pointerdown', function(evt) {
x = evt.pageX;
y = evt.pageY;
});
paper.on('blank:pointerup', function(evt) {
var pageRect = g.Rect(x, y, evt.pageX - x, evt.pageY - y).normalize();
var localRect1 = this.pageToLocalRect(pageRect);
// alternative method signature
var localRect2 = this.pageToLocalRect(x, y, evt.pageX - x, evt.pageY - y).normalize();
// Move and resize the element to cover the area, that the user just selected.
element.position(localRect1.x, localRect1.y);
element.resize(localRect1.width, localRect1.height);
});
paper.paperToLocalPoint(p)
Transform the point p
defined in the paper coordinate system to the local coordinate system.
var paperCenter = g.Point(paper.options.width / 2, paper.options.height / 2);
var localPoint1 = paper.paperToLocalPoint(paperCenter);
// alternative method signature
var localPoint2 = paper.paperToLocalPoint(paper.options.width / 2, paper.options.height / 2);
// Move the element to the center of the paper viewport.
var elSize = element.size();
element.position(localPoint1.x - elSize.width, localPoint1.y - elSize.height);
paper.paperToLocalRect(rect)
Transform the rectangle rect
defined in the paper coordinate system to the local coordinate system.
var paperRect = g.Rect(0, 0, paper.options.width, paper.options.height);
var localRect1 = paper.paperToLocalRect(paperRect);
// alternative method signature
var localRect2 = paper.paperToLocalRect(0, 0, paper.options.width, paper.options.height);
// Move and resize the element to cover the entire paper viewport.
element.position(localRect1.x , localRect1.y);
element.size(localRect1.width, localRect1.height);
The following list contains properties exposed by the paper object:
svg
- a reference to the SVG document object the paper uses to render all the graphics.layers
- a reference to the SVG group <g>
element that contains all the layers of the paper. Paper transformations such as scale is performed on this element and it affects all the layers inside.cells
(cells layer) - a reference to the SVG group <g>
element the paper wraps all the rendered elements and links into.tools
(tools layer) - a reference to the SVG group <g>
element the paper wraps all the rendered tools into.defs
- a reference to the SVG <defs> element used to define SVG elements for later reuse. This is also a good place to put SVG masks, patterns, filters and gradients.Normally, you do not need to access these properties directly but in some (advanced) situations, it is handy to have access to them.
paper.removeTools()
Remove all tools view objects attached to all element and link views on this paper.
paper.requireView(cell[, opt])
Ensure that the view associated with the cell
model is attached to the DOM and that it is updated.
This function finds the view by the cell model. If the view is not part of the paper's DOM (e.g. because it was outside the paper viewport), this function attaches it. Additionally, the view is updated to reflect any changes that may have been done on the cell model in the meantime.
Certain CellView methods require the view to be updated and present in the DOM to function properly (e.g. elementView.getBBox/linkView.getBBox). In async
papers, you should precede calls to these methods with this function to guard against inconsistencies.
If opt.silent
is set to true
, the paper beforeRender and afterRender callbacks are not fired (and 'render:done'
event is not triggered).
paper.scale()
If the method is called with no parameter the current paper scale
transformation is returned.
paper.scale() // returns e.g. { sx: 2, sy: 2 }
paper.scale(sx, [sy, data])
Lets you modify the scale of a paper.
paper.scale(2) // scale 2x (uniformly)
paper.scale(2,3) // scale 2x in `x` axis 3x in `y` axis (non-uniformly)
If the method is called the "scale"
and "transform"
events are triggered on the paper (only if the new scale differs from the previous).
data
object if it was specified when the method was called.
paper.on('scale', (sx, sy, data) => console.log(sx, sy, data.foo));
paper.on('transform', (matrix, data) => console.log(matrix, data.foo));
paper.scale(2, 2, { foo: 'bar' }); // will trigger both events
paper.scaleContentToFit([opt])
Scale the paper content so that it fits the paper dimensions.
Deprecated alias of the transformToFitContent.
paper.scaleUniformAtPoint(scale, point, [data])
Lets you modify the transformation of a paper, so that the paper is scaled uniformly around the given point.
// scale 2x around the point (100, 100)
paper.scaleUniformAtPoint(2, { x: 100, y: 100 });
When the method is called, a couple of transformation events (e.g. "transform"
event) might be triggered. For more details, please see the matrix() method.
The method is ideal for zooming in/out around the mouse pointer.
paper.on('paper:pinch', function(evt, x, y, sx) {
evt.preventDefault();
const { sx: sx0 } = paper.scale();
paper.scaleUniformAtPoint(sx0 * sx, { x, y });
});
paper.on('paper:pan', function(evt, tx, ty) {
evt.preventDefault();
evt.stopPropagation();
const { tx: tx0, ty: ty0 } = paper.translate();
paper.translate(tx0 - tx, ty0 - ty);
});
paper.setDimensions(width, height, [data])
Change dimensions of a paper. Dimensions should always be passed to the options object of the joint.dia.Paper
constructor. Use setDimensions()
to change dimensions of the paper later on if needed.
Type | Description |
---|---|
Number | Dimension in pixels (e.g 800 ). |
String |
Dimension as CSS property value (e.g "100%" ). Make sure the paper container element has size defined.
|
null | No dimension set. Useful when size of the paper controlled in CSS. |
If new dimensions are set, the "resize"
event is triggered (only if the size has changed). The event handler receives the current size and the data
object if it was specified when the method was called.
paper.on('resize', (width, height, data) => console.log(width, height, data.foo));
paper.setDimensions(800, 600, { foo: 'bar' }); // will trigger the event
paper.setGrid(gridOption)
Set the type of visual grid on the paper. If a falsy value is provided, the grid is removed.
paper.setGrid(); // removes the grid visual
paper.setGrid(true); // default pattern (dot) with default settings
paper.setGrid('mesh'); // pre-defined pattern with default settings
paper.setGrid({ name: 'dot', args: { color: 'hsla(212, 7%, 75%, 0.5)' }});
paper.setGridSize(gridSize)
Set the grid size of the paper.
paper.setInteractivity(interactive)
Set the interactivity of the paper. For example, to disable interactivity:
paper.setInteractivity(false);
paper.showTools()
Show all tools attached to all element and link views on this paper.
paper.transformToFitContent([opt])
Transforms the paper so that it fits the content.
The function accepts an object with additional settings (all optional):
opt.padding
– a number or an object of the form { top?: [number], right?: [number], bottom?: [number], left?: [number], vertical?: [number], horizontal?: [number] }
that specifies the width of additional padding that should be added around the resulting (scaled) paper content. Default is 0
.opt.preserveAspectRatio
– should the original aspect ratio of the paper content be preserved in the scaled version? Default is true
.opt.minScaleX
, opt.minScaleY
, opt.maxScaleX
, opt.maxScaleY
– the minimum and maximum allowed scale factors for both axes.opt.scaleGrid
– a number to be used as a rounding factor for the resulting scale. For example, if you pass 0.2
to this option and the scale factor is calculated as 1.15
, then the resulting scale factor will be rounded to 1.2
.opt.useModelGeometry
– should paper content bounding box be calculated from cell models instead of views? Default is false
. See the documentation of the paper.getContentBBox
function for more details.opt.fittingBBox
– an object of the form { x: [number], y: [number], width: [number], height: [number] }
is the area of the paper that the content should be scaled to. By default opt.fittingBBox
is { x: 0, y: 0, width: paper.options.width, height: paper.options.height }
, i.e. the bounding box of the paper in the paper coordinate system.opt.contentArea
– an object of the form { x: [number], y: [number], width: [number], height: [number] }
is the area representing the content in local coordinate system that should be scaled to fit the opt.fittingBBox
. By default opt.contentArea
is paper.getContentArea(opt)
.opt.verticalAlign
– a string which specifies how the content shold be vertically aligned in the transformed paper. The options are 'top'
, 'middle'
and 'bottom'
. By default the opt.verticalAlign
is 'top'
.opt.horizontalAlign
– a string which specifies how the content shold be horizontally aligned in the transformed paper. The options are 'left'
, 'middle'
and 'right'
. By default the opt.horizontalAlign
is 'left'
.The function is illustrated in our paper demo.
paper.translate()
If the method is called with no parameter the current paper translate
transformation is returned.
paper.translate() // returns e.g. { tx: 100, ty: 100 }
paper.translate(x, y, [data])
Lets you modify the origin (zero coordinates) of a paper.
paper.translate(100, 200) // set the origin to `x=100` and `y=200`
paper.translate(100) // same as calling `translate(100,0)`
If the method is called the "translate"
and "transform"
events are triggered on the paper (only if the origin has changed).
The event handlers receive the current translation / matrix and the data
object if it was specified when the method was called.
paper.on('translate', (tx, ty, data) => console.log(tx, ty, data.foo));
paper.on('transform', (matrix, data) => console.log(matrix, data.foo));
paper.translate(100, 200, { foo: 'bar' }); // will trigger both events
paper.unfreeze([opt])
Update the views of an async
paper to make sure that the views reflect the cells' models; unfreeze the paper afterward.
Several extra arguments may be provided in the opt
object:
batchSize | number | For async papers, how many views should there be per one asynchronous process? Default is 1000 . |
|||||||||
progress | function | Callback function that is called whenever a batch is finished processing. The callback function is provided with three arguments:
|
|||||||||
viewport | function | Callback function to determine whether a given view should be added to the DOM. By default, paper.options.viewport is used, but you may provide a different callback function. The format of the callback method is described in the option's documentation, as are examples of usage.
|
|||||||||
beforeRender | function | Callback function executed before processing scheduled updates. By default, paper.options.beforeRender is used, but you may provide a different callback function. The format of the callback method is described in the option's documentation.
| |||||||||
afterRender | function | Callback function executed after all scheduled updates had been processed. By default, paper.options.afterRender is used, but you may provide a different callback function. The format of the callback method is described in the option's documentation.
|
Use paper.freeze()
to freeze the paper again.
paper.updateViews([opt])
Update views in a frozen async
paper to make sure that the views reflect the cells' models; keep the paper frozen afterwards.
Several extra arguments may be provided in the opt
object:
batchSize | number | For async papers, how many views should there be per one asynchronous process? Default is 1000 . |
|||||||||
progress | function | Callback function that is called whenever a batch is finished processing. The callback function is provided with three arguments:
|
|||||||||
viewport | function | Callback function to determine whether a given view should be added to the DOM. By default, paper.options.viewport is used, but you may provide a different callback function. The format of the callback method is described in the option's documentation, as are examples of usage.
|
afterRender
- Callback function executed after all scheduled updates had been processed (both asynchronous and synchronous rendering). The callback function is provided with the following arguments:
stats | object | Statistics about the current update. |
options | object | Parameters the current updates were scheduled with. |
paper | joint.dia.Paper | This paper instance |
allowLink
- expects a function returning a boolean. The function is run when the user stops interacting with a linkView's arrowhead (source or target).
If the function returns false
, the link is either removed (for links which are created during the interaction) or reverted to the state before the interaction.
// Return `false` if a graph cycle is detected (`graphlib` refers to a dependency of the DirectedGraph plugin).
paper.options.allowLink = function(linkView, paper) {
var graph = paper.model;
return graphlib.alg.findCycles(graph.toGraphLib()).length === 0;
}
anchorNamespace
- built-in anchors are defined by default on the joint.anchors
namespace.
It is also possible to define custom anchors on this namespace. If you would like JointJS to look for anchor
definitions under a different namespace, you can set the anchorNamespace
option to your desired
namespace.
async
- when true
, the paper uses asynchronous rendering to display graph cells (i.e. cells added either with graph.resetCells()
or graph.addCells()
methods).
This is very useful for adding a large number of cells into the graph. The rendering performance boost is significant and it doesn't block the UI. However, asynchronous rendering brings about some caveats – at the time when you call another function...
paper.options.viewport
function.paper.options.frozen
or paper.freeze()
.This is an issue because certain CellView/Paper methods require the view to be updated and present in the DOM to function properly (e.g. paper.findViewByModel()
or element.findView()
/link.findView()
, as well as paper.getContentBBox()
and elementView.getBBox()
/linkView.getBBox()
).
The problem may be circumvented in several Paper methods via the useModelGeometry
option to force using model calculations instead of view measurements (e.g. paper.getContentBBox()
, paper.getContentArea()
, paper.scaleContentToFit()
, paper.fitToContent()
). In this case, the methods refer to the (always up-to-date) model data.
For the methods that truly need a to refer to a CellView, one way to prevent inconsistencies is to rely on the 'render:done'
paper event. This event signals that all scheduled updates are done and that the state of cell views is consistent with the state of the cell models.
Alternatively, you may trigger a manual update immediately before a sensitive function call. JointJS offers several suitable methods:
paper.requireView()
- bring a single view into the DOM and update it.paper.dumpViews()
- bring all views into the DOM and update them.paper.updateViews()
- update all views.autoFreeze
- By default, the paper periodically checks the viewport calling viewport
callback for every view.
The options autoFreeze
can be used to suppress this behavior. When the option is enabled, it freezes the paper as soon as there are no more updates and unfreezes it when there is an update scheduled.
(paper has no updates to perform when hasScheduledUpdates() returns false
).
In that case, the user needs to checkViewport
manually when he wants to reinvoke viewport
when external changes occurred, e. g. during scrolling and zooming. The freeze state will be set on the next frame cycle after processing all updates. On this cycle paper will call viewport
callback
and freeze itself.
An object defining the paper background color and image. It defaults to false
meaning there is no background set. The configuration object can have the following properties.
background-color
property.
e.g 'red'
, 'rgba(255, 255, 0, 0.5)'
, 'radial-gradient(ellipse at center, red, green);'
'/my-background.png'
.{ x: Number, y: Number }
defining the background image position. It also allows to use all the CSS background-position
property values.
In that case all the paper transformations have no impact on the background image position. It defaults to center
.{ width: Number, height: Number }
defining the background image size. It also allows to use all the CSS background-size
property values. In that case all the paper transformations have no impact on the background size. It defaults to auto auto
.background-repeat
property and few more defined by JointJS. Those are flip-x
, flip-y
, flip-xy
and watermark
. It defaults to no-repeat
.0.5
uses only 50% the image size for creating a pattern). Applicable only for the JointJS repeat
option values. It defaults to 1
.[0,1]
specifying the transparency of the background image (0
is fully transparent and 1
is fully opaque). It defaults to 1
.'watermark'
repeat
option. It defaults to 20
deg.background: {
color: '#6764A7',
image: 'jointjs-logo.png',
repeat: 'watermark',
opacity: 0.3
}
beforeRender
- Callback function executed before processing scheduled updates (both asynchronous and synchronous rendering). The callback function is provided with the following arguments:
options | object | Parameters the current updates were scheduled with. |
paper | joint.dia.Paper | This paper instance. |
cellViewNamespace
- In order for the JointJS paper
to read cell view definitions from the correct location,
the paper
option cellViewNamespace
must be provided. Built-in cell view definitions are usually located in the
joint.shapes
namespace, so this is a common namespace to use. It's possible to a add custom cell view to this namespace, or
alternatively, you may like to use a different namespace completely.
For example, if joint.shapes
is provided as the value of cellViewNamespace
, and a cell is of type
'custom.Element'
, then the paper
looks up the joint.shapes.custom.ElementView
object type when rendering the
cell onto the screen. If cellViewNamespace
is set with a value of myCustomNamespace
, the paper will read view
definitions from the myCustomNamespace.custom.ElementView
object instead. This option is often used in combination with the
cellNamespace
option on the joint.dia.Graph
object.
clickThreshold
- When number of mousemove events exceeds the clickThreshold
there is no pointerclick
event triggered after mouseup. It defaults to 0
.
connectionPointNamespace
- built-in connectionPoints are defined by default on the
joint.connectionPoints
namespace. It is also possible to define custom connectionPoints
on this namespace. If you would like JointJS to look for connectionPoint definitions under a different
namespace, you can set the connectionPointNamespace
option to your desired
namespace.
connectionStrategy
- the connection strategy to use on this paper. It can either be a function or null
. JointJS provides a collection of built-in connection strategies in the joint.connectionStrategies
namespace; alternatively, a custom function may be provided. See the link geometry connectionStrategy documentation for more information.
connectorNamespace
- built-in connectors are defined by default on the joint.connectors
namespace.
It is also possible to define custom connectors on this namespace. If you would like JointJS to look for connector
definitions under a different namespace, you can set the connectorNamespace
option to your desired
namespace.
defaultAnchor
- an anchor function that is used by links if no anchor
property is defined for a link end. It can either be an object (with a name
and optional args
) or a function. JointJS provides a collection of built-in anchor functions in the joint.anchors
namespace; alternatively, a custom function may be provided. See the link geometry anchor documentation for more information.
defaultConnectionPoint
- a connection point function that is used by links if no connectionPoint
property is defined for a link end. It can either be an object or a function. JointJS provides a collection of built-in connection point functions in the joint.connectionPoints
namespace; alternatively, a custom function may be provided. See the link geometry connectionPoint documentation for more information.
defaultConnector
- a connector that is used by links if no connector
property is defined on them. It can either be an object or a function. JointJS provides a collection of built-in connectors in the joint.connectors
namespace; alternatively, a custom function may be provided. See the link geometry connector documentation for more information.
defaultLink
- link that should be created when the user clicks and drags an active magnet (when creating a link from a port via the UI). Defaults to new joint.shapes.standard.Link()
. It can also be a function with signature function(cellView, magnet) {}
that must return an object of type joint.dia.Link
.
defaultLinkAnchor
- a link anchor function that is used by links if no linkAnchor
property is defined for a link end. It can either be an object (with a name
and optional args
) or a function. JointJS provides a collection of built-in link anchor functions in the joint.linkAnchors
namespace; alternatively, a custom function may be provided. See the link geometry linkAnchor documentation for more information.
defaultRouter
- a router that is used by links if no router
property is defined on them. It can either be an object or a function. JointJS provides a collection of built-in routers in the joint.routers
namespace; alternatively, a custom function may be provided. See the link geometry router documentation for more information.
drawGrid
- option whether the paper grid should be drawn or not. It could be a boolean
or an object
. It defaults to false
.
Define color
and thickness
to adjust the default grid pattern:
color | string | color of the default grid pattern. |
---|---|---|
thickness | number | thickness of the default grid pattern. |
There are also some pre-defined grid patterns: dot
, fixedDot
, mesh
, doubleMesh
. If you'd like to use these patterns, define drawGrid
options as follows:
name | string | name of the pre-defined pattern. Can be either dot , fixedDot , mesh , doubleMesh |
---|---|---|
args | object | array | an extra argument for the pre-defined grid patterns. It can be either an object (e.g. dot , mesh ) or an array (e.g. doubleMesh ) |
Pre-defined grids with default settings:
The paper from the images below has been scaled 2 times and has gridSize set to 10.
drawGrid: true // default pattern (dot) with default settings
drawGrid: 'mesh' // pre-defined pattern with default settings
drawGrid: { name: 'mesh', args: { color: 'black' }}
drawGrid: {
name: 'doubleMesh',
args: [
{ color: 'red', thickness: 1 }, // settings for the primary mesh
{ color: 'green', scaleFactor: 5, thickness: 5 } //settings for the secondary mesh
]}
el
- CSS selector, or a DOM element holding the container for the paper
elementView
- object that is responsible for rendering an element model into the paper. Defaults to joint.dia.ElementView
. It can also be a function of the form function(element)
that takes an element model and should return an object responsible for rendering that model onto the screen (in most cases a subtype of joint.dia.ElementView
)
embeddingMode
- when set to true
, the paper is set to embedding mode. In this mode,
when you drag an element and drop into another element, the element below becomes a parent
of the dropped element (the dropped element gets automatically embedded into the parent element). Similarly, when you drag an
element out of its parent, the element gets automatically unembedded from its parent. The embedding mode
also makes sure all the connected links and child elements have proper z
index set so that they stay visible.
To control what elements can be embedded into what other elements, use the validateEmbedding()
function
on the paper (see below).
This is useful for hierarchical diagrams. See the DEVS demo on how this can be used.
findParentBy
- determines the way how a cell finds a suitable parent when it's dragged over the paper. The cell with the highest z-index (visually on the top) will be chosen.
findParentBy
option comes to play when the paper is in embedding mode. All possible values are
Value | Description |
---|---|
'bbox' (default) |
Choose the parent from the elements under the element rectangle. |
'pointer' |
Choose the parent from the elements under the cursor (pointer). |
'center' | 'origin' | 'corner' | 'topRight' | 'bottomLeft' |
Choose the parent from the elements under a specific point of the element rectangle. |
function |
The function accepts an elementView . evt , x , y and it returns array of possible candidates.
|
frontParentOnly
- when set to the default true
, only the element at the very front is taken into account for
embedding. If disabled, the elements under the dragged view are tested one by one (from front to back) until a valid parent is found.
// If a child element is embedded and covers the area of 'app.Container', a third element is unable to be embedded
validateEmbedding: function(childView, parentView) {
const model = parentView.model;
if (model.get('type') === 'app.Container') return true;
return false;
},
frontParentOnly: true,
frozen
- when true
on an async
paper, the paper is frozen. This means that changes made in this paper's graph are not reflected by the paper until this state is reversed. Use
paper.unfreeze()
to unfreeze the paper, and paper.freeze()
to freeze it again. (You may also use paper.updateViews()
to manually refresh the paper and leave it frozen.)
Example:
var graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
graph.resetCells([cell1, cell2]);
var paper = new joint.dia.Paper({
el: document.getElementById('paper-holder'),
model: graph,
cellViewNamespace: joint.shapes,
frozen: true
});
paper.unfreeze();
gridSize
- size of the grid in pixels
guard
- guard paper from handling a browser UI event. This function is of the form
function(evt, view)
and should return true
if you want to prevent the paper from handling the event evt
,
false
otherwise. This is an advanced option that can be useful if you have your own logic for handling events.
height
- height of the paper (see setDimensions()).
highlighterNamespace
- built-in highlighters are defined by default on the
joint.highlighters
namespace. Existing and custom highlighters are defined by extending
the base class. If you would like JointJS to look for highlighter
definitions under a different namespace, you can set the highlighterNamespace
option
to your desired namespace.
highlighting
- Configure which highlighter to use (if any) for each type of interaction.
Name | Enum | Description | Default |
---|---|---|---|
default | joint.dia.CellView.Highlighting.DEFAULT | the default highlighter to use (and options) when none is specified |
|
connecting | joint.dia.CellView.Highlighting.CONNECTING | when a valid link connection can be made to an element | Not defined. The default is used. |
embedding | joint.dia.CellView.Highlighting.EMBEDDING | when a cell is dragged over another cell in embedding mode | Not defined. The default is used. |
magnetAvailability | joint.dia.CellView.Highlighting.MAGNET_AVAILABILITY | when showing all magnets to which a valid connection can be made |
|
elementAvailability | joint.dia.CellView.Highlighting.ELEMENT_AVAILABILITY | when showing all elements to which a valid connection can be made |
|
If a type is set to false
, no highlighter is used.
If a type is set to null | undefined
, the default highlighter is used.
Example usages:
new joint.dia.Paper({
highlighting: {
'default': {
name: 'stroke', // `joint.highlighters.stroke`
options: {
padding: 2
}
},
'connecting': {
name: 'addClass', // `joint.highlighters.addClass`
options: {
className: 'highlight-connecting'
}
},
// Disable highlighter for embedding
'embedding': false
}
});
new joint.dia.Paper({
// Disable all highlighters
highlighting: false
});
List of available highlighters and their options:
If you need to highlight an element or a link differently based on the cell type or one of its attribute, you can disable the paper highlighting and add a highlighter manually.
// Disable Embedding
paper.options.highlighting.embedding = false;
const MyHighlighters = {
EMBEDDING: 'embedding-highlighter'
};
paper.on('cell:highlight', (cellView, node, { type }) => {
if (type === joint.dia.CellView.Highlighting.EMBEDDING) {
const isLink = cellView.model.isLink();
joint.highlighters.mask.add(cellView, node, MyHighlighters.EMBEDDING, {
attrs: {
'stroke': isLink ? 'red' : 'blue'
}
});
}
});
paper.on('cell:unhighlight', (cellView, node, { type }) => {
if (type === joint.dia.CellView.Highlighting.EMBEDDING) {
joint.highlighters.mask.remove(cellView, MyHighlighters.EMBEDDING);
}
});
By default, when a user connects a link, the target node for the highlighter is either the root of the cell or a magnet. To change this use highlighterSelector attribute.
const element = new joint.shapes.standard.Rectangle({
attrs: {
root: {
highlighterSelector: 'body'
}
}
});
// When a user tries to connect a link to the element.
paper.on('cell:highlight', (elementView, node) => {
assert.notOk(node === elementView.el);
assert.ok(node === elementView.el.querySelector('[joint-selector="body"]');
});
interactive
- Configure which of the default interactions with elements and links should be enabled.
The property value defaults to { labelMove: false }
. This can be overwritten in three ways: with a boolean value, with an object specifying interaction keys, or with a function.
If set to false
, all interactions with elements and links are disabled. If set to true
, all interactions are enabled.
// disable all interaction
var paper = new joint.dia.Paper({
// ...
interactive: false,
});
// enable all interaction (including labelMove)
var paper = new joint.dia.Paper({
// ...
interactive: true,
});
Using an object, specific interactions may be disabled by assigning false
to their corresponding property name. It is not necessary to pass true
values; all omitted properties are assigned true
by default. (Note that the passed object is not merged with the default; unless labelMove
is explicitly excluded, it becomes enabled.) A full list of recognized interaction keys is provided below.
// disable labelMove
var paper = new joint.dia.Paper({
// ...
interactive: { labelMove: false }
});
// disable all element interactions
var paper = new joint.dia.Paper({
// ...
interactive: { elementMove: false, addLinkFromMagnet: false }
});
If defined as a function, the function is passed cellView
(the elementView/linkView the user is about to interact with) as the first parameter, followed by the name of the event ('pointerdown'
, 'pointermove'
, ...) that triggered the interaction. The return value of the function is then interpreted in the way specified above (false
causes all interaction to be disabled, an object disables specific interactions, etc.).
// disable link interactions for cellViews when a custom property is set
var paper = new joint.dia.Paper({
// ...
interactive: function(cellView) {
if (cellView.model.get('disableLinkInteractions')) {
return {
linkMove: false,
labelMove: false,
};
}
// otherwise
return true;
}
});
The example below has all interactions on the link and on the elements enabled. This is the default behavior:
The following tables present a list of all recognized interaction keys, followed by an example of a paper with only the related interactive property set to true
(and all other properties set to false
).
Links:
linkMove |
Is the user allowed to move the link? |
---|---|
labelMove |
Is the user allowed to move the link label? Use the |
Elements:
elementMove |
Is the user allowed to move the element? |
---|---|
addLinkFromMagnet |
Is the user allowed to add connections from magnets/ports? |
The stopDelegation
option is special. If it is true
(default), the element's elementMove
option determines whether the element responds to user drag.
However, if stopDelegation
is false
and the element is embedded within a parent, the user's dragging is delegated to the embedding parent. The parent's elementMove
option then determines whether both elements respond to user drag. The behavior is recursive. If the embedding parent has stopDelegation: false
, it delegates to its own embedding parent's elementMove
option and so on. If all children within an embedding have stopDelegation
set to false
, then no matter which element is dragged by the user, the whole embedding is dragged.
If the element is not embedded within an element, the stopDelegation
option is ignored (treated as true
). There is no other element to delegate to. Then elementMove
determines whether the element responds to user drag.
In the following example, both embedded elements have stopDelegation: false
. Thus, when the embedded element is dragged by the user, the parent ancestor (Movable
) is dragged instead. When the parent ancestor has elementMove
disabled (Not movable
), nothing happens.
stopDelegation |
---|
labelsLayer
- controls the stacking context of the link labels.
Value | Description |
---|---|
false (default) | The labels are rendered as part of the links. Overlapping links with a larger z can cover link labels with a smaller one. |
true | The labels are rendered into its own layer on top of other elements and links. A link's connection can cover no label. |
linkAnchorNamespace
- built-in linkAnchors are defined by default on the joint.linkAnchors
namespace. It is also possible to define custom linkAnchors on this namespace. If you would like JointJS to
look for linkAnchor definitions under a different namespace, you can set the linkAnchorNamespace
option to your desired namespace.
linkPinning
- when set to true
(the default), links can be pinnedto the paper meaning a source or target of a link can be a point. If you do not want to let the user drag a link and drop it somewhere in a blank paper area, set this to
false
. The effect is
that the link will be returned to its original position whenever the user drops it somewhere in a blank paper area.
linkView
- object that is responsible for rendering a link model into the paper. Defaults to joint.dia.LinkView
. It can also be a function of the form function(link)
that takes a link model and should return an object responsible for rendering that model onto the screen (in most cases a subtype of joint.dia.LinkView
).
magnetThreshold
- When defined as a number, it denotes the required mousemove events before a new link is created from a magnet. When defined as keyword 'onleave'
, the link is created when the pointer leaves the magnet DOM element. It defaults to 0
.
markAvailable
- marks all the available magnets or elements when a link is dragged (being reconnected). Default is false
. This gives a hint to the user to what other elements/ports this link can be connected.
What magnets/cells are available is determined by the validateConnection
function.
Internally, available magnets (SVG elements) are given the 'available-magnet'
class name and all the available cells the 'available-cell'
class name. This allows you to change the
styling of the highlight effect.
model
- joint.dia.Graph
object
moveThreshold
- Number of required mousemove events before the first pointermove event is triggered. It defaults to 0
.
multiLinks
- when set to false
, an element may not have
more than one link with the same source and target element.
overflow
- if set to true
, the content of the paper is not clipped and may be rendered outside the paper area. If set to false
, the content is clipped, if necessary, to fit the paper area.
preventContextMenu
- Prevents default context menu action (when right button of the mouse is clicked). It defaults to true
.
preventDefaultBlankAction
- Prevents default action when an empty paper area is clicked. Setting the option to false
will make the paper pannable inside a container on touch devices. It defaults to true
.
preventDefaultViewAction
- Prevents default action when a cell view is clicked. For example, setting the option to false
will cause clicking on the view to blur the document active element. It defaults to true
.
restrictTranslate
- restrict the translation (movement) of elements by a given bounding box.
If set to true
, the user will not be able to move elements outside the boundary of the paper area. It's set to false
by default.
This option also accepts a function with signature function(elementView, x0, y0)
in which case it must return one of the following:
{ x: Number, y: Number, width: Number, height: Number }
) that defines the area in which the element represented by elementView
can be moved.
x,y
return a new position { x: Number, y: Number }
. The function is invoked every time the user moves the pointer.
For example, to restrict translation of embedded elements by the bounding box defined by their parent element, you can do:
restrictTranslate: function(elementView) {
const parent = elementView.model.getParentCell();
return parent
? parent.getBBox() // children movement is constrained by the parent area
: null; // parent movement is not restricted
}
Or restrict an element movement along a path:
restrictTranslate: function(elementView, x0, y0) {
// x0 and y0 are pointer coordinates at the start of the translation
const path = new g.Path('M 20 20 L 200 200 L 380 20');
const { x: x1, y: y1, width, height } = elementView.model.getBBox();
// The initial difference between the pointer coordinates and the element position
const dx = x1 - x0;
const dy = y1 - y0;
return function(x, y) {
// Find the point on the path.
const point = path.closestPoint({ x: x - dx, y: y - dy });
// Put the center of the element on this point
point.offset(-width / 2, -height / 2);
return point;
};
}
routerNamespace
- built-in routers are defined by default on the joint.routers
namespace.
It is also possible to define custom routers on this namespace. If you would like JointJS to look for router
definitions under a different namespace, you can set the routerNamespace
option to your desired
namespace.
snapLabels
- when enabled, force a dragged label to snap to its link. The default is false
, which means that the label may also be dragged around the link.
snapLinks
- when enabled, force a dragged link to snap to the closest element/port in the given radius. The option accepts true
in which case default values are used or an object of the form
{ radius: <value> }
where you can specify the radius (default is 50).
snapLinksSelf
- when enabled, forces a dragged link end to snap to the x
or y
coordinate of the closest point on the current link (one of the ends and vertices) if the distance to the point on one axis is lesser than the specified radius. The option accepts true
in which case default values are used or an object of the form
{ radius: <value> }
where you can specify the radius (the default is 20).
sorting
- the sorting type to use when rendering the views in this paper (i.e. In what order should the views be rendered?
).
The SVG 1.1 format does not have built-in z-index functionality, so JointJS implements it programmatically. You can set the z-index directly using regular mvc.Model set('z')
/get('z')
methods or through element.toFront()
/element.toBack()
/link.toFront()
/link.toBack()
methods. See the element Z documentation for more information.
The Paper object exposes a sorting
object with three values that may be used as values of this option:
joint.dia.Paper.sorting.APPROX
- (default) render views according to their z-values. Views with different z-value are rendered in order, but the ordering of views with the same z-value is indeterminate. Similar in functionality to the EXACT
option, but much faster.joint.dia.Paper.sorting.EXACT
- render views in exactly the same order as reported by graph.getCells()
(views with different z-values are rendered in order, and views with the same z-value are rendered in the order in which they were added). This is by far the slowest option, present mainly for backwards compatibility.joint.dia.Paper.sorting.NONE
- render views in an indeterminate order. (Note that this setting disables all toFront
/toBack
functions mentioned above.)validateConnection(cellViewS, magnetS, cellViewT, magnetT, end, linkView)
-
a function that allows you control which link connections can be made between the source cellView/magnet (cellViewS/magnetS
), and the target cellView/magnet (cellViewT/magnetT
).
end
is either the "source"
or the "target"
, and indicates which end of the link is being dragged.
This is useful for defining whether a link starting in port POut of element A can lead to port PIn of element B. By default, all connections are allowed.
// The default allows all element connections
validateConnection: function(cellViewS, _magnetS, cellViewT, _magnetT, end, _linkView) {
return (end === 'target' ? cellViewT : cellViewS) instanceof ElementView;
}
// Other examples
validateConnection: function(cellViewS, magnetS, cellViewT, magnetT, end, linkView) {
// Prevent Link to Link connections
if (cellViewT.model.isLink() || cellVIewS.model.isLink()) return false;
// Prevent loop linking - This means the source and target cannot connect to the same magnet (e.g. port)
if (magnetS === magnetT) return false;
// Only allow target end to be reconnected
if (end === 'source') return false;
// Don't allow connection to cellView with 'isInactive' attribute of value true
if (cellViewS.model.get('isInactive') || cellViewT.model.get('isInactive')) return false;
// Prevent linking from input ports (assumes you have port groups setup)
if (magnetS && magnetS.getAttribute('port-group') === 'in') return false;
// Allow all other connections
return true;
}
validateEmbedding
- a function that allows you to control what elements can be embedded to
what other elements when the paper is put into the embeddingMode
(see above).
The function signature is: function(childView, parentView)
and should return
true
if the childView
element can be embedded into the parentView
element.
By default, all elements can be embedded into all other elements (the function returns true
no matter what).
validateMagnet(cellView, magnet, evt)
- decide whether to create a link if the user clicks a magnet. magnet
is the DOM element representing the magnet. By default, this function returns true
for magnets that are not explicitly set to "passive" (which is usually the case of input ports).
validateUnembedding
- a function that allows you to control which elements can be unembedded when the paper is put into the embeddingMode
(see above).
function(childView)
and should return
true
if the childView
element can be unembedded from the parent and become a new root.
true
no matter what).
false
is returned, the position of the element, as well as the parent reference is reverted to the values before dragging.
viewport
- a callback function that is used to determine whether a given view should be shown in an async
paper. If the function returns true
, the view is attached to the DOM; if it returns false
, the view is detached/unmounted from the DOM. The callback function is provided with three arguments:
view | mvc.View | The view in question |
isMounted | boolean | Is the view currently visible in the paper? |
paper | dia.Paper | This paper (for context) |
This function is meant to support hiding of views when they are outside the viewport. In most situations, it is unnecessary to render DOM elements that are not visible by the user; removing those elements from the DOM dramatically improves rendering times of huge graphs (thousands of views) and improves smoothness of user interaction. (If you do need to show a view that falls outside of this viewport, you can manually force the view to be shown using paper.requireView()
. If you need to show views according to a different viewport function, use paper.checkViewport()
. If you need to force all views to be shown, use paper.dumpViews()
.)
Example usage:
var viewportRect = new g.Rect(0, 0, 600, 400);
var graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
var paper = new joint.dia.Paper({
model: graph,
cellViewNamespace: joint.shapes,
async: true,
viewport: function(view) {
var model = view.model;
var bbox = model.getBBox();
if (model.isLink()) {
// vertical/horizontal links have zero width/height
// we need to manually inflate the bounding box
bbox.inflate(1);
}
// Return true if there is an intersection
// Return true if bbox is within viewportRect
return viewportRect.intersect(bbox);
}
});
The viewport
function can also be used to support collapsing/uncollapsing behavior:
var graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
var paper = new joint.dia.Paper({
model: graph,
cellViewNamespace: joint.shapes,
async: true,
viewport: function(view) {
// Return true if model is not hidden
return !model.get('hidden');
}
});
paper.on('element:pointerclick', function(view, evt) {
evt.stopPropagation();
toggleBranch(view.model);
});
function toggleBranch(root) {
var shouldHide = !root.get('collapsed');
root.set('collapsed', shouldHide);
graph.getSuccessors(root).forEach(function(successor) {
successor.set('hidden', shouldHide);
successor.set('collapsed', false);
});
}
width
- width of the paper (see setDimensions()).
The joint.dia.ToolsView
class works as a container for one set of link tools (an array of joint.dia.ToolView
objects). It is responsible for rendering the tools as a group when the tools view is attached to a joint.dia.LinkView.
To create a new tools view object, we call its constructor. Two optional arguments are accepted:
name | string | The name of this tools view. Default is null (no name). |
---|---|---|
tools | Array<dia.ToolView> | An array of tools that should be a part of the tools view. Default is [] (no tools). |
Creating a tools view is the second step in the process of setting up link tools on a link view:
// 1) creating link tools
var verticesTool = new joint.linkTools.Vertices();
var segmentsTool = new joint.linkTools.Segments();
var boundaryTool = new joint.linkTools.Boundary();
// 2) creating a tools view
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [verticesTool, segmentsTool, boundaryTool]
});
// 3) attaching to a link view
var linkView = link.findView(paper);
linkView.addTools(toolsView);
Every link view we want to attach to requires its own tools view object (ToolsView
objects are automatically reassigned to the last link view they are added to). Similarly, every tools view we create requires its own set of tools (ToolView
objects are automatically reassigned to the last toolsView.tools
array they were made part of).
toolsView.blurTools()
Stop focusing any tools within this tools view.
This function reverses the effect of the toolsView.focusTool
function applied to any tool in this tools view.
toolsView.focusTool(tool)
Focus the specified tool
(of the joint.dia.ToolView
type) within this tools view.
When a tool is focused
, all other tools within this tools view are hidden and the tool's opacity is changed according to the tool's focusOpacity
option. Only one tool can be focused at a time; the focus passes to the last tool within this tools view with which the focusTool
function was called.
The effect of this function can be reversed for a single tool
by the toolsView.blurTool
function. All tools can be unfocused by the toolsView.blurTools
function.
toolsView.getName()
Return the name of this tools view. If no name was set during the construction of the tools view, null
is returned.
toolsView.hide()
Hide all tools within this tools view.
toolsView.show()
Show all tools within this tools view.
layer - the stacking context of the tools view.
"tools" (default) |
Render the tools in front of all the cells. |
null |
Render the tools above the cell. It can be hidden by a cell with a higher z index. |
z - the stacking order (z-index) of the tools view in the given stacking context.
The joint.dia.ToolView
class is the parent class of all link tool views included in the joint.linkTools
namespace. It is responsible for rendering the tool within a joint.dia.ToolsView, when the tools view is attached to a joint.dia.LinkView.
Creating link tools objects is the first step in the process of setting up link tools on a link view:
// 1) creating link tools
var verticesTool = new joint.linkTools.Vertices();
var segmentsTool = new joint.linkTools.Segments();
var boundaryTool = new joint.linkTools.Boundary();
// 2) creating a tools view
var toolsView = new joint.dia.ToolsView({
name: 'basic-tools',
tools: [verticesTool, segmentsTool, boundaryTool]
});
// 3) attaching to a link view
var linkView = link.findView(paper);
linkView.addTools(toolsView);
Every link view we want to attach to requires its own tools view object (ToolsView
objects are automatically reassigned to the last link view they are added to). Similarly, every tools view we create requires its own set of tools (ToolView
objects are automatically reassigned to the last toolsView.tools
array they were made part of).
toolView.blur()
Stop focusing the tool within its containing toolsView
.
This function reverses the effect of the toolView.focus
function.
toolView.focus()
Focus the tool within its containing toolsView
.
When a tool is focused
, all other tools within the toolsView
are hidden and the tool's opacity is changed according to the tool's focusOpacity
option. Only one tool can be focused at a time; the focus passes to the last tool within a tools view on which the focus
function was called.
The effect of this function can be reversed by the toolView.blur
function.
toolView.getName()
Return the name of the tool. The names of built-in link tools are kebab-case versions of their class names (e.g. the name of joint.linkTools.SourceAnchor
is 'source-anchor'
).
When creating a custom tool (e.g. by extending the joint.linkTools.Button
class), it is recommended that you provide a custom name
prototype property, as well:
joint.linkTools.InfoButton = joint.linkTools.Button.extend({
name: 'info-button',
options: {
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
distance: 60,
offset: 0,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
}
});
toolView.hide()
Hide the tool.
The effect of this function can be reversed by the toolView.show
function.
toolView.show()
Show the tool.
The effect of this function can be reversed by the toolView.hide
function.
An element tool is a view that renders a certain type of control elements on top of the ElementView it is attached to; for example the Remove tool creates an interactive button above the element (this button then allow the user to remove the element). Element tools all inherit from the joint.dia.ToolView
class. A collection of tools is added to a ToolsView; a tools view is then added to the elementView with the linkView.addTools()
function.
The JointJS library comes with a collection of pre-made element tool definitions in the joint.elementTools
namespace:
Boundary
- shows element bboxRemove
- adds an interactive remove buttonTo create a new element tool, we call its constructor. Example:
var removeTool = new joint.elementTools.Remove({
rotate: true
});
In addition, the joint.elementTools
namespace contains a customizable button class:
Button
- adds a customizable buttonExample:
var infoTool = new joint.elementTools.Button({
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
x: '100%',
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
});
All of the built-in element tools accept the following optional argument, in addition to their own arguments:
focusOpacity | number | What should be the opacity of the tool when it is focused (e.g. with the toolView.focus function)? Default is undefined , meaning that the tool's opacity is kept unchanged. |
---|
Example:
var boundaryTool = new joint.elementTools.Boundary({
focusOpacity: 0.5
});
The Boundary
element tool renders a rectangular border to show the bounding box of the element. It accepts a few additional arguments, which can be passed as an object to the element tool constructor:
padding | number|object | This option determines whether the boundary area should be visually inflated and if so, by how much. Default is 10 ({ left: 10, top: 10, right: 10, bottom: 10 } ). |
---|---|---|
rotate | boolean | Should the boundary rotate according to the element angle? Default is false . |
useModelGeometry | boolean | If this option is set to true , the position of the boundary is calculated based on the dimensions of the element model. |
Example:
var boundaryTool = new joint.elementTools.Boundary({
focusOpacity: 0.5,
padding: 20,
useModelGeometry: true
});
The Button
element tool allows you to have a custom button rendered at a given position above the element. It accepts a few additional arguments, which can be passed as an object to the element tool constructor:
x | string | number | Use percentage strings (e.g. '40%' ) or calc() expression (e.g. 'calc(0.4 * w)' ) to position the button relatively to the element width/height. A number means distance from the top-left corner of the element. Default is 0 . |
---|---|---|
y | ||
rotate | boolean | Should the button rotate according to the element angle around the element center? Default is false . |
offset | object | Additional offset of the button from the position defined by x and y . Default is { x: 0, y: 0 } . |
action | function | What should happen when the user clicks the button? Default is undefined (no interaction).The callback function is expected to have the signature function(evt, elementView, buttonView) where evt is a DOM event. The element view is available inside the function as this ; the element model is available as this.model . |
markup | JSONMarkup | The markup of the button, provided in the JointJS JSON format. Default is undefined (no content). |
scale | number | Scale the button up or down on a 2D plane. The default is 1 . |
Example of a useful custom info button:
var infoButton = new joint.elementTools.Button({
focusOpacity: 0.5,
// top-right corner
x: '100%',
y: '0%'',
offset: { x: -5, y: -5 },
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
},
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}]
});
The elementTools.Button
class can also be extended, to create a reusable custom button type. Then, a new instance of the custom button type can be obtained by calling its constructor:
var InfoButton = joint.elementTools.Button.extend({
name: 'info-button',
options: {
focusOpacity: 0.5,
distance: 60,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
},
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}]
}
});
var infoButton = new InfoButton();
The Connect
tool allows the user to create links in a drag & drop fashion. The tool extends the Button tool and accepts additional arguments, which can be passed as an object to the connect tool constructor:
magnet | string SVGElement (view: dia.LinkView) => SVGElement |
Choose the source magnet of the link view which the new link should be connected to.
The callback function is expected to have the signature |
---|
Example:
const connectButton = new joint.elementTools.Connect({
x: '100%',
y: '0%',
offset: { x: -5, y: -5 },
magnet: 'body'
});
The Control
element tool is an abstract class which allows you to build tools to control the look or shape of an element by simply dragging a UI handle. It accepts a few additional arguments, which can be passed as an object to the element tool constructor:
selector | string | null | The element selector pointing to an element subnode, which the tool draws a frame around during dragging. If null provided, no frame will be shown. |
---|---|---|
padding | number | The padding between the area and the bounding box of the node designated by selector . |
handleAttributes | function | An object with SVG attributes to be applied to the tool's handle. |
scale | number | Scale the button up or down on a 2D plane. The default is 1 . |
namespace Control {
interface Options extends dia.ToolView.Options {
selector?: string;
padding?: number;
handleAttributes?: Partial<attributes.NativeSVGAttributes>
}
}
abstract class Control extends dia.ToolView {
protected getPosition(view: dia.ElementView): dia.Point;
protected setPosition(view: dia.ElementView, coordinates: g.Point): void;
protected resetPosition(view: dia.ElementView): void;
}
getPosition(view: dia.ElementView): dia.Point; |
The method should return the position of the handle based on a model value.
The position is defined in the element model coordinate system (point [0,0] is the top-left corner; point [width, height] is the bottom-right corner - the element's rotation does not affect this).
|
setPosition(view: dia.ElementView, coordinates: g.Point): void; |
The method is executed each time the handle is moved. It's supposed to set the new model value derived from the current pointer coordinates (defined in the element model coordinate system) |
resetPosition(view: dia.ElementView): void; |
The method is executed when the handle is double-clicked. It's supposed to reset the model value back to a default. |
Here's an ES5 example of a tool, which provides the user a way to modify the border radius of an rectangle.
var RadiusTool = elementTools.Control.extend({
getPosition: function(view) {
var model = view.model;
var size = model.size();
var tilt = model.topRy();
return { x: size.width / 2, y: 2 * tilt };
},
setPosition: function(view, coordinates) {
var model = view.model;
var size = model.size();
var tilt = Math.min(Math.max(coordinates.y, 0), size.height / 2);
model.topRy(tilt, { ui: true, tool: this.cid });
},
resetPosition: function(view) {
var radius = this.options.defaultRadius || 0;
model.attr(['body'], { rx: radius, ry: radius });
}
});
TypeScript example of the same control:
interface RadiusControlOptions extends elementTools.Control.Options {
defaultRadius?: number;
}
class RadiusControl extends elementTools.Control {
protected getPosition(view: dia.ElementView): dia.Point {
const { model } = view;
const radius = model.attr(['body', 'ry']) || 0;
return { x: 0, y: radius };
}
protected setPosition(view: dia.ElementView, coordinates: dia.Point): void {
const { model } = view;
const { height } = model.size();
const radius = Math.min(Math.max(coordinates.y, 0), height / 2);
model.attr(['body'], { rx: radius, ry: radius });
}
protected resetPosition(view): void {
const { model } = view;
const { defaultRadius = 0 } = this.options;
model.attr(['body'], { rx: defaultRadius, ry: defaultRadius });
}
}
Add the tool to a rectangle element to allow the user adjust its "border radius".
rectangle.findView(paper).addTools(new dia.ToolsView({
tools: [
new RadiusTool({ handleAttributes: { fill: 'orange' }})
]
}));
The HoverConnect
tool allows the user to create links from elements in a drag & drop fashion. The tool extends the Connect tool. The difference is that the button appears along the invisible track path (in the shape defined by trackPath
) at the point where the user moves the mouse over the track. It accepts additional arguments, which can be passed as an object to the hover connect tool constructor:
trackWidth | number | The thickness of the track path. The default is 15 . |
---|---|---|
trackPath | string (view: dia.ElementView) => string |
The SVG list of Path Commands. It may contain calc expressions which are evaluated in the context of the related element view (or model when useModelGeometry is set to true ). The default is 'M 0 0 H calc(w) V calc(h) H 0 Z' (a rectangle in size of the element). |
Example:
const hoverButton = new joint.elementTools.HoverConnect({
useModelGeometry: true,
trackPath: (view) => view.model.attr(['body', 'd'])
});
The Remove
element tool renders a remove button at a given position above the element. It accepts a few additional arguments, which can be passed as an object to the element tool constructor:
x | string | number | Use percentage strings (e.g. '40%' ) to position the button relatively to the element width/height. A number means distance from the top-left corner of the element. Default is 0 . |
---|---|---|
y | ||
rotate | boolean | Should the button rotate according to the element angle around the element center? Default is false . |
offset | object | Additional offset of the button from the position defined by x and y . Default is { x: 0, y: 0 } . |
action | function | What should happen when the user clicks the remove button? Default:
|
markup | JSONMarkup | The markup of the button, provided in the JointJS JSON format. Default is undefined (no content). |
scale | number | Scale the button up or down on a 2D plane. The default is 1 . |
Example:
var removeButton = new joint.elementTools.Remove({
focusOpacity: 0.5,
rotate: true,
// top-mid
x: '50%',
y: '0%',
offset: { x: 10, y: 10 }
});
env.addTest(name, fn)
Add a custom feature-detection test where name
is a string which uniquely identifies your feature test and fn
is a function that returns true
if the browser supports the feature, and false
if it does not.
joint.env.addTest('customTest', function() {
// Just as an example, we will always return true here.
return true;
});
if (joint.env.test('customTest')) {
// Feature is supported.
}
env.test(name)
Tests whether the browsers supports the given feature or not. Returns true
if the feature is supported, otherwise it returns false
.
if (joint.env.test('someFeature')) {
// Feature is supported.
}
JointJS ships with the following tests:
Highlighters can be used to provide visual emphasis to an element; during user interactions for example.
Highlighters inherit from dia.HighlighterView.
In the above demo, we listen to the paper for the element:pointerclick
event and highlight the entire element we clicked on.
paper.on('element:pointerclick', (elementView) => {
joint.highlighters.mask.add(elementView, { selector: 'root' }, 'my-element-highlight', {
deep: true,
attrs: {
'stroke': '#FF4365',
'stroke-width': 3
}
});
});
We also listen to the paper for the element:magnet:pointerclick
event and highlight the port we clicked on.
paper.on('element:magnet:pointerclick', (elementView, magnet, evt) => {
// Prevent highlighting the body of the element
evt.stopPropagation();
// Find the port ID of the selected magnet
const port = cellView.findAttribute('port', magnet);
joint.highlighters.mask.add(elementView, { port }, 'my-port-highlight', {
attrs: {
'stroke': '#FF4365',
'stroke-width': 3
}
});
});
Then we listen for the link:pointerclick
event and highlight the line node of the link we clicked on.
paper.on('link:pointerclick', (linkView) => {
joint.highlighters.mask.add(linkView, { selector: 'line' }, 'my-link-highlight', {
// Draw the highlighter under the LinkView
layer: 'back',
attrs: {
'stroke': '#FF4365',
'stroke-width': 3,
'stroke-linecap': 'square'
}
});
});
Next we set up a listener for a custom event link:label:pointerdown
and highlight the label we clicked on.
paper.on('link:label:pointerdown', (linkView, evt) => {
// Prevent highlighting the line of the link
evt.stopPropagation();
// Find the index of the selected label
const label = cellView.findAttribute('label-idx', evt.target);
joint.highlighters.mask.add(linkView, { label }, 'my-label-highlight', {
// Decrease the gap between the label and highlighter
padding: 1,
attrs: {
'stroke': '#FF4365',
'stroke-width': 3
}
});
});
Generally, it's possible to highlight a cell (Element or Link) or a part of the cell calling the add()
method of a highlighter.
// Add Mask Highlighter with ID `my-mask-highlighter` to the CellView.
// Note: `root` is a shortcut for `{ selector: 'root' }`
joint.highlighters.mask.add(cellView, 'root', 'my-mask-highlighter', {
deep: true
});
// Add class name `my-highlight` to the CellView's body node.
joint.highlighters.addClass.add(cellView, 'body', 'my-class-highlighter', {
className: 'my-highlight'
});
// Add Stroke Highlighter to a specific port node (using default options).
joint.highlighters.stroke.add(cellView, { port: 'port-id-1', selector: 'portBody' }, 'my-port-highlighter');
To unhighlight a cell, call the remove()
method.
// Remove all highlighters from the CellView
joint.dia.HighlighterView.remove(cellView);
// Remove the highlighter with ID `my-highlighter` from the CellView
joint.dia.HighlighterView.remove(cellView, 'my-highlighter');
// Remove all Mask highlighters from the cellView
joint.highlighters.mask.remove(cellView);
// Remove Stroke Highlighter with ID `my-highlighter` from the cellView.
joint.highlighters.stroke.remove(cellView, 'my-highlighter');
// If you have a reference to a highlighter, calling its prototype `remove()` method is also valid.
const highlighter = joint.dia.HighlighterView.get(cellView, 'my-highlighter');
highlighter.remove();
To see if a cell has a highlighter, call the get()
method.
// Get all the highlighters (an array) from the CellView
joint.dia.HighlighterView.get(cellView);
// Get the highlighter with ID `my-highlighter` from the CellView
joint.dia.HighlighterView.get(cellView, 'my-highlighter');
// Get all Mask highlighters from the cellView
joint.highlighters.mask.get(cellView);
// Get Stroke Highlighter with ID `my-highlighter` from the cellView.
// If there is no such highlighter (ID or Type does not match, `null` is returned).
joint.highlighters.stroke.get(cellView, 'my-highlighter');
// Prefer this:
joint.highlighters.mask.add(elementView, { port: 'port1' }, 'port-highlight');
// Over this:
joint.highlighters.mask.add(elementView, elementView.findPortNode('port1'), 'port-highlight');
If a node (determined by the node selector) stops to exist (e.g. a port is removed) the cell:highlight:invalid
event is triggered on the paper.
// If we don't want the highlighter wait for the port to reappear, we can remove it when the event occurs.
paper.on('cell:highlight:invalid', (cellView, id) => {
joint.dia.HighlighterView.remove(cellView, id);
})
Toggles a class name on an arbitrary cell view's SVG node.
Available options:
Example usage:
joint.highlighters.addClass.add(cellView, 'root', 'my-highlighter-id', {
className: 'some-custom-class'
});
New highlighter can be defined by extending the joint.dia.HighlighterView base class.
// A simple highlighter, which draws a rectangle over the entire CellView
const MyHighlighter = joint.dia.HighlighterView.extend({
tagName: 'rect',
attributes: {
'stroke': 'blue',
'fill': 'blue',
'fill-opacity': 0.1,
'pointer-events': 'none'
},
options: {
padding: 5
},
// Method called to highlight a CellView
highlight(cellView, _node) {
const { padding } = this.options;
const bbox = cellView.model.getBBox();
// Highlighter is always rendered relatively to the CellView origin
bbox.x = bbox.y = 0;
// Increase the size of the highlighter
bbox.inflate(padding);
this.vel.attr(bbox.toJSON());
},
// Method called to unhighlight a CellView
unhighlight(_cellView, _node) {
// Cleaning required when the highlighter adds
// attributes/nodes to the CellView or Paper.
// This highlighter only renders a rectangle.
}
});
MyHighlighter.add(el1.findView(paper), 'root', 'my-custom-highlighter', {
layer: 'front', // "layer" is an option inherited from the base class
padding: 10
});
Adds a list of arbitrary SVGElements to the cell view.
createListItem
) method.number
.number
or an object with width
and height
properties.row
(for growth on x
coordinate) or column
(for growth on y
coordinate).bottom-right
value positions the list by its bottom-right corner to the bottom-right corner of the element.number
or an object with left
, top
, right
, bottom
properties.protected createListItem(item, size, currentListItem)
It accepts the item
(a value at a position from the model attributes's array), the normalized size
object and currentListItem
SVGElement if such a node was created in a previous call. It returns an SVGElement.
It's an abstract method meant to be overridden.
The list optimizes the updates i.e. the method is not executed if the particular item does not change.
class StatusList extends highlighters.list {
// list of colored ellipses
createListItem(color, { width, height }) {
const { node } = V('ellipse', {
'rx': width / 2,
'ry': height / 2,
'cx': width / 2,
'cy': height / 2,
'fill': color,
'stroke': '#333',
'stroke-width': 2,
});
return node;
}
}
StatusList.add(element.findView(paper), 'root', 'status-list', {
attribute: 'status',
size: 10,
gap: 2
});
element.set('status', ['green', 'green', 'red'])
Adds a stroke around an arbitrary cell view's SVG node.
It's an alternative to the stroke highlighter.
Pros:Available options:
Example usage:
joint.highlighters.mask.add(cellView, 'body', 'my-highlighter-id', {
padding: 5,
attrs: {
'stroke-width': 3,
'stroke': '#FF0000',
// round the mask at the corners of paths
'stroke-linejoin': 'round',
// round the mask at the end of open subpaths
'stroke-linecap': 'round'
// to change the opacity use `rgba` color format
// 'stroke': 'rgba(255, 0, 0, 0.5)',
// this would affect the opacity of the stroke and the padding
// 'stroke-opacity': 0.5
}
});
Changes the opacity of an arbitrary cell view's SVG node.
Available options:
0.3
.Example usage:
joint.highlighters.opacity.add(cellView, 'body', 'my-highlighter-id', {
alphaValue: 0.4
});
Adds a stroke around an arbitrary cell view's SVG node.
Available options:
el
compound path.false
.Example usage:
joint.highlighters.stroke.add(cellView, 'body', 'my-highlighter-id', {
padding: 10,
rx: 5,
ry: 5,
useFirstSubpath: true,
attrs: {
'stroke-width': 3,
'stroke': '#FF0000'
}
});
Automatic layout of directed graphs. This plugin uses the open-source (MIT license) Dagre library internally.
Add the @joint/layout-directed-graph
package as a dependency of your project (for example via yarn
):
yarn add @joint/layout-directed-graph
yarn install
You can then import the package into your code:
import { DirectedGraph } from '@joint/layout-directed-graph';
The DirectedGraph
package exposes the layout(graphOrCells, opt)
function. The first parameter graphOrCells
is a joint.dia.Graph
or an array of joint.dia.Cells
that we want to lay out. The second parameter opt
is an object that contains various options for configuring the layout.
import { DirectedGraph } from '@joint/layout-directed-graph';
var graphBBox = DirectedGraph.layout(graph, {
nodeSep: 50,
edgeSep: 80,
rankDir: "TB"
});
console.log('x:', graphBBox.x, 'y:', graphBBox.y)
console.log('width:', graphBBox.width, 'height:', graphBBox.height);
A blog post explaining the usage of the layout(graphOrCells, opt)
function in more detail can be found here.
The following table lists options that you can pass to the layout(graphOrCells, opt)
function:
nodeSep | a number of pixels representing the separation between adjacent nodes in the same rank |
---|---|
edgeSep | a number of pixels representing the separation between adjacent edges in the same rank |
rankSep | a number of pixels representing the separation between ranks |
rankDir | direction of the layout (one of "TB" (top-to-bottom) / "BT" (bottom-to-top) / "LR" (left-to-right) / "RL" (right-to-left)) |
marginX | number of pixels to use as a margin around the left and right of the graph. |
marginY | number of pixels to use as a margin around the top and bottom of the graph. |
ranker | Type of algorithm to assign a rank to each node in the input graph. Possible values: 'network-simplex' (default), 'tight-tree' or 'longest-path' . |
resizeClusters | set to false if you don't want parent elements to stretch in order to fit all their embedded children. Default is true . |
clusterPadding | A gap between the parent element and the boundary of its embedded children. It could be a number or an object e.g. { left: 10, right: 10, top: 30, bottom: 10 } . It defaults to 10 . |
setPosition(element, position) |
a function that will be used to set the position of elements at the end of the layout. This is useful
if you don't want to use the default element.set('position', position) but want to set the position in an animated fashion via transitions.
|
setVertices(link, vertices) |
If set to true the layout will adjust the links by setting their vertices. It defaults to false . If the option is defined as a function it will be used to set the vertices of links at the end of the layout. This is useful
if you don't want to use the default link.set('vertices', vertices) but want to set the vertices in an animated fashion via transitions.
|
setLabels(link, labelPosition, points) |
If set to true the layout will adjust the labels by setting their position. It defaults to false . If the option is defined as a function it will be used to set the labels of links at the end of the layout.
Note: Only the first label ( link.label(0); ) is positioned by the layout.
|
exportElement(element) | Convert element attributes into dagre node attributes. By default, it returns the element attributes below. |
exportLink(link) | Convert link attributes into dagre edge attributes. By default, it returns the link attributes below. |
Additionally, the layout engine takes into account some properties on elements/links to fine tune the layout further. These are:
size | element | An object with `width` and `height` properties representing the size of the element. |
---|---|---|
minLen | link | The number of ranks to keep between the source and target of the link. |
weight | link | The weight to assign edges. Higher weight edges are generally made shorter and straighter than lower weight edges. |
labelPosition | link | Where to place the label relative to the edge. 'l' = left, 'c' = center (default), 'r' = right.
|
labelOffset | link | How many pixels to move the label away from the edge. Applies only when labelPosition is left or right. |
labelSize | link | The width and height of the edge label in pixels. e.g. { width: 100, height: 50 } |
The layout(graphOrCells, opt)
function returns a bounding box (g.Rect
) of the resulting graph.
The DirectedGraph
package also provides two functions that make it easy to convert graphs to and from the Graphlib graph format. This allows you to use the wealth of graph algorithms provided by the Graphlib library.
In order to use Graphlib, you need to add the package as an additional dependency of your project (for example via yarn
):
yarn add @dagrejs/graphlib
yarn install
You can then import the package into your code:
import * as graphlib from '@dagrejs/graphlib';
toGraphLib(graph, opt) | Convert the provided JointJS joint.dia.Graph object to a Graphlib graph object.
|
---|---|
fromGraphLib(glGraph, opt) | Convert the provided Graphlib graph object to a JointJS joint.dia.Graph object.
The opt.importNode and opt.importEdge callbacks are provided with a Graphlib node / edge object, and are expected to return a corresponding JointJS element / link object.
|
Port layouts are functions that accept an array of port's args
and return an array of port positions. Positions are relative to the element model bounding box. For example if we have an element at position { x:10, y:20 }
with a relative port position { x:1, y:2 }
, the absolute port position will be { x:11, y:22 }
.
Port layout can be defined only at the group
level. Optionally you can pass some additional arguments into the layout function via args
. The args
is the only way to adjust port layout from the port definition perspective.
const rect = new joint.shapes.standard.Rectangle({
// ...
ports: {
groups: {
'a': {
position: {
name: 'layoutType',
args: {},
}
}
},
items: [
// initialize 'rect' with port in group 'a'
{
group: 'a',
args: {} // overrides `args` from the group level definition.
},
// ... other ports
]
}
});
// ....
// add another port to group 'a'.
rect.addPort({ group:'a' });
A simple layout suitable for rectangular shapes. It evenly spreads ports along a single side.
name | string |
Can be either 'left' , 'right' , 'top' , 'bottom' .
|
||||||||||||||
args | object |
|
{
name: 'left',
args: {
x: 10,
y: 10,
angle: 30,
dx: 1,
dy: 1
}
}
A layout which evenly spreads ports along a line defined by a start
and end
point.
name | string |
'line'
|
||||||
args | object |
|
{
name: 'line',
args: {
start: { x: 10, y: 10 },
end: { x: 'calc(w)', y: '50%' }
}
}
It lays a port out at the given position (defined as x
, y
coordinates or percentage of the element dimensions).
name | string |
'absolute'
|
|||||||
args | object |
|
{
name: 'absolute',
args: {
x: '10%',
y: 10,
angle: 45
}
}
Suitable for circular shapes. The ellipseSpreads
evenly spreads ports along an ellipse. The ellipse
spreads ports from the point at startAngle
leaving gaps between ports equal to step
.
name | string |
Can be either 'ellipse' or 'ellipseSpread' .
|
|||||||||||||||||||||||
args | object |
|
{
name: 'ellipseSpread',
args: {
dx: 1,
dy: 1,
dr: 1,
startAngle: 10,
step: 10,
compensateRotation: false
}
}
An alternative for built-in layouts is providing a function directly, where the function returns an array of port transformations (position and angle).
/**
* @param {Array<object>} portsArgs
* @param {g.Rect} elBBox shape's bounding box
* @param {object} opt Group options
* @returns {Array<joint.layout.Port.Transformation>}
*/
function(portsArgs, elBBox, opt) {
// Distribute ports along the sinusoid
return portsArgs.map((portArgs, index) => {
const step = -Math.PI / 8;
const y = Math.sin(index * step) * 50;
return {
x: index * 12,
y: y + elBBox.height,
angle: 0
};
});
}
Port label layout functions calculate port label positions relatively to port positions.
Simple label layout suitable for rectangular shapes. It places the label on arbitrary side of a port. The args
object is optional.
name | string |
Can be either 'left' , 'right' , 'top' , or 'bottom' .
|
||||||||||||
args | object |
|
label: {
position: {
name : 'right',
args: {
x: 0,
y: 0,
angle: 0,
attrs: {}
}
}
}
Places the label inside or outside of a rectangular shape. Where 'oriented' versions rotate the text towards the element center. The args
object is optional.
name | string |
Can be either 'inside' , 'outside' , 'insideOriented' , or 'outsideOriented' .
|
|||||||||||||||
args | object |
|
label: {
position: {
name :'outsideOriented',
args: {
offset: 10,
attrs: {}
}
}
Places the label outside of a circular shape. Where the 'oriented' version rotates the text towards the element center. The args
object is optional.
name | string |
Can be either 'radial' , or 'radialOriented' .
|
|||||||||||||||
args | object |
|
label: {
position: {
name :'radialOriented',
args: {
offset: 0,
attrs: {}
}
}
}
It allows setting label position directly.
name | string |
'manual'
|
||||||||||||
args | object |
|
label: {
position: {
name: 'manual',
args: {
x: 10,
y: 20,
angle: 45,
attrs: {}
}
}
}
A link anchor of a link is a point on the reference link that this link wants to reach as its endpoint. Link anchors are set via a linkAnchor
property provided within link end definitions (i.e. the objects provided to link.source()
and link.target()
functions). (If the reference object is an Element, JointJS looks at anchor
property instead.)
There are several built-in linkAnchor functions in JointJS:
'connectionRatio'
- default link anchor at specified ratio at reference link'connectionLength'
- link anchor at specified length along reference link'connectionPerpendicular'
- link anchor that ensures an orthogonal route to the reference link'connectionClosest'
- link anchor at the point on reference link that is closest to the last vertex on link pathExample:
link.source(link2, {
linkAnchor: {
name: 'connectionRatio',
args: {
ratio: 0.25
}
}
});
The default link anchor function is 'connectionRatio'
; this can be changed with the defaultLinkAnchor
paper option. Example:
paper.options.defaultLinkAnchor = {
name: 'connectionLength',
args: {
length: 20
}
};
JointJS also contains mechanisms to define one's own custom link anchor functions.
The 'connectionClosest'
link anchor function places the anchor of the link at the point along the reference link that is the closest to the appropriate reference point of this link. (If we are calling this method for a source anchor, the reference point is the first vertex; if there are no vertices, it is the target anchor. If we are calling this method for a target anchor, the reference point is the last vertex; if there are no vertices, it is the source anchor.)
Example:
link.source(link2, {
linkAnchor: {
name: 'connectionClosest'
}
});
The 'connectionLength'
link anchor function places the anchor of the link at a point a specified length along reference link (counting from reference link's source). It accepts one argument, which can be passed within the linkAnchor.args
property:
length | number | The length at which to place the target anchor. Default is 20 , meaning a point 20 pixels from the source of the reference link. |
---|
Example:
link.source(link2, {
linkAnchor: {
name: 'connectionLength',
args: {
ratio: 50
}
}
});
The 'connectionPerpendicular'
link anchor function tries to place the anchor of the link inside the view bbox so that the link is made orthogonal. The anchor is placed along two line segments inside the view bbox (between the centers of the top and bottom side and between the centers of the left and right sides). If it is not possible to place the link anchor so that the resulting link would be orthogonal, the anchor is placed at the point on the reference link that is the closest to the appropriate reference point on this link. (If we are calling this method for a source anchor, the reference point is the first vertex; if there are no vertices, it is the target anchor. If we are calling this method for a target anchor, the reference point is the last vertex; if there are no vertices, it is the source anchor.)
Example:
link.source(link2, {
anchor: {
name: 'connectionPerpendicular'
}
});
When the link has no vertices, the other end cell's center is used as a reference point. By default, this means that a link using the 'connectionPerpendicular'
anchor slides
alongside the source reference link while pointing to target's center. To invert this behavior, and have the link slide
alongside the target reference link while pointing to source's center, pass a priority
option to the target function:
link.target(link2, {
priority: true,
anchor: {
name: 'connectionPerpendicular',
}
});
The 'connectionRatio'
link anchor function places the anchor of the link at a point at a specified ratio of the reference link's length (from reference link's source). It accepts one argument, which can be passed within the linkAnchor.args
property:
ratio | number | The length ratio of the target anchor. Default is 0.5 , meaning the midpoint of the reference link. |
---|
Example:
link.source(link2, {
linkAnchor: {
name: 'connectionRatio',
args: {
ratio: 0.25
}
}
});
New link anchor functions can be defined in the joint.linkAnchors
namespace (e.g. joint.linkAnchors.myLinkAnchor
) or passed directly as a function to the linkAnchor
property of link source/target (or to the defaultLinkAnchor
option of a paper).
In either case, the link anchor function must return the link anchor as a Point. The function is expected to have the form function(endView, endMagnet, anchorReference, args)
:
endView | dia.LinkView | The LinkView to which we are connecting. The Link model can be accessed as endView.model ; this may be useful for writing conditional logic based on link attributes. |
---|---|---|
endMagnet | null | (Not used) This argument is only present to ensure that the signature of custom linkAnchor methods is the same as the signature of custom anchor methods. |
anchorReference | g.Point | A reference to another component of the link path that may be necessary to find this anchor point. If we are calling this method for a source anchor, it is the first vertex; if there are no vertices, it is the target anchor. If we are calling this method for a target anchor, it is the last vertex; if there are no vertices, it is the source anchor... |
SVGElement | ...if the anchor in question does not exist (yet), it is that link end's magnet. (The built-in methods usually use this element's center point as reference.) | |
args | object | An object with additional optional arguments passed to the link anchor method by the user when it was called (the args property). |
A link tool is a view that renders a certain type of control elements on top of the LinkView it is attached to; for example the Vertices tool creates an interactive handle above every vertex (these handles then allow the user to move and/or delete each vertex). Link tools all inherit from the joint.dia.ToolView
class. A collection of tools is added to a ToolsView; a tools view is then added to the linkView with the linkView.addTools()
function.
The JointJS library comes with a collection of pre-made link tool definitions in the joint.linkTools
namespace:
Vertices
- adds handles above link verticesSegments
- adds handles above link segmentsSourceArrowhead
- adds a handle above link sourceTargetArrowhead
- adds a handle above link targetSourceAnchor
- adds a handle above link source anchorTargetAnchor
- adds a handle above link target anchorBoundary
- shows link bboxRemove
- adds an interactive remove buttonTo create a new link tool, we call its constructor. Example:
var verticesTool = new joint.linkTools.Vertices({
snapRadius: 10
});
In addition, the joint.linkTools
namespace contains a customizable button class:
Button
- adds a customizable buttonExample:
var infoTool = new joint.linkTools.Button({
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}],
distance: 60,
offset: 0,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
}
});
All of the built-in link tools accept the following optional argument, in addition to their own arguments:
focusOpacity | number | What should be the opacity of the tool when it is focused (e.g. with the toolView.focus function)? Default is undefined , meaning that the tool's opacity is kept unchanged. |
---|
Example:
var verticesTool = new joint.linkTools.Vertices({
focusOpacity: 0.5
});
The Boundary
link tool renders a rectangular border to show the bounding box of the link. It accepts a few additional arguments, which can be passed as an object to the link tool constructor:
padding | number|object | This option determines whether the boundary area should be visually inflated and if so, by how much. Default is 10 ({ left: 10, top: 10, right: 10, bottom: 10 } ). |
---|---|---|
useModelGeometry | boolean | If this option is set to true , the position of the boundary is calculated based on the dimensions of the link model. |
Example:
var boundaryTool = new joint.linkTools.Boundary({
focusOpacity: 0.5,
padding: 20,
useModelGeometry: true
});
The Button
link tool allows you to have a custom button rendered at a given position along the link. It accepts five additional arguments, which can be passed as an object to the link tool constructor:
distance | number | Distance at which the button should be placed. Negative numbers are accepted; then the distance is counted from the end of the link. Default is 0 . |
---|---|---|
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
rotate | boolean | Should the button rotate according to the slope of the link at the position specified by distance ? Default is false . |
offset | number | Relative offset of the button from the link. Positive numbers mean that the button should be offset to the right of the link (relative to the direction from source to target); negative numbers mean that the button should be offset to the left of the link (relative to the direction from source to target). Default is 0 . |
action | function | What should happen when the user clicks the button? Default is undefined (no interaction).The callback function is expected to have the signature function(evt, linkView, buttonView) where evt is a DOM event. The related link view is available inside the function as this . The link model is available as this.model . |
markup | JSONMarkup | The markup of the button, provided in the JointJS JSON format. Default is undefined (no content). |
scale | number | Scale the button up or down on a 2D plane. The default is 1 . |
Example of a useful custom info button:
var infoButton = new joint.linkTools.Button({
focusOpacity: 0.5,
distance: 60,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
},
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}]
});
The linkTools.Button
class can also be extended, to create a reusable custom button type. Then, a new instance of the custom button type can be obtained by calling its constructor:
var InfoButton = joint.linkTools.Button.extend({
name: 'info-button',
options: {
focusOpacity: 0.5,
distance: 60,
action: function(evt) {
alert('View id: ' + this.id + '\n' + 'Model id: ' + this.model.id);
},
markup: [{
tagName: 'circle',
selector: 'button',
attributes: {
'r': 7,
'fill': '#001DFF',
'cursor': 'pointer'
}
}, {
tagName: 'path',
selector: 'icon',
attributes: {
'd': 'M -2 4 2 4 M 0 3 0 0 M -2 -1 1 -1 M -1 -4 1 -4',
'fill': 'none',
'stroke': '#FFFFFF',
'stroke-width': 2,
'pointer-events': 'none'
}
}]
}
});
var infoButton = new InfoButton();
The Connect
tool allows the user to create links in a drag & drop fashion. The tool extends the Button tool and accepts additional arguments, which can be passed as an object to the connect tool constructor:
magnet | string SVGElement (view: dia.ElementView) => SVGElement |
Choose the source magnet of the element view which the new link should be connected to.
The callback function is expected to have the signature |
---|
Example:
const connectButton = new joint.linkTools.Connect({
rotate: true,
distance: -20,
offset: 20,
magnet: 'body'
});
The HoverConnect
tool allows the user to create links from other links in a drag & drop fashion. The tool extends the Connect tool. The difference is that the button appears along the invisible track path (in the shape of a linkView) at the point where the user moves the mouse over the track. It accepts additional arguments, which can be passed as an object to the hover connect tool constructor:
trackWidth | number | The thickness of the track path. The default is 15 . |
---|
Example:
const hoverButton = new joint.linkTools.HoverConnect({
magnet: 'body',
trackWidth: 10
});
The Remove
link tool renders a remove button at a given position along the link. It accepts five additional arguments, which can be passed as an object to the link tool constructor:
distance | number | Distance at which the button should be placed. Negative numbers are accepted; then the distance is counted from the end of the link. Default is 60 . |
---|---|---|
string | Percentage strings (e.g. '40%' ) are also accepted. |
|
rotate | boolean | Should the button rotate according to the slope of the link at the position specified by distance ? Default is false . |
offset | number | Relative offset of the button from the link. Positive numbers mean that the button should be offset to the right of the link (relative to the direction from source to target); negative numbers mean that the button should be offset to the left of the link (relative to the direction from source to target). Default is 0 . |
action | function | What should happen when the user clicks the remove button? Default:
The callback function is expected to have the signature function(evt) where evt is a DOM event. The button view is available inside the function as this ; the button model is available as this.model .
|
markup | JSONMarkup | The markup of the button, provided in the JointJS JSON format. Default:
|
scale | number | Scale the button up or down on a 2D plane. The default is 1 . |
Example:
var removeButton = new joint.linkTools.Remove({
focusOpacity: 0.5,
rotate: true,
distance: -20,
offset: 20
});
The Segments
link tool renders handles above all segments of the link (as determined by the link connector). It accepts four additional arguments, which can be passed as an object to the link tool constructor:
redundancyRemoval | boolean | If the user arranges two (or more) segments so that they lie on a single line, should the middle one(s) be considered redundant and removed? Default is true . Note that this setting is not applied until the user actually moves one of the segments in question; this means that segments can still be arranged in this redundantfashion, using the link.vertices function, for example. |
---|---|---|
segmentLengthThreshold | number | The minimum segment length for which to display a segment handle (to prevent the handle from overflowing its segment). Default is 40 . |
snapRadius | number | While the user is moving the segment, from how far away should the segment snap in order to arrange itself in line with another segment? Default is 10 . |
snapHandle | number | If the snapRadius option is set to true and the segment is snapped in place while the user moves the segment handle, should the handle follow the user pointer or should the handle stay snapped with the segment until it un-snaps? Default is true , meaning that the handle snaps with the segment. |
stopPropagation | boolean | Should be events stopped from propagating to the paper? Default is true . |
handleClass | mvc.View | The view for the segment handle. By default it uses linkTools.Segments.SegmentHandle class. |
scale | number | Scale the segment handles up or down on a 2D plane. The default is 1 . |
The tool is meant to be used with normal router only. It does not work with e.g. orthogonal router. It throws the "Segments: incompatible router in use" error if used with any other router.
Example:
var segmentsTool = new joint.linkTools.Segments({
focusOpacity: 0.5,
redundancyRemoval: false,
segmentLengthThreshold: 50,
snapHandle: false,
snapRadius: 10
});
The SourceAnchor
link tool renders a handle above the source anchor of the link (as determined by the anchor function applied on link source). It accepts several additional arguments, which can be passed as an object to the link tool constructor:
redundancyRemoval | boolean | If the user moves the anchor so that it lies on a single line with two (or more) vertices, should the middle one(s) be considered redundant and removed? Default is true . Note that this setting is not applied until the user actually moves the anchor; this means that the anchor and vertices can still be arranged in this redundantfashion, using the link.vertices function, for example. |
||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
restrictArea | boolean | Should the user only be allowed to move the anchor in a restricted area (the area of the bounding box of the source magnet)? Default is true . |
||||||||||||||||||
areaPadding | number | If the restrictArea option is set to true , the user can only move the anchor in a restricted area (the area of the bounding box of the source magnet). JointJS shows this restriction by drawing a boundary around that area. This option determines whether this boundary area should be visually inflated and if so, by how much. Default is 10 . Note that this is a purely cosmetic setting; regardless of the provided value, the movement stays restricted to the original uninflated bounding box. |
||||||||||||||||||
snapRadius | number | While the user is moving the anchor, from how far away should the segment snap in order to arrange itself in line with the anchor reference? Default is 10 . (For link source, the anchor reference is the first vertex. If there are no vertices, it is the target anchor.) |
||||||||||||||||||
resetAnchor | boolean | object | When the user double clicks the anchor tool, the following action should be performed:
|
||||||||||||||||||
snap | function | What snap function should be applied when the user moves the anchor? Default is a simple function that emulates the snapping behavior of Vertices and Segments link tools: If the value of one of the user pointer coordinates is within snapRadius of the value of a coordinate of the anchor reference, snap to the reference value. (For link source, the anchor reference is the first vertex. If there are no vertices, it is the target anchor.)The callback function must return the anchor as a g.Point and have the signature function(coords, endView, endMagnet, endType, linkView, toolView) :
|
||||||||||||||||||
scale | number | Scale the anchor element up or down on a 2D plane. The default is 1 . |
Example:
var sourceAnchorTool = new joint.linkTools.SourceAnchor({
focusOpacity: 0.5,
redundancyRemoval: false,
restrictArea: false,
snapRadius: 20
});
An example of a useful custom snap
function is provided below. It snaps the anchor to the center of the closest side of the restricted area.
var snapAnchor = function(coords, endView, endMagnet) {
// remove rotation of the restricted area
var bbox = endView.getNodeUnrotatedBBox(endMagnet);
var angle = endView.model.angle();
var origin = endView.model.getBBox().center();
coords.rotate(origin, angle);
// identify the side nearest to pointer coords
var anchor;
var side = bbox.sideNearestToPoint(coords);
switch (side) {
case 'left': anchor = bbox.leftMiddle(); break;
case 'right': anchor = bbox.rightMiddle(); break;
case 'top': anchor = bbox.topMiddle(); break;
case 'bottom': anchor = bbox.bottomMiddle(); break;
}
// rotate the anchor according to original rotation of restricted area
return anchor.rotate(origin, -angle);
};
var sourceAnchorTool = new joint.linkTools.SourceAnchor({
snap: snapAnchor;
});
If the user moves the anchor away from its original position, the anchor position may be reset by double-clicking the anchor handle.
The SourceArrowhead
link tool renders an arrow-like handle above the source connection point of the link (as determined by the connectionPoint function applied on link source). It accepts a few additional arguments, which can be passed as an object to the link tool constructor:
scale | number | Scale the arrowhead element up or down on a 2D plane. The default is 1 . |
---|
Example:
var sourceArrowheadTool = new joint.linkTools.SourceArrowhead({
focusOpacity: 0.5
});
The TargetAnchor
link tool renders a handle above the target anchor of the link (as determined by the anchor function applied on link target). It accepts several additional arguments, which can be passed as an object to the link tool constructor:
redundancyRemoval | boolean | If the user moves the anchor so that it lies on a single line with two (or more) vertices, should the middle one(s) be considered redundant and removed? Default is true . Note that this setting is not applied until the user actually moves the anchor; this means that the anchor and vertices can still be arranged in this redundantfashion, using the link.vertices function, for example. |
||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
restrictArea | boolean | Should the user only be allowed to move the anchor in a restricted area (the area of the bounding box of the target magnet)? Default is true . |
||||||||||||||||||
areaPadding | number | If the restrictArea option is set to true , the user can only move the anchor in a restricted area (the area of the bounding box of the target magnet). JointJS shows this restriction by drawing a boundary around that area. This option determines whether this boundary area should be visually inflated and if so, by how much. Default is 10 . Note that this is a purely cosmetic setting; regardless of the provided value, the movement stays restricted to the original uninflated bounding box. |
||||||||||||||||||
snapRadius | number | While the user is moving the anchor, from how far away should the segment snap in order to arrange itself in line with the anchor reference? Default is 10 . (For link target, the anchor reference is the last vertex. If there are no vertices, it is the source anchor.) |
||||||||||||||||||
resetAnchor | boolean | object | When the user double clicks the anchor tool, the following action should be performed:
|
||||||||||||||||||
snap | function | What snap function should be applied when the user moves the anchor? Default is a simple function that emulates the snapping behavior of Vertices and Segments link tools: If the value of one of the user pointer coordinates is within snapRadius of the value of a coordinate of the anchor reference, snap to the reference value. (For link target, the anchor reference is the last vertex. If there are no vertices, it is the source anchor.)The callback function must return the anchor as a g.Point and have the signature function(coords, endView, endMagnet) :
|
||||||||||||||||||
scale | number | Scale the anchor element up or down on a 2D plane. The default is 1 . |
Example:
var targetAnchorTool = new joint.linkTools.TargetAnchor({
focusOpacity: 0.5,
redundancyRemoval: false,
restrictArea: false,
snapRadius: 20
});
An example of a useful custom snap
function is provided below. It snaps the anchor to the center of the closest side of the restricted area.
var snapAnchor = function(coords, endView, endMagnet) {
// remove rotation of the restricted area
var bbox = endView.getNodeUnrotatedBBox(endMagnet);
var angle = endView.model.angle();
var origin = endView.model.getBBox().center();
coords.rotate(origin, angle);
// identify the side nearest to pointer coords
var anchor;
var side = bbox.sideNearestToPoint(coords);
switch (side) {
case 'left': anchor = bbox.leftMiddle(); break;
case 'right': anchor = bbox.rightMiddle(); break;
case 'top': anchor = bbox.topMiddle(); break;
case 'bottom': anchor = bbox.bottomMiddle(); break;
}
// rotate the anchor according to original rotation of restricted area
return anchor.rotate(origin, -angle);
};
var targetAnchorTool = new joint.linkTools.TargetAnchor({
snap: snapAnchor;
});
If the user moves the anchor away from its original position, the anchor position may be reset by double-clicking the anchor handle.
TargetArrowhead
link tool renders an arrow-like handle above the target connection point of the link (as determined by the connectionPoint function applied on link target). It accepts a few additional arguments, which can be passed as an object to the link tool constructor:
scale | number | Scale the arrowhead element up or down on a 2D plane. The default is 1 . |
---|
Example:
var targetArrowheadTool = new joint.linkTools.TargetArrowhead({
focusOpacity: 0.5
});
The Vertices
link tool renders handles above all vertices of the link. It accepts three additional arguments, which can be passed as an object to the link tool constructor:
redundancyRemoval | boolean | If the user arranges three (or more) vertices so that they lie on a single line, should the middle one(s) be considered redundant and removed? Default is true . Note that this setting is not applied until the user actually moves one of the vertices in question; this means that vertices can still be arranged in this redundantfashion, using of the link.vertices function, for example. |
---|---|---|
snapRadius | number | While the user is moving the vertex, from how far away should the vertex snap in order to arrange itself perpendicularly to another vertex? Default is 20 . |
vertexAdding | boolean | Can the user add new vertices (by clicking a segment of the link)? Default is true . |
vertexMoving | boolean | Can the user move vertices (by dragging them)? Default is true . |
vertexRemoving | boolean | Can the user remove vertices (by double clicking them)? Default is true . |
stopPropagation | boolean | Should be events stopped from propagating to the paper? Default is true . |
handleClass | mvc.View | The view for the vertex handle. By default it uses linkTools.Vertices.VertexHandle class. |
scale | number | Scale the vertices handles up or down on a 2D plane. The default is 1 . |
Example:
var verticesTool = new joint.linkTools.Vertices({
focusOpacity: 0.5,
redundancyRemoval: false,
snapRadius: 10,
vertexAdding: false,
});
Collections are ordered sets of models. You can bind "change"
events to be notified when any model in the collection has been
modified, and listen for "add"
and "remove"
events.
Any event that is triggered on a model in a collection will also be triggered on the collection directly, for convenience. This allows you to listen for changes to specific attributes in any model in a collection.
collection.add(models, [options])
Add a model (or an array of models) to the collection, firing an "add"
event for each model, and an "update"
event
afterwards. This is a variant of set()
with the same options and return value, but it
always adds and never removes. If you're adding models to the collection that are already in the collection, they'll be ignored, unless you
pass { merge: true }
, in which case their attributes will be merged into the corresponding models, firing any appropriate
"change"
events.
const shapes = new mvc.Collection;
shapes.on('add', function(shape) {
console.log(shape.get('name'));
// A
// B
});
shapes.add([
{ name: 'A' },
{ name: 'B' }
]);
Note that adding the same model (a model with the same id
) to a collection more than once is a no-op.
collection.at(index)
Get a model from a collection, specified by index. Useful if your collection is sorted, and if your collection isn't sorted, at
will still retrieve models in insertion order. When passed a negative index, it will retrieve the model from the back of the collection.
collection.clone()
Returns a new instance of the collection with an identical list of models.
collection.comparator
By default there is no comparator for a collection. If you define a comparator, it will be used to sort the collection any time a model is
added. A comparator can be defined as a sortBy
(pass a function
that takes a single argument), as a
sort
(pass a comparator function that expects two arguments), or as a string indicating the attribute to sort by.
"sortBy" comparator functions take a model and return a numeric or string value by which the model should be ordered relative to others.
"sort" comparator functions take two models, and return -1
if the first model should come before the second, 0
if
they are of the same rank and 1
if the first model should come after. Note that JointJS depends on the arity of your comparator
function to determine between the two styles, so be careful if your comparator function is bound.
const Shape = new mvc.Model;
const shapes = new mvc.Collection;
shapes.comparator = 'order';
shapes.add(new Shape({ order: 3, letter: "C" }));
shapes.add(new Shape({ order: 2, letter: "B" }));
shapes.add(new Shape({ order: 1, letter: "A" }));
console.log(shapes.pluck('letter')); // A, B, C
Note: Collections with a comparator will not automatically re-sort if you later change model attributes, so you may wish to call
sort
after changing model attributes that would affect the order.
collection.each(fn, [context])
Iterate over models of a collection, and invoke fn
for each model. fn
is invoked with 3 arguments:
(model, index, array).
const a = new mvc.Model({ id: 1, label: 'a' });
const b = new mvc.Model({ id: 2, label: 'b' });
const c = new mvc.Model({ id: 3, label: 'c' });
const col = new mvc.Collection([a, b, c]);
col.each((model, i) => model.set({ customData: i }));
col.each((model) => console.log(model.get('customData')));
// 0
// 1
// 2
mvc.Collection.extend(properties, [classProperties])
To create a Collection class of your own, extend mvc.Collection
. Provide instance properties, and optional classProperties
to be attached directly to the constructor function.
collection.filter(fn, [context])
Iterate over models of a collection, returning an array of all models fn
returns truthy for. fn
is invoked with three
arguments: (model, index, array).
const a = new mvc.Model({ id: 3, label: 'a' });
const b = new mvc.Model({ id: 2, label: 'b' });
const c = new mvc.Model({ id: 1, label: 'c' });
const d = new mvc.Model({ id: 0, label: 'd' });
const col = new mvc.Collection([a, b, c, d]);
console.log(col.filter((model) => model.get('id') === 0).length); // 1
collection.find(fn, [context])
Return the first element in the collection that satisfies the provided testing function.
If no values satisfy the testing function, undefined
is returned.
const a = new mvc.Model({ id: 3, label: 'a' });
const b = new mvc.Model({ id: 2, label: 'b' });
const c = new mvc.Model({ id: 1, label: 'c' });
const d = new mvc.Model({ id: 0, label: 'd' });
const col = new mvc.Collection([a, b, c, d]);
console.log(col.find((model) => model.get('label') === 'c').id); // 1
collection.findIndex(fn, [context])
Return the index of the first element in the collection that satisfies the provided testing function.
If no elements satisfy the testing function, -1
is returned.
const a = new mvc.Model({ id: 3, label: 'a' });
const b = new mvc.Model({ id: 2, label: 'b' });
const c = new mvc.Model({ id: 1, label: 'c' });
const d = new mvc.Model({ id: 0, label: 'd' });
const col = new mvc.Collection([a, b, c, d]);
console.log(col.findIndex((model) => model.get('label') === 'c')); // 2
collection.first()
Return the first model of a collection.
collection.includes(value)
Return true
if model is found in a collection.
const a = new mvc.Model({ id: 3, label: 'a' });
const b = new mvc.Model({ id: 2, label: 'b' });
const c = new mvc.Model({ id: 1, label: 'c' });
const d = new mvc.Model({ id: 0, label: 'd' });
const col = new mvc.Collection([a, b, c]);
console.log(col.includes(a)); // true
console.log(col.includes(d)); // false
When creating a Collection, you may choose to pass in the initial array of models. The collection's
comparator
may be included as an option. Passing false
as the
comparator
option will prevent sorting. If you define an initialize function, it will be invoked when the collection is created.
Initialize is an empty function by default. Override it with your own initialization logic.
There are a couple of options that, if provided, are attached to the collection directly: model
and comparator
.
Pass null
for models to create an empty Collection with options
.
const shapes = new mvc.Collection(null, {
model: Shape
});
collection.isEmpty()
Return true
if a collection is empty.
const col = new mvc.Collection([]);
console.log(col.isEmpty()); // true
col.set([new mvc.Model({ id: 1, label: 'a' })]);
console.log(col.isEmpty()); // false
collection.last()
Return the last model of a collection.
collection.length
Like an array, a Collection maintains a length
property, counting the number of models it contains.
collection.map(fn, [context])
Create an array of values by running each model in the collection through fn
. fn
is invoked with three arguments:
(model, index, array).
const a = new mvc.Model({ id: 3, label: 'a' });
const b = new mvc.Model({ id: 2, label: 'b' });
const c = new mvc.Model({ id: 1, label: 'c' });
const d = new mvc.Model({ id: 0, label: 'd' });
const col = new mvc.Collection([a, b, c, d]);
console.log(col.map((model) => model.get('label')).join(' ')); // 'a b c d'
collection.model([attrs], [options])
Override this property to specify the model class that the collection contains. If defined, you can pass raw attributes objects (and arrays)
and options to add()
, and
reset()
, and the attributes will be converted into a model of the proper type using
the provided options, if any.
const Shapes = mvc.Collection.extend({
model: Shape
});
A collection can also contain polymorphic models by overriding this property with a constructor that returns a model.
const Shapes = mvc.Collection.extend({
model: function(attrs, options) {
if (condition) {
return new ShapeA(attrs, options);
} else {
return new ShapeB(attrs, options);
}
}
});
collection.modelId(attrs, idAttribute)
Override this method to return the value the collection will use to identify a model given its attributes. Useful for combining models from
multiple tables with different idAttribute
values into a single collection.
By default returns the value of the given idAttribute
within the attrs
, or failing that, id
. If your
collection uses a model factory and the id ranges of those models might collide, you must override
this method.
const Shapes = mvc.Collection.extend({
modelId: function(attrs) {
return attrs.type + attrs.id;
}
});
const shapes = new Shapes([
{ type: 'a', id: 1 },
{ type: 'b', id: 1 }
]);
console.log(shapes.get('a1').id); // 1
collection.models
Raw access to the JavaScript array of models inside of the collection. Usually you'll want to use get()
or at()
to
access model objects, but occasionally a direct reference to the array is desired.
collection.pop([options])
Remove and return the last model from a collection. Takes the same options as
remove()
.
For use with collections as ES classes. If you define a preinitialize
method, it will be invoked when the Collection is first
created and before any instantiation logic is run for the Collection.
class Shapes extends mvc.Collection {
preinitialize() {
this.on('add', function() {
console.log('Add model event got fired!');
});
}
}
collection.push(model, [options])
Like add()
, but always adds a model at the end of the collection and never sorts.
collection.reduce(fn, [initialValue])
Reduce a collection to a value which is the accumulated result of running each model in the collection through fn
, where each
successive invocation is supplied the return value of the previous. If initialValue
is not given, the first model in the collection
is used as the initial value. fn
is invoked with four arguments: (accumulator, currentValue, currentIndex, array).
const collection = new mvc.Collection([new mvc.Model({ id: 1, label: 'a' })]);
console.log(collection.reduce((acc, model) => acc.get('id') + model.id )); // 2
collection.remove(models, [options])
Remove a model (or an array of models) from the collection, and return them. Each model can be a Model instance, an id
string or a
JS object, any value acceptable as the id
argument of collection.get
.
Fires a "remove"
event for each model, and a single "update"
event afterwards, unless { silent: true }
is passed. The model's index before removal is available to listeners as options.index
.
collection.reset([models], [options])
Use reset to replace a collection with a new list of models (or attribute hashes), triggering a single "reset"
event on completion,
and without triggering any "add"
or "remove"
events on any models. Returns the newly-set models. For convenience,
within a "reset"
event, the list of any previous models is available as options.previousModels
.
Pass null
for models
to empty your Collection with options
.
Calling collection.reset()
without passing any models as arguments will empty the entire collection.
collection.set(models, [options])
The set method performs a "smart" update of the collection with the passed list of models. If a model in the list isn't yet in the collection
it will be added; if the model is already in the collection its attributes will be merged; and if the collection contains any models that aren't
present in the list, they'll be removed. All of the appropriate "add"
, "remove"
, and "change"
events are
fired as this happens, with a single "update"
event at the end. Returns the touched models in the collection. If you'd like to
customize this behavior, you can change it with options: { add: false }
, { remove: false }
, or
{ merge: false }
.
If a model
property is defined, you may also pass raw attributes objects and options,
and have them be vivified as instances of the model using the provided options. If you set a
comparator
, the collection will automatically sort itself and trigger a
"sort"
event, unless you pass { sort: false }
or use the { at: index }
option. Pass
{ at: index }
to splice the model(s) into the collection at the specified index
.
const players = new mvc.Collection([ carlsen, nakamura, caruana, liren ]);
players.set([ carlsen, nakamura, caruana, firouzja ]);
// Fires a "remove" event for "liren", and an "add" event for "firouzja".
// Updates any of "caruana", "nakamura", and carlsen's attributes that may have changed.
collection.shift([options])
Remove and return the first model from a collection. Takes the same options as
remove()
.
collection.slice(begin, end)
Return a shallow copy of this collection's models, using the same options as native Array.prototype.slice.
collection.sort([options])
Force a collection to re-sort itself. Note that a collection with a comparator
will sort itself automatically whenever a model is added. To disable sorting when adding a model, pass { sort: false }
to
add()
. Calling sort triggers a "sort"
event on the collection.
collection.toJSON([options])
Return an array containing the attributes hash of each model (via toJSON
) in the
collection. This can be used to serialize and persist the collection as a whole.
collection.unshift(model, [options])
Like add()
, but always adds a model at the beginning of the collection and never sorts.
A module that can be mixed in to any object in order to provide it with a custom event channel. You may bind a callback to an event with
on
or remove with off
; trigger
-ing an event fires all callbacks in succession. Events do not have to be
declared before they are bound, and may take passed arguments.
const object = {};
joint.util.assign(object, joint.mvc.Events);
object.on('expand', function(msg){ alert('expanded' + msg); });
object.trigger('expand', 'the example');
Here's the complete list of built-in JointJS events, with arguments. You're also free to trigger your own events on Models, Collections and Views as you see fit.
"add" (model, collection, options)
- when a model is added to a collection."remove" (model, collection, options)
- when a model is removed from a collection."update" (collection, options)
- single event triggered after any number of models have been added, removed or changed in a collection."reset" (collection, options)
- when the collection's entire contents have been reset."sort" (collection, options)
- when the collection has been re-sorted."change" (model, options)
- when a model's attributes have changed."changeId" (model, previousId, options)
- when the model's id has been updated."change:[attribute]" (model, value, options)
- when a specific attribute has been updated."invalid" (model, error, options)
- when a model's validation fails."all"
- this special event fires for any triggered event, passing the event name as the first argument followed by all trigger arguments.
Generally speaking, when calling a function that emits an event (model.set
, collection.add
, and so on...), if you'd
like to prevent the event from being triggered, you may pass {silent: true}
as an option. Note that this is rarely, perhaps even
never, a good idea. Passing through a specific flag in the options for your event callback to look at, and choose to ignore,
will usually work out better.
object.listenTo(other, event, callback)
Tell an object to listen to a particular event on an 'other' object. listenTo
allows the object to keep track of the events, and
they can be removed all at once later on. The callback will always be called with object as context.
view.listenTo(model, 'change', view.render);
object.listenToOnce(other, event, callback)
Just like listenTo
, but causes the bound callback to fire only once before being removed.
object.off([event], [callback], [context])
Remove a previously-bound callback function from an object. If no context is specified, all of the versions of the callback with different contexts will be removed. If no callback is specified, all callbacks for the event will be removed. If no event is specified, callbacks for all events will be removed.
// Removes just the `onChange` callback.
object.off('change', onChange);
// Removes all "change" callbacks.
object.off('change');
// Removes the `onChange` callback for all events.
object.off(null, onChange);
// Removes all callbacks for `context` for all events.
object.off(null, null, context);
// Removes all callbacks on `model`(including internal JointJS events).
model.off();
Note that calling model.off()
, for example, will indeed remove all events on the model — including events that JointJS uses for
internal bookkeeping.
object.on(event, callback, [context])
Bind a callback function to an object. The callback will be invoked whenever the event is fired. If you have a large number of different events
on a page, the convention is to use colons to namespace them: "poll:start"
, or "change:selection"
.
model.on('change', ...);
// Space-delimited list for more than one event
film.on('change:title change:director', ...);
// Supply a context value for "this" when the callback is invoked by passing the optional last argument
model.on('change', this.render, this);
Callbacks bound to the special "all"
event will be triggered when any event occurs, and are passed the name of the event as
the first argument. For example, to proxy all events from one object to another:
proxy.on("all", function(eventName) {
object.trigger(eventName);
});
All JointJS event methods also support an event map syntax, as an alternative to positional arguments:
book.on({
"change:author": authorPane.update,
"change:title change:subtitle": titleView.update
});
To supply a context value for this
when the callback is invoked, pass the optional last argument:
model.on('change', this.render, this)
or model.on({change: this.render}, this)
.
object.once(event, callback, [context])
Just like on
, but causes the bound callback to fire only once before being removed. When multiple events are passed in using the
space separated syntax, the event will fire once for every event you passed in, not once for a combination of all events.
object.stopListening([other], [event], [callback])
Tell an object to stop listening to events. Either call stopListening
with no arguments to have the object remove all of its
registered callbacks ... or be more precise by telling it to remove just the events it's listening to on a
specific object, or a specific event, or just a specific callback.
view.stopListening();
view.stopListening(model);
object.trigger(event, [*args])
Trigger callbacks for the given event, or space-delimited list of events. Subsequent arguments to trigger will be passed along to the event callbacks.
A listener is an object that allows you to listen for events triggered by other objects and, if needed, remove them all at once.
// Example setup for switching between display mode and edit mode
class ViewController extends mvc.Listener {
startListening() {
const [{ paper }] = this.callbackArguments;
this.listenTo(paper, 'element:mouseenter', (_appContext, elementView) => {
joint.highlighters.mask.add(elementView, 'body', 'highlighted-element');
});
this.listenTo(paper, 'element:mouseleave', (_appContext, elementView) => {
joint.highlighters.mask.remove(elementView, 'highlighted-element');
});
}
}
class EditController extends mvc.Listener {
startListening() {
const [{ paper }] = this.callbackArguments;
this.listenTo(paper, 'element:pointerdblclick', (appContext, elementView) => {
const { model } = elementView;
if (appContext.graph.getConnectedLinks(model).length > 0) return;
model.remove();
});
}
}
const appContext = { paper, graph };
const viewController = new ViewController(appContext);
const editController = new EditController(appContext);
let editMode = false;
function toggleEditMode(canEdit = !editMode) {
editMode = canEdit;
if (editMode) {
editController.startListening();
viewController.stopListening();
} else {
viewController.startListening();
editController.stopListening();
}
}
// start app in view mode
toggleEditMode(false);
listener.callbackArguments
An array of constructor arguments.
const listener = new mvc.Listener(callbackArg1, callbackArg2);
listener.callbackArguments; // [callbackArg1, callbackArg2]
listener.listenTo(object, event, callback [, context])
Tell the listener to listen to a particular event on an object.
listener.listenTo(model, 'change', onModelChangeCallback);
To supply a context value for this when the callback is invoked, pass the optional last argument:
listener.listenTo(object, this.update, this);
The arguments of the constructor are passed to the callback when it is invoked.
const listener = new mvc.Listener();
listener.listenTo(paper, 'element:pointerclick', (elementView) => {});
const listener = new mvc.Listener(callbackArgument);
listener.listenTo(paper, 'element:pointerclick', (callbackArgument, elementView) => {});
const listener = new mvc.Listener(callbackArg1, callbackArg2);
listener.listenTo(paper, 'element:pointerclick', (callbackArg1, callbackArg2, elementView) => {});
listener.listenTo(object, eventMap, [, context])
Tell the listener to listen to multiple events on an object (using event map syntax).
const listener = new mvc.Listener(callbackArgument);
listener.listenTo(graph, {
'remove': (callbackArgument, cell) => {},
'add': (callbackArgument, cell) => {}
});
listener.stopListening()
Tell the listener to remove all of its registered callbacks.
Models are the basic data object in JointJS. They are a discrete chunk of data and a bunch of useful, related methods for performing
computations and transformations on that data. dia.Cell
extends mvc.Model
.
model.changed
The changed property is the internal hash containing all the attributes
that have changed since its last
set()
. Please do not update changed directly since its state is internally maintained by
set()
. A copy of changed can be acquired from changedAttributes
.
model.changedAttributes([attributes])
Retrieve a hash of only the model's attributes that have changed since the last set()
, or
false
if there are none. Optionally, an external attributes
hash can be passed in, returning the attributes in
that hash which differ from the model. This can be used to figure out which portions of a view should be updated.
model.cid
A special property of models, the cid
or client id is a unique identifier automatically assigned to all models when they're first created.
model.cidPrefix
If your model has an id
that is anything other than an integer or a UUID, there is the possibility that it might collide with its
cid
. To prevent this, you can override the prefix that cids
start with.
model.clear([options])
Removes all attributes from the model, including the id
attribute. Fires a "change"
event unless silent
is passed as an option.
model.clone()
Returns a new instance of the model with identical attributes.
model.defaults or model.defaults()
The defaults hash (or function) can be used to specify the default attributes for your model. When creating an instance of the model, any unspecified attributes will be set to their default value.
Remember that in JavaScript, objects are passed by reference, so if you include an object as a default value, it will be shared among all instances. Instead, define defaults as a function.
mvc.Model.extend(properties, [classProperties])
To create a Model
class of your own, you can extend mvc.Model
. Provide instance properties, and optional
classProperties to be attached directly to the constructor function.
extend
correctly sets up the prototype chain, so subclasses created with extend
can be further extended and
subclassed.
const BaseShape = mvc.Model.extend({
initialize: function() {...}
});
const Shape = BaseShape.extend({...});
Brief aside on super
: JavaScript does not provide a simple way to call super — the function of the same name defined higher on
the prototype chain. If you override a core function like set
, and you want to invoke the parent object's implementation,
you'll have to explicitly call it, along these lines:
const Shape = mvc.Model.extend({
set: function(attributes, options) {
mvc.Model.prototype.set.apply(this, arguments);
...
}
});
model.get(attribute)
Get the current value of an attribute from the model. For example: model.get("title")
. get()
doesn't provide nesting
capability in the form of a string. That means any path representation is considered to be one attribute.
model.has(attribute)
Returns true
if the attribute is set to a non-null or non-undefined value.
model.hasChanged([attribute])
Has the model changed since its last set()
? If an attribute is passed, returns
true
if that specific attribute has changed.
Note that this method is only useful during the course of a "change"
event.
shape.on("change", function() {
if (shape.hasChanged("title")) {
...
}
});
model.id
A special property of models, the id
is an arbitrary string (integer id or UUID). If you set the id
in the attributes
hash, it will be copied onto the model as a direct property. model.id
should not be manipulated directly, it should be modified
only via model.set('id', …)
.
model.idAttribute
A model's unique identifier is stored under the id
attribute. If you're directly communicating with a backend (MongoDB) that
uses a different unique key, you may set a Model's idAttribute
to transparently map from that key to id
.
If you set idAttribute
, you may also want to override cidPrefix
.
const Shape = mvc.Model.extend({
idAttribute: "_id"
});
const shape = new Shape({ _id: 1, name: "Rectangle" });
console.log("Shape id: " + shape.id); // Shape id: 1
If the model defines an initialize function, it will be invoked when the model is created. Initialize is an empty function by default. Override it with your own initialization logic.
model.isValid(options)
Run validate
to check the model state.
The validate method receives the model attributes as well as any options passed to isValid
, if validate returns an error an
"invalid"
event is triggered, and the error is set on the model in the validationError
property.
For use with models as ES classes. If you define a preinitialize
method, it will be invoked when the Model is first created,
before any instantiation logic is run for the Model
.
class BaseShape extends mvc.Model {
preinitialize({ type }) {
this.type = type;
}
initialize() {...}
}
model.previous(attribute)
During a "change"
event, this method can be used to get the previous value of a changed attribute.
model.previousAttributes()
Return a copy of the model's previous attributes. Useful for getting a diff between versions of a model, or getting back to a valid state after an error occurs.
model.set(attribute)
Set a hash of attributes (one or many) on the model. If any of the attributes change the model's state, a "change"
event will be
triggered on the model. set()
doesn't provide nesting capability in the form of a string. That means any path representation is
considered to be one attribute.
model.toJSON([options])
Return a shallow copy of the model's attributes
object for JSON stringification. This can be used for persistance or serialization.
Note that this method doesn't return a JSON string but rather an object that can be then serialized to JSON with JSON.stringify()
.
model.unset(attribute, [options])
Remove an attribute by deleting it from the internal attributes hash. Fires a "change"
event unless silent
is passed
as an option.
model.validate(attributes, options)
This method is left undefined and you're encouraged to override it with any custom validation logic you have that can be performed in
JavaScript. If the attributes
are valid, don't return anything from validate
; if they are invalid return an error
of your choosing. It can be as simple as a string error message to be displayed, or a complete error object that describes the error
programmatically.
It's possible to tell set()
to validate the new attributes by passing { validate: true }
as an option. The validate
method receives the model attributes as well as any options passed to set()
. "invalid"
events are useful for
providing coarse-grained error messages at the model or collection level.
model.validationError
The value returned by validate
during the last failed validation.
A View is simply a JavaScript object that represents a logical chunk of UI in the DOM. The general idea is to organize your interface into
logical views, backed by models, each of which can be updated independently when the model changes, without having to redraw the page.
This allows you to bind your view's render
function to the model's "change"
event — and now everywhere that model
data is displayed in the UI, it is always immediately up to date.
view.attributes
A hash of attributes that will be set as HTML DOM element attributes on the view's el
(id, class, data-properties, etc.), or a
function that returns such a hash.
delegateEvents([events])
Provide declarative callbacks for DOM events within a view. If an events hash is not passed directly, uses this.events
as the
source. Events are written in the format { "event selector": "callback" }
. The callback may be either the name of a method on the
view, or a direct function body. Omitting the selector causes the event to be bound to the view's root element (this.el
).
By default, delegateEvents
is called within the View's constructor for you, so if you have a simple events hash,
all of your DOM events will always already be connected, and you will never have to call this function yourself.
The events
property may also be defined as a function that returns an events hash, to make it easier to programmatically define
your events, as well as inherit them from parent views.
Using delegateEvents
provides a number of advantages. All attached callbacks are bound to the view before being handed off, so
when the callbacks are invoked, this
continues to refer to the view object. When delegateEvents
is run again,
perhaps with a different events
hash, all callbacks are removed and delegated afresh — useful for views which need to behave
differently when in different modes.
A single-event version of delegateEvents
is available as delegate
. In fact, delegateEvents
is simply a
multi-event wrapper around delegate
. A counterpart to undelegateEvents
is available as undelegate
.
view.el
All views have a DOM element at all times (the el
property), whether they've already been inserted into the page or not.
In this fashion, views can be rendered at any time, and inserted into the DOM all at once, in order to get high-performance UI rendering with
as few reflows and repaints as possible.
this.el
can be resolved from a DOM selector string or an Element; otherwise it will be created from the view's
tagName
, className
, id
and attributes
properties. If none are set, this.el
is an empty div
, which is often just fine. An el
reference may also be passed in to the view's constructor.
const ShapeView = mvc.View.extend({
el: 'body'
});
const shape = new ShapeView();
console.log(shape.el) // <body>...</body>
view.events or view.events()
The events hash (or method) can be used to specify a set of DOM events that will be bound to methods on your View through
delegateEvents
. JointJS will automatically attach the event listeners at
instantiation time, right before invoking initialize
.
mvc.View.extend(properties, [classProperties])
Create a custom view class. You'll want to override the render
function, specify your declarative events
, and perhaps
the tagName
, className
, or id
of the View's root element.
const ShapeRow = mvc.View.extend({
tagName: "li",
className: "shape-row",
events: {
"click .icon": "open",
"click .button.edit": "openEditDialog"
},
initialize: function() {
this.listenTo(this.model, "change", this.render);
},
render: function() {
...
}
});
Properties like tagName
, id
, className
, el
, and events
may also be defined as
a function, if you want to wait to define them until runtime.
There are several special options that, if passed, will be attached directly to the view: model
, collection
,
el
, id
, className
, tagName
, attributes
and events
.
If the view defines an initialize
function, it will be called when the view is first created. Initialize is an empty function by
default. Override it with your own initialization logic.
If you'd like to create a view that references an element already in the DOM, pass in the element as an option:
new View({ el: existingElement })
For use with views as ES classes. If you define a preinitialize
method, it will be invoked when the view is first created, before
any instantiation logic is run. preinitialize
is an empty function by default. You can override it with a function or object.
class View extends mvc.View {
preinitialize({ autoRender }) {
this.autoRender = autoRender;
}
initialize() {
if (this.autoRender) {
this.listenTo(this.model, 'change', this.render);
}
}
}
view.remove()
Removes a view and its el
from the DOM, and calls stopListening
to remove
any bound events that the view has listenTo
'd.
view.render()
render
is the core function that your view should override, in order to populate its element (this.el
),
with the appropriate HTML. The convention is for render to always return this
to enable chained calls.
view.setElement(element)
Change the view's element (this.el
property) and re-delegate the view's events on the new element.
undelegateEvents()
Removes all of the view's delegated events. Useful if you want to disable or remove a view from the DOM temporarily.
Routers take an array of link vertices and transform them into an array of route points that the link should go through. The router
property of a link can be accessed with the link.router()
function.
The difference between vertices
and the route is that the vertices are user-defined while the route is computed. The route inserts additional private vertices to complement user vertices as necessary (e.g. to make sure the route is orthogonal).
There are five built-in routers:
'manhattan'
- smart orthogonal router'metro'
- smart octolinear router'normal'
- default simple router'orthogonal'
- basic orthogonal router'rightAngle'
- orthogonal router that goes in a given directionExample:
link.router('orthogonal', {
padding: 10
});
The default router is 'normal'
; this can be changed with the defaultRouter
paper option. Example:
paper.options.defaultRouter = {
name: 'orthogonal',
args: {
padding: 10
}
});
The 'manhattan'
and 'metro'
routers are our smart routers
; they automatically avoid obstacles (elements) in their way.
The 'orthogonal'
, 'manhattan'
and 'rightAngle'
routers generate routes consisting exclusively of vertical and horizontal segments. The 'metro'
router generates routes consisting of orthogonal and diagonal segments.
JointJS also contains mechanisms to define one's own custom routers.
Note that the modular architecture of JointJS allows mixing-and-matching routers with connectors as desired; for example, a link may be specified to use the 'jumpover'
connector on top of the 'manhattan'
router:
var link = new joint.shapes.standard.Link();
link.source(rect);
link.target(rect2);
link.router('manhattan');
link.connector('jumpover');
New routers can be defined in the joint.routers
namespace (e.g. joint.routers.myRouter
) or passed directly as a function to the router
property of a link (or to the defaultRouter
option of a paper).
In either case, the router function must return an array of route points that the link should go through (not including the start/end connection points). The function is expected to have the form function(vertices, args, linkView)
:
vertices | Array<g.Point> | The vertices of the route. |
---|---|---|
args | object | An object with additional optional arguments passed to the router method by the user when it was called (the args property). |
linkView | dia.LinkView | The LinkView of the connection. The Link model can be accessed as the model property; this may be useful for writing conditional logic based on link attributes. |
Example of a router defined in the joint.routers
namespace:
joint.routers.randomWalk = function(vertices, args, linkView) {
var NUM_BOUNCES = args.numBounces || 20;
vertices = joint.util.toArray(vertices).map(g.Point);
for (var i = 0; i < NUM_BOUNCES; i++) {
var sourceCorner = linkView.sourceBBox.center();
var targetCorner = linkView.targetBBox.center();
var randomPoint = g.Point.random(sourceCorner.x, targetCorner.x, sourceCorner.y, targetCorner.y);
vertices.push(randomPoint)
}
return vertices;
}
var link = new joint.shapes.standard.Link();
link.source(source);
link.target(target);
link.router('randomWalk', {
numBounces: 10
});
An example of a router passed as a function is provided below. Notice that this approach does not enable passing custom args
nor can it be serialized with the graph.toJSON()
function.
var link = new joint.shapes.standard.Link();
link.source(source);
link.target(target);
link.router(function(vertices, args, linkView) {
var NUM_BOUNCES = 20;
vertices = joint.util.toArray(vertices).map(g.Point);
for (var i = 0; i < NUM_BOUNCES; i++) {
var sourceCorner = linkView.sourceBBox.center();
var targetCorner = linkView.targetBBox.center();
var randomPoint = g.Point.random(sourceCorner.x, targetCorner.x, sourceCorner.y, targetCorner.y);
vertices.push(randomPoint)
}
return vertices;
});
The 'manhattan'
router is the smart version of the 'orthogonal'
router. It connects vertices with orthogonal line segments, inserting route points when necessary, while avoiding obstacles in its way. The router has useful options that determine how the algorithm behaves. These options can be passed as the router.args
property:
step | number | Size of the imaginary grid followed by the 'manhattan' pathfinding algorithm. Lower number -> higher complexity. The 'manhattan' router performs best when step has the same value as dia.Paper.option.gridSize . Default is 10 . |
---|---|---|
padding | number | object | Padding applied around start/end elements and obstacles. Default is the step value (see above). The util.normalizeSides function is used to understand the provided value. A single number is applied as padding to all sides of the elements. An object may be provided to specify values for left , top , right , bottom , horizontal and/or vertical sides. |
maximumLoops | number | The maximum number of iterations of the pathfinding loop. If the number of iterations exceeds this maximum, pathfinding is aborted and the fallback router ('orthogonal' ) is used instead. Higher number -> higher complexity. Default is 2000 . |
maxAllowedDirectionChange | number | Maximum change of direction of the 'manhattan' route, in degrees. Default is 90 . |
perpendicular | boolean | Should the linkView.perpendicular option be in effect? This causes the router not to link precisely to the anchor of the end element but rather to a point close by that is orthogonal. This creates a clean connection between the element and the first/last vertex of the route. Default is true . |
excludeEnds | Array<string> | An array with strings 'source' and/or 'target' that tells the algorithm that the given end element(s) should not be considered as an obstacle. Default is [] (both are considered obstacles). |
excludeTypes | Array<string> | An array of element types that should not be considered obstacles when calculating the route. Default is ['basic.Text'] . |
startDirections | Array<string> | An array that specifies the possible starting directions from an element. Change this in situations where you need the link to, for example, always start at the bottom of the source element (then, the value would be ['bottom'] ). Default is ['left', 'right', 'top', 'bottom'] (all directions allowed). |
endDirections | Array<string> | An array that specifies the possible ending directions to an element. Change this in situations where you need the link to, for example, always end at the bottom of the source element (then, the value would be ['bottom'] ). Default is ['left', 'right', 'top', 'bottom'] (all directions allowed). |
isPointObstacle | (point) => boolean | A function that determines whether a given point is an obstacle or not. If used, the padding, excludeEnds and excludeTypes options are ignored. |
fallbackRouter | (vertices, options, linkView) => Array<g.Point> | A function that is called when the routing fails. Use this in situations where you need to retry routing with more relaxed options e.g. no start/endDirections constraints. The default fallbackRouter is an orthogonal router. |
Example:
link.router('manhattan', {
excludeEnds: ['source'],
excludeTypes: ['myNamespace.MyCommentElement'],
startDirections: ['top'],
endDirections: ['bottom']
});
The 'metro'
router is a modification of the 'manhattan'
router that produces an octolinear route (i.e. a route consisting of orthogonal and diagonal line segments, akin to the London Underground map design). It also avoids obstacles, and accepts the same router.args
as 'manhattan'
, with a few modifications:
maximumLoops | number | Does not use the 'orthogonal' router as fallback if path cannot to be found in the given number of iterations. Instead, a custom octolinear fallback route is used that does not avoid obstacles. |
---|---|---|
maxAllowedDirectionChange | number | Default changes to 45 . |
startDirection | Array<string> | Same as 'manhattan' (i.e. only the four orthogonal directions are accepted as start directions). |
endDirection | Array<string> | Same as 'manhattan' (i.e. only the four orthogonal directions are accepted as end directions). |
Example:
link.router('metro', {
excludeEnds: ['source'],
excludeTypes: ['myNamespace.MyCommentElement'],
startDirections: ['top'],
endDirections: ['bottom']
});
The 'normal'
router is the default router for links and it is the simplest router. It does not have any options and it does not do anything; it returns the vertices passed to it without modification as the route to be taken.
Example:
link.router('normal');
The 'orthogonal'
router returns a route with orthogonal line segments. It generates extra route points in order to create the right angles on the way. It does not avoid obstacles. The router has one optional argument; this can be passed as the router.args.elementPadding
property:
padding | number | object | The minimum distance from element at which the first/last route angle may be placed. Default is 20 . The util.normalizeSides function is used to understand the provided value. A single number is applied as padding to all sides of the elements. An object may be provided to specify values for left , top , right , bottom , horizontal and/or vertical sides. |
---|
Example:
link.router('orthogonal', {
padding: 10
});
The 'rightAngle'
router returns a route with orthogonal line segments just like 'orthogonal'
router,
except it chooses the direction of the link based on the position of the source and target anchors (or port position).
This router avoids collisions with source and target elements, but does not avoid other obstacles.
The behavior of the router can be modified by passing optional arguments:
linkTools.Vertices
will not result in any change of path.
margin | number | The minimum distance between the route and the element. The default is 20 . |
---|---|---|
sourceDirection | rightAngle.Directions | The direction of the link from the source anchor. The default is rightAngle.Directions.AUTO . |
targetDirection | rightAngle.Directions | The direction of the link from the target anchor. The default is rightAngle.Directions.AUTO . |
Example:
link.router('rightAngle', {
margin: 10,
sourceDirection: routers.rightAngle.Directions.TOP,
targetDirection: routers.rightAngle.Directions.BOTTOM
});
Available values for the sourceDirection
and targetDirection
options:
rightAngle.Directions.AUTO |
It automatically decides which direction to use. If the link is connected to a port, the
rightAngle.Directions.MAGNET_SIDE is used. Otherwise, it uses rightAngle.Directions.ANCHOR_SIDE .
|
---|---|
rightAngle.Directions.ANCHOR_SIDE | Determines the direction depending on the side of the magnet where the anchor is located. |
rightAngle.Directions.MAGNET_SIDE | Determines the direction depending on the side of the element where the magnet (e.g. port) is located. |
rightAngle.Directions.LEFT | Sets the direction of the link to the left side. |
rightAngle.Directions.RIGHT | Sets the direction of the link to the right side. |
rightAngle.Directions.TOP | Sets the direction of the link to the top side. |
rightAngle.Directions.BOTTOM | Sets the direction of the link to the bottom side. |
The Standard plugin provides you with ready-to-use, high-performance versions of the most common shapes. The shapes can be used as they are or can serve as an inspiration for creating custom shapes.
An image with a border and label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
image | SVGImageElement | Image body of the shape |
border | SVGRectElement | Border around the image |
background | SVGRectElement | Area behind the image |
label | SVGTextElement | Text below the image |
var borderedImage = new joint.shapes.standard.BorderedImage();
borderedImage.resize(150, 100);
borderedImage.position(225, 410);
borderedImage.attr('root/title', 'joint.shapes.standard.BoarderedImage');
borderedImage.attr('label/text', 'Bordered\nImage');
borderedImage.attr('border/rx', 5);
borderedImage.attr('image/xlinkHref', 'image.png');
borderedImage.addTo(graph);
A circle with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGCircleElement | Circular body of the shape |
label | SVGTextElement | Text inside the body |
var circle = new joint.shapes.standard.Circle();
circle.resize(100, 100);
circle.position(250, 10);
circle.attr('root/title', 'joint.shapes.standard.Circle');
circle.attr('label/text', 'Circle');
circle.attr('body/fill', 'lightblue');
circle.addTo(graph);
A cylinder with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGPathElement | Lateral area of the cylinder
The shape has a custom attribute |
top | SVGEllipseElement | Top of the cylinder (cylinder base) |
label | SVGTextElement | Text inside the body |
cylinder.topRy()
Return the vertical radius of the exposed area of the cylinder base (the value of the body/lateralArea
attribute; 10
by default).
cylinder.topRy(t, [opt])
Set the cylinder vertical radius of the exposed area of the cylinder base.
If the provided value is a percentage, it is relative to the refBBox.height
of the shape. In practice, only values between '0%'
and '50%'
make sense. If the provided value is a number, it determines the vertical radius directly. Only values between 0
and half of refBBox.height
make sense.
The function automatically sets the value of several attributes: body/lateralArea
; and top/ry
, top/cy
, top/refRy
and top/refCy
. If these arguments need to be modified further, make sure to assign them only after calling cylinder.topRy
.
Example usage:
var cylinder = new standard.Cylinder();
cylinder.resize(100, 200);
cylinder.position(525, 75);
cylinder.attr('root/title', 'joint.shapes.standard.Cylinder');
cylinder.attr('body/fill', 'lightgray');
cylinder.attr('top/fill', 'gray');
cylinder.attr('label/text', 'Cylinder');
cylinder.topRy('10%');
cylinder.addTo(graph);
A double line link.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
line | SVGPathElement | Inner connection |
outline | SVGPathElement | Outer connection |
var doubleLink = new joint.shapes.standard.DoubleLink();
doubleLink.prop('source', { x: 500, y: 600 });
doubleLink.prop('target', { x: 450, y: 750 });
doubleLink.prop('vertices', [{ x: 500, y: 700 }]);
doubleLink.attr('root/title', 'joint.shapes.standard.DoubleLink');
doubleLink.attr('line/stroke', '#30d0c6');
doubleLink.addTo(graph);
An ellipse with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGEllipseElement | Elliptical body of the shape |
label | SVGTextElement | Text inside the body |
var ellipse = new joint.shapes.standard.Ellipse();
ellipse.resize(150, 100);
ellipse.position(425, 10);
ellipse.attr('root/title', 'joint.shapes.standard.Ellipse');
ellipse.attr('label/text', 'Ellipse');
ellipse.attr('body/fill', 'lightblue');
ellipse.addTo(graph);
An image embedded into a rectangle with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGRectElement | Rectangular body of the shape |
image | SVGImageElement | Image inside the body |
label | SVGTextElement | Text next to the image |
var embeddedImage = new joint.shapes.standard.EmbeddedImage();
embeddedImage.resize(150, 100);
embeddedImage.position(425, 410);
embeddedImage.attr('root/title', 'joint.shapes.standard.EmbeddedImage');
embeddedImage.attr('label/text', 'Embedded\nImage');
embeddedImage.attr('image/xlinkHref', 'image.png');
embeddedImage.addTo(graph);
A rectangle with header.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGRectElement | Rectangular body of the shape |
header | SVGRectElement | Rectangular header of the shape |
headerText | SVGTextElement | Text inside the header |
bodyText | SVGTextElement | Text inside the body |
var headeredRectangle = new joint.shapes.standard.HeaderedRectangle();
headeredRectangle.resize(150, 100);
headeredRectangle.position(25, 610);
headeredRectangle.attr('root/title', 'joint.shapes.standard.HeaderedRectangle');
headeredRectangle.attr('header/fill', 'lightgray');
headeredRectangle.attr('headerText/text', 'Header');
headeredRectangle.attr('bodyText/text', 'Headered\nRectangle');
headeredRectangle.addTo(graph);
An image with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
image | SVGImageElement | Image body of the shape |
label | SVGTextElement | Text below the image |
var image = new joint.shapes.standard.Image();
image.resize(150, 100);
image.position(25, 410);
image.attr('root/title', 'joint.shapes.standard.Image');
image.attr('label/text', 'Image');
image.attr('image/xlinkHref', 'image.png');
image.addTo(graph);
An image inscribed in an ellipse with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
image | SVGImageElement | Image inscribed in the ellipse |
border | SVGEllipseElement | Border around the ellipse |
background | SVGEllipseElement | Area of the ellipse |
label | SVGTextElement | Text below the shape |
const inscribedImage = new joint.shapes.standard.InscribedImage();
inscribedImage.resize(150, 100);
inscribedImage.position(225, 410);
inscribedImage.attr('root/title', 'joint.shapes.standard.InscribedImage');
inscribedImage.attr('label/text', 'Inscribed Image');
inscribedImage.attr('border/strokeWidth', 5);
inscribedImage.attr('background/fill', 'lightgray');
inscribedImage.attr('image/xlinkHref', 'image.png');
inscribedImage.addTo(graph);
A single line link.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
line | SVGPathElement | Visible connection of the link |
wrapper | SVGPathElement | Invisible wrapper around the connection to make the line thicker, so the user can interact with the link more easily. |
var link = new joint.shapes.standard.Link();
link.prop('source', { x: 450, y: 600 });
link.prop('target', { x: 400, y: 750 });
link.prop('vertices', [{ x: 450, y: 700 }]);
link.attr('root/title', 'joint.shapes.standard.Link');
link.attr('line/stroke', '#fe854f');
link.addTo(graph);
A path with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGPathElement | Generic body of the shape |
label | SVGTextElement | Text inside the body |
var path = new joint.shapes.standard.Path();
path.resize(100, 100);
path.position(50, 210);
path.attr('root/title', 'joint.shapes.standard.Path');
path.attr('label/text', 'Path');
path.attr('body/refD', 'M 0 5 10 0 C 20 0 20 20 10 20 L 0 15 Z');
path.addTo(graph);
A polygon with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGPolygonElement | Polygonal body of the shape |
label | SVGTextElement | Text inside the body |
var polygon = new joint.shapes.standard.Polygon();
polygon.resize(100, 100);
polygon.position(250, 210);
polygon.attr('root/title', 'joint.shapes.standard.Polygon');
polygon.attr('label/text', 'Polygon');
polygon.attr('body/refPoints', '0,10 10,0 20,10 10,20');
polygon.addTo(graph);
A polyline with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGPolylineElement | Generic body of the shape |
label | SVGTextElement | Text inside the body |
var polyline = new joint.shapes.standard.Polyline();
polyline.resize(100, 100);
polyline.position(450, 210);
polyline.attr('root/title', 'joint.shapes.standard.Polyline');
polyline.attr('label/text', 'Polyline');
polyline.attr('body/refPoints', '0,0 0,10 10,10 10,0');
polyline.addTo(graph);
A rectangle with a label.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGRectElement | Rectangular body of the shape |
label | SVGTextElement | Text inside the body |
var rectangle = new joint.shapes.standard.Rectangle();
rectangle.resize(100, 100);
rectangle.position(50, 10);
rectangle.attr('root/title', 'joint.shapes.standard.Rectangle');
rectangle.attr('label/text', 'Rectangle');
rectangle.attr('body/fill', 'lightblue');
rectangle.addTo(graph);
A thicker line with shadow.
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
line | SVGPathElement | Visible connection of the link |
shadow | SVGPathElement | Shadow of the connection |
var shadowLink = new joint.shapes.standard.ShadowLink();
shadowLink.prop('source', { x: 550, y: 600 });
shadowLink.prop('target', { x: 500, y: 750 });
shadowLink.prop('vertices', [{ x: 550, y: 700 }]);
shadowLink.attr('root/title', 'joint.shapes.standard.ShadowLink');
shadowLink.attr('line/stroke', '#5654a0');
shadowLink.addTo(graph);
A rectangle with an HTML label (with a fallback to SVG label for IE).
Supportedattrs
properties
Selector | Node | Description |
---|---|---|
root | SVGGElement | Container of all nodes |
body | SVGRectElement | Rectangular body of the shape |
label | HTMLDivElement | Text inside the body |
var textBlock = new joint.shapes.standard.TextBlock();
textBlock.resize(100, 100);
textBlock.position(250, 610);
textBlock.attr('root/title', 'joint.shapes.standard.TextBlock');
textBlock.attr('body/fill', 'lightgray');
textBlock.attr('label/text', 'Hyper Text Markup Language');
// Styling of the label via `style` presentation attribute (i.e. CSS).
textBlock.attr('label/style/color', 'red');
textBlock.addTo(graph);
util.assign(destinationObject, [...sourceObjects])
Assigns own enumerable string keyed properties of source objects to the destination object. Source objects are applied from left to right. Subsequent sources overwrite property assignments of previous sources.
destinationObject | Object | The destination object |
---|---|---|
[sourceObjects] | Object[] | The source objects |
util.bindAll(object, methodNames)
Binds methods of an object to the object itself, overwriting the existing method.
object | Object | The object to bind and assign the bound methods to |
---|---|---|
methodNames | string|string[] | The object method names to bind |
util.breakText(text, size [, attrs, opt])
Break the provided text
into lines so that it can fit into the bounding box defined by size.width
and (optionally) size.height
.
The function creates a temporary SVG <text>
element and adds words to it one by one, measuring the element's actual rendered width and height at every step. If a word causes a line to overflow size.width
, a newline character ('\n'
) is generated. If a newline causes the text to overflow size.height
, the string is cut off.
The returned string is a (possibly truncated) copy of text
with newline characters at appropriate locations.
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100 })
// 'lorem ipsum\ndolor sit amet\nconsectetur\nadipiscing elit'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 })
// 'lorem ipsum\ndolor sit amet'
The attrs
parameter is an object with SVG attributes that should be set on the temporary SVG text element while it is being measured (such as 'font-weight'
, 'font-size'
, 'font-family'
, etc.). For example, an area of fixed size can obviously fit more words of font size 12px
than it can fit words of font size 36px
. If nothing can fit, an empty string is returned. (If an attrs
object is not provided, the browser's default style is used.)
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 12 })
// 'lorem ipsum dolor\nsit amet consectetur\nadipiscing elit'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 16 })
// 'lorem ipsum\ndolor sit amet'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 36 })
// 'lorem'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 72 })
// ''
The method also accepts the following additional options:
opt.separator
- a string or a regular expression which denotes how to split the text into words. Defaults to ' '
opt.eol
- a string representing the end-of-line symbol. Defaults to '\n'
.opt.ellipsis
- a boolean that specifies whether the ellipsis symbol ('…'
, U+2026 HORIZONTAL ELLIPSIS
) should be displayed when the text overflows. Defaults to false
. If you provide a string, that string will be used as the ellipsis symbol instead.opt.svgDocument
- an SVG document to which the temporary SVG text element should be added. By default, an SVG document is created automatically for you.opt.hyphen
- a string or regex representing the hyphen symbol. If required, the method tries to break long words at hyphens first.opt.maxLineCount
- a number to limit the maximum number of lines.opt.preserveSpaces
- preserve the text spaces (avoid all consecutive spaces being deleted and replaced by one space, and preserve a space at the beginning and the end of the text).Examples of text with the ellipsis
option specified:
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 12 }, { ellipsis: true })
// 'lorem ipsum dolor\nsit amet consectetur\nadipiscing elit'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 16 }, { ellipsis: true })
// 'lorem ipsum\ndolor sit ame…'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 16 }, { ellipsis: '...!?' })
// 'lorem ipsum\ndolor sit a...!?'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 36 }, { ellipsis: true })
// 'lore…'
joint.util.breakText('lorem ipsum dolor sit amet consectetur adipiscing elit', { width: 100, height: 50 }, { 'font-size': 36 }, { ellipsis: true })
// ''
If you need to wrap text inside a text
attribute of an Element, you should use the textWrap
attribute instead. It does not require you to provide explicit size measurements, and it automatically responds to element resizing.
util.camelCase([string=''])
Converts string to camel case.
[string=''] | string | The string to convert |
---|
util.cancelFrame(requestId)
Cancels an animation frame request identified by requestId
previously scheduled through a call to joint.util.nextFrame.
util.clone(value)
Creates a shallow clone of value.
value | any | The value to clone |
---|
util.cloneDeep(value)
This method is like util.clone
except that it recursively clones value.
value | any | The value to recursively clone |
---|
util.dataUriToBlob(dataUri)
Convert a Data URI string into a Blob object.
util.debounce(func, [wait=0], [options={}])
Creates a debounced function that delays invoking func until after wait milliseconds have elapsed since the last time the debounced function was invoked. The debounced function comes with a cancel method to cancel delayed func invocations and a flush method to immediately invoke them. Provide options to indicate whether func should be invoked on the leading and/or trailing edge of the wait timeout. The func is invoked with the last arguments provided to the debounced function. Subsequent calls to the debounced function return the result of the last func invocation.
func | Function | The function to debounce |
---|---|---|
[wait=0] | number | The number of milliseconds to delay |
[options={}] | Object | The options object |
[options.leading=false] | boolean | Specify invoking on the leading edge of the timeout |
[options.maxWait] | number | The maximum time func is allowed to be delayed before it's invoked |
[options.trailing=true] | boolean | Specify invoking on the trailing edge of the timeout |
util.deepMixin(destinationObject, [...sourceObjects])
(Deprecated) An alias for util.assign
, use it instead.
util.deepSupplement(destinationObject [...sourceObjects])
(Deprecated) An alias for util.defaultsDeep
, use it instead.
util.defaults(destinationObject [...sourceObjects])
Assigns own and inherited enumerable string keyed properties of source objects to the destination object for all destination properties that resolve to undefined. Source objects are applied from left to right. Once a property is set, additional values of the same property are ignored.
destinationObject | Object | The destination object |
---|---|---|
[sourceObjects] | Object[] | The source objects |
util.defaultsDeep(destinationObject [...sourceObjects])
This method is like util.defaults
except that it recursively assigns default properties.
destinationObject | Object | The destination object |
---|---|---|
[sourceObjects] | Object[] | The source objects |
util.difference(array, [...values])
Creates an array of array values not included in the other given arrays using SameValueZero for equality comparisons. The order and references of result values are determined by the first array.
array | any[] | The array to inspect |
---|---|---|
[values] | any[] | The values to exclude |
util.downloadBlob(blob, fileName)
Download provided Blob object as a file with given fileName
.
util.downloadDataUri(dataUri, fileName)
Download provided Data URI string as a file with given fileName
.
util.flattenDeep(array)
Recursively flattens array.
array | any[] | The array to flatten |
---|
util.flattenObject(object, delim, stop)
Flatten a nested object
up until the stop
function returns true
. The stop
function takes the value of the node currently traversed. delim
is a delimiter for the combined keys in the resulting object. Example:
joint.util.flattenObject({
a: {
a1: 1,
a2: 2,
a3: {
a31: 5,
a32: {
a321: { a3211: 5 }
}
}
},
b: 6
}, '/', function(v) { return !!v.a321; });
/*
{
"a/a1": 1,
"a/a2": 2,
"a/a3/a31": 5,
"a/a3/a32": {
"a321": {
"a3211": 5
}
},
"b": 6
}
*/
util.forIn(object, [iteratee])
Iterates over elements of collection and invokes iteratee for each element. The iteratee is invoked with three arguments: (value, index|key, collection). Iteratee functions may exit iteration early by explicitly returning false.
collection | any[]|Object | The collection to iterate over |
---|---|---|
[iteratee] | Function | The function invoked per iteration |
util.getByPath(object, path, delim)
Return a value at the path
in a nested object
. delim
is the delimiter used in the path
Example:
joint.util.getByPath({ a: { aa: { aaa: 3 } } }, 'a/aa/aaa', '/');
// 3
util.getElementBBox(el)
Return a bounding box of the element el
. The advantage of this method is that it can handle
both HTML and SVG elements. The resulting object is of the form { x: Number, y: Number, width: Number, height: Number }
.
util.getRectPoint(rect, positionName)
Returns a g.Point on the rectangle specified by the keyword positionName
.
The rect
object has the form { x: Number, y: Number, width: Number, height: Number }
.
The positionName
keyword can be one of the following:
'top-left'
'top'
'top-right'
'bottom-left'
'bottom'
'bottom-right'
'left'
'right'
'center'
util.groupBy(collection, [iteratee])
Creates an object composed of keys generated from the results of running each element of collection thru iteratee. The order of grouped values is determined by the order they occur in collection. The corresponding value of each key is an array of elements responsible for generating the key. The iteratee is invoked with one argument: (value).
collection | any[]|Object | The collection to iterate over |
---|---|---|
[iteratee] | Function | The function invoked per iteration |
util.guid()
Return an identifier unique for the page.
util.has(object, propertyPath)
Checks if path is a direct property of object.
object | Object | The object to query |
---|---|---|
propertyPath | string[]|string | The path to check |
util.hashCode(str)
Return a simple hash code from a string.
util.imageToDataUri(url, callback)
Convert an image at url
to the Data URI scheme. The function is able to handle PNG, JPG and SVG formats. Useful if you need to embed images directly into your diagram instead of having them referenced externally.
The callback
function has the following signature: function(err, dataUri) {}
.
util.intersection([...arrays])
Creates an array of unique values that are included in all given arrays using SameValueZero for equality comparisons. The order and references of result values are determined by the first array.
[arrays] | any[][] | The arrays to inspect |
---|
util.invoke(collection, path, [args])
Invokes the method at path of each element in collection, returning an array of the results of each invoked method. Any additional arguments are provided to each invoked method. If path is a function, it's invoked for, and this bound to, each element in collection.
collection | any[]|Object | The collection to iterate over |
---|---|---|
path | string[]|string|Function | The path of the method to invoke or the function invoked per iteration |
[args] | any[] | The arguments to invoke each method with |
util.invokeProperty(object, path, [args])
Invokes the method at path of object.
object | Object | The object to query |
---|---|---|
path | string[]|string | The path of the method to invoke |
[args] | any[] | The arguments to invoke the method with |
util.isBoolean(value)
Return true
when value
is a boolean.
util.isEmpty(value)
Checks if value is an empty object, collection, map, or set.
Objects are considered empty if they have no own enumerable string keyed properties.
Array-like values such as arguments objects, arrays, strings, or jQuery-like collections are considered empty if they have a length of 0. Similarly, maps and sets are considered empty if they have a size of 0.
value | any | The value to check |
---|
util.isEqual(value, otherValue)
Performs a deep comparison between two values to determine if they are equivalent.
value | any | The value to compare |
---|---|---|
otherValue | any | The other value to compare |
util.isFunction(value)
Checks if value is classified as a Function object.
value | any | The value to check |
---|
util.isNumber(value)
Return true
when value
is a number.
util.isObject(value)
Return true
when value
is an object (plain object or a function).
util.isPercentage(value)
Return true
when value
is a string that holds a percentage value, e.g. '10%'
.
util.isPlainObject(value)
Checks if value is a plain object, that is, an object created by the Object constructor or one with a [[Prototype]] of null.
value | any | The value to check |
---|
util.isString(value)
Return true
when value
is a string.
util.merge(destinationObject, [...sourceObjects], [customizer])
This method is like joint.util.assign
except that it recursively merges own and inherited enumerable string keyed properties of source objects into the destination object. Source properties that resolve to undefined are skipped if a destination value exists. Array and plain object properties are merged recursively. Other objects and value types are overridden by assignment. Source objects are applied from left to right. Subsequent sources overwrite property assignments of previous sources. In addition this method accepts a customizer which is invoked to produce the merged values of the destination and source properties. The customizer is invoked with six arguments: (objValue, srcValue, key, object, source, stack)
destinationObject | Object | The destination object |
---|---|---|
[sourceObjects] | Object[] | The source objects |
customizer | Function | The function to customize assigned values |
util.mixin(destinationObject, [...sourceObjects])
(Deprecated) An alias for util.assign
, use it instead.
util.nextFrame(callback [, context, ...args])
Tell the browser to schedule the callback
function to be called before the next repaint.
This is a cross-browser version of the window.requestAnimationFrame
function. It returns an ID of the frame request.
You can optionally pass a context
for your callback function. Any further arguments are passed as arguments to the callback function.
util.noop()
An empty function with no return statement (void
in Typescript).
util.normalizeEvent(evt)
Normalize the provided event.
For touch events, the normalized event receives a copy of all properties from evt.originalEvent
that are not included in evt
.
Additionally, if evt.target.correspondingUseElement
is defined (as in IE), that element is assigned as target
in the normalized event.
util.normalizeSides(box)
Return a new object of the form { top: Number, right: Number, bottom: Number, left: Number }
.
If box
is a number, the value of all four sides will be this number. If box
is an object with some/all of the top
, right
, bottom
, left
properties defined, the values of these properties will be used.
Composite properties horizontal
(for right and left side) and vertical
(for top and bottom side) can be used, as well; they will be destructured appropriately. When two properties clash (e.g. horizontal: 10
and left: 5
), the more specific one prevails (here the returned object would contain right: 10
and left: 5
).
If any property is missing, its side will be set to 0
in the resulting object.
joint.util.normalizeSides() // { top: 0, right: 0, bottom: 0, left: 0 }
joint.util.normalizeSides(5) // { top: 5, right: 5, bottom: 5, left: 5 }
joint.util.normalizeSides({ horizontal: 5 }) // { top: 0, right: 5, bottom: 0, left: 5 }
joint.util.normalizeSides({ left: 5 }) // { top: 0, right: 0, bottom: 0, left: 5 }
joint.util.normalizeSides({ horizontal: 10, left: 5 }) // { top: 0, right: 10, bottom: 0, left: 5 }
joint.util.normalizeSides({ horizontal: 0, left: 5 }) // { top: 0, left: 5, right: 0, bottom: 0 }
JointJS and JointJS+ use this method internally whenever there is an option object that can be specified either by a number or a (possibly incomplete) object with sides (for example, the padding
option).
util.omit(object, [...paths])
Opposite of util.pick
, this method creates an object composed of the own and inherited enumerable property paths of object that are not omitted.
object | Object | The source object |
---|---|---|
[paths] | string[]|string[][] | The property paths to omit |
util.parseCssNumeric(value)
Parse the provided value as a float and return an object with the number as the value
parameter. If the value cannot be converted into a number, return null
.
If the number is followed by a unit, assign it as the unit
parameter in the return object. If there is no unit, assign an empty string. Examples:
joint.util.parseCssNumeric('hello'); // => null
joint.util.parseCssNumeric(1.1); // => { value: 1.1, unit: '' }
joint.util.parseCssNumeric('1.1'); // => { value: 1.1, unit: '' }
joint.util.parseCssNumeric('1.1px'); // => { value: 1.1, unit: 'px' }
joint.util.parseCssNumeric('1.1em'); // => { value: 1.1, unit: 'em' }
util.parseCssNumeric(value, restrictUnits)
Parse the provided value and only accept it if its unit is part of the restrictUnits
parameter; otherwise return null
.
The restrictUnits
parameter can be a string or an array of strings. If you provide an empty string, that means you are expecting value
to have no unit. Providing an empty array always returns null
. Examples:
joint.util.parseCssNumeric(1.1, ''); // => { value: 1.1, unit: '' })
joint.util.parseCssNumeric('1.1', ''); // => { value: 1.1, unit: '' })
joint.util.parseCssNumeric('1.1px', ''); // => null
joint.util.parseCssNumeric('1.1px', 'px'); // => { value: 1.1, unit: 'px' });
joint.util.parseCssNumeric('1.1px', 'em'); // => null
joint.util.parseCssNumeric(1.1, []); // => null
joint.util.parseCssNumeric('1.1', []); // => null
joint.util.parseCssNumeric('1.1px', []); // => null
joint.util.parseCssNumeric(1.1, ['']); // => { value: 1.1, unit: '' })
joint.util.parseCssNumeric('1.1', ['']); // => { value: 1.1, unit: '' })
joint.util.parseCssNumeric('1.1px', ['px']); // => { value: 1.1, unit: 'px' });
joint.util.parseCssNumeric('1.1px', ['em']); // => null
joint.util.parseCssNumeric(1.1, ['', 'px']); // => { value: 1.1, unit: '' })
joint.util.parseCssNumeric('1.1', ['', 'px']); // => { value: 1.1, unit: '' })
joint.util.parseCssNumeric('1.1px', ['', 'px']); // => { value: 1.1, unit: 'px' })
joint.util.parseCssNumeric('1.1em', ['', 'px']); // => null
util.pick(object, [...paths])
Creates an object composed of the picked object properties.
object | Object | The source object |
---|---|---|
[paths] | string[]|string[][] | The property paths to pick |
util.result(object, path, [defaultValue])
Gets the value at path of object. If the resolved value is undefined, the defaultValue is returned in its place. If the resolved value is a function it's invoked with the this binding of its parent object and its result is returned.
object | Object | The source object |
---|---|---|
path | string|string[] | The path of the property to get |
[defaultValue] | any | The value returned for undefined resolved values |
util.sanitizeHTML(html)
Sanitize the provided HTML (string) to protect against XSS attacks. The algorithm has several steps:
<div>
tag. This will remove tags that are invalid in that context (e.g. <body>
and <head>
).<script>
tags.on...
attributes (e.g. onload
, onerror
).javascript:
pseudo-protocol as value.The six simple steps protect against the most common XSS attacks; however, we cannot guarantee bulletproof security here. If you need stronger security, you should always keep an eye on a list XSS attacks and replace the joint.util.sanitizeHTML()
function with your own, more secure version.
Examples:
joint.util.sanitizeHTML('<html><body><p>Hello</p></body></html>'); // => '<p>Hello</p>'
joint.util.sanitizeHTML('<p>Hello</p><script>alert("Hacked");</script>'); // => '<p>Hello</p>'
joint.util.sanitizeHTML('<p>Hello</p><img onload="alert("Hacked");">'); // => '<p>Hello</p><img>'
joint.util.sanitizeHTML('<p>Hello</p><img src="javascript:alert("Hacked");">'); // => '<p>Hello</p><img>'
util.setAttributesBySelector(el, attrs)
Set attributes on the DOM element (SVG or HTML) el
and its descendants based on the selector in the attrs
object. The attrs
object is of the form: { [selector]: [attributes }
. For example:
var myEl = document.querySelector('.mydiv');
joint.util.setAttributesBySelector(myEl, {
'.myDiv': { 'data-foo': 'bar' }, // Note the reference to the myEl element itself.
'input': { 'value': 'my value' }
});
The code above sets 'data-foo'
attribute of the myEl
element to the value 'bar'
and 'value'
attribute of all the descendant inputs to the value 'my value'
. This is a convenient way of setting attributes on elements (including themselves) and their descendants. Note that 'class'
attribute is treated as a special case and its value does not override the previous value of the 'class'
attribute. Instead, it adds this new class to the list of classes for the element referenced in the selector.
util.setByPath(object, path, value, delim)
Set a value
at the path
in a nested object
. delim
is the
delimiter used in the path
. Returns the augmented object
.
joint.util.setByPath({ a: 1 }, 'b/bb/bbb', 2, '/');
/*
{
"a": 1,
"b": {
"bb": {
"bbb": 2
}
}
}
*/
util.sortBy(collection, [iteratees])
Creates an array of elements, sorted in ascending order by the results of running each element in a collection thru each iteratee. This method performs a stable sort, that is, it preserves the original sort order of equal elements. The iteratees are invoked with one argument: (value).
collection | any[]|Object | The collection to iterate over |
---|---|---|
[iteratees] | Funtion|Funtion[] | The iteratees to sort by |
util.sortedIndex(array, value, [iteratee])
Uses a binary search to determine the lowest index at which value should be inserted into array in order to maintain its sort order. In addition this method accepts iteratee which is invoked for value and each element of array to compute their sort ranking. The iteratee is invoked with one argument: (value)
array | any[] | The sorted array to inspect |
---|---|---|
value | any | The value to evaluate |
[iteratee] | Function | The iteratee invoked per element |
util.sortElements(elements, comparator)
Change the order of elements
(a collection of HTML elements or a selector) in the DOM
according to the comparator(elementA, elementB)
function. The comparator
function has
the exact same meaning as in Array.prototype.sort(comparator)
.
The function returns the sorted array of elements.
util.supplement(destinationObject [...sourceObjects])
(Deprecated) An alias for util.defaults
, use it instead.
svg(strings)
This is the tagged template which converts a string into a markup object while declaring a Cell. The resulting object contains fields which are described in this part of the documentation. SVG is converted into JSON markup as follows:
Property | Representation |
---|---|
tagName | SVG tag name. |
selector | @selector attribute. |
groupSelector | @group-selector attribute. Accepts comma-separated lists e.g. @group-selector="group1, group2" . |
namespaceURI | The namespace URI of the element. It defaults to the SVG namespace "http://www.w3.org/2000/svg" . |
attributes | Attributes of the element. |
style | The style attribute of the element parsed as key-value pairs. |
className | The class attribute of the element. |
children | The children of the element. |
<span>a<span>b</span></span>
is converted to { tagName: 'span', children: ['a', { tagName: 'span', children: ['b']}]}
.
util.template(html)
Return a template function that returns pre-compiled html
when called.
util.toArray(value)
Converts value to an array.
value | any | The value to convert |
---|
util.toggleFullScreen([element])
Make (or cancel) an element
to be displayed full-screen. The default element is window.top.document.body
.
joint.util.toggleFullScreen(paper.el);
util.toKebabCase(str)
Convert the string to kebab-case. Example:
joint.util.toKebabCase('strokeWidth'); // => 'stroke-width'
util.union([...arrays])
Creates an array of unique values, in order, from all given arrays using SameValueZero for equality comparisons.
[arrays] | any[] | The arrays to inspect |
---|
util.uniq(array, [iteratee])
Creates a duplicate-free version of an array, using SameValueZero for equality comparisons, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the array. In addition this method accepts iteratee which is invoked for each element in array to generate the criterion by which uniqueness is computed. The order of result values is determined by the order they occur in the array. The iteratee is invoked with one argument: (value).
array | any[] | The array to inspect |
---|---|---|
[iteratee] | Function | The iteratee invoked per element |
util.uniqueId([prefix])
Generates a unique ID. If prefix is given, the ID is appended to it.
[prefix] | string | The prefix of the ID |
---|
util.unsetByPath(object, path, delim)
Unset (delete) a property at the path
in a nested object
. delim
is the
delimiter used in the path
. Returns the augmented object
.
joint.util.unsetByPath({ a: { aa: { aaa: 3 } } }, 'a/aa/aaa', '/');
// { a: { aa: {} } }
util.without(array, [...values])
Creates an array excluding all given values using SameValueZero for equality comparisons.
array | any[] | The array to inspect |
---|---|---|
[values] | any[] | The values to exclude |
util.format.number(specifier, value)
Format number value
according to the specifier
defined via the Python Format Specification Mini-language.
joint.util.format.number('.2f', 5) // 5.00
joint.util.format.number('03d', 5) // 005
joint.util.format.number('.1%', .205) // 20.5%
joint.util.format.number('*^9', 5) // ****5****