Joint API

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.

anchors

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:

Example:

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.

anchors.bottom

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%'
        }
    }
});

anchors.bottomLeft

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%'
        }
    }
});

anchors.bottomRight

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%'
        }
    }
});

anchors.center

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%'
        }
    }
});

anchors.custom

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).

anchors.left

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%'
        }
    }
});

anchors.midSide

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
        }
    }
});

anchors.modelCenter

The 'modelCenter' anchor function places the anchor of the link at center of the model bbox.

Example:

link.source(model, {
    anchor: {
        name: 'modelCenter'
    }
});

anchors.perpendicular

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
        }
    }
});

anchors.right

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%'
        }
    }
});

anchors.top

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%'
        }
    }
});

anchors.topLeft

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%'
        }
    }
});

anchors.topRight

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%'
        }
    }
});

connectionPoints

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:

Example:

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.

connectionPoints.anchor

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:
  • from the anchor point by x pixels in the direction of the corresponding link end's path segment.
  • by y pixels in the direction of the corresponding link end's path segment, rotated by 90 degrees anti-clockwise around the anchor point.
If a number is provided, it will be used as the 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:
  • The reference point lies below the anchor point. The anchor point is the topmost point of the two, so the connection point receives both the `x` and `y` coordinates from the anchor point. The connection point is set to be coincident with the anchor point.
  • The reference point lies above the anchor point. In this case, the reference point is the topmost point of the two, so the connection point receives the `y` coordinate from the reference point. The connection point still receives the `x` coordinate from the anchor point. This means that the connection point is offset from the anchor point in the `'top'` direction. The three points create a right-angled triangle, with the right angle at the connection point.
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
        }
    }
});

connectionPoints.bbox

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:
  • from the anchor point by x pixels in the direction of the corresponding link end's path segment.
  • by y pixels in the direction of the corresponding link end's path segment, rotated by 90 degrees anti-clockwise around the anchor point.
If a number is provided, it will be used as the 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
        }
    }
});

connectionPoints.boundary

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:
  • from the anchor point by x pixels in the direction of the corresponding link end's path segment.
  • by y pixels in the direction of the corresponding link end's path segment, rotated by 90 degrees anti-clockwise around the anchor point.
If a number is provided, it will be used as the 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
        }
    }
});

connectionPoints.custom

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).

connectionPoints.rectangle

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:
  • from the anchor point by x pixels in the direction of the corresponding link end's path segment.
  • by y pixels in the direction of the corresponding link end's path segment, rotated by 90 degrees anti-clockwise around the anchor point.
If a number is provided, it will be used as the 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
        }
    }
});

connectionStrategies

Connection strategies come into play when the user modifies the position of link endpoints. There are two situations in which this is relevant:

  • When the user drags a link endpoint and connects it to an element or its port. The connection strategy determines the end anchor after the user is finished dragging the link endpoint. (Note that any individual 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.)
  • When a user creates a link, for example by clicking a port. The connection strategy determines the new link's source anchor.

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:

The default connection strategy is specified as null in paper settings, which is equivalent to joint.connectionStrategies.useDefaults.

Built-in connection strategies are specified by reference to their their name in the joint.connectionStrategies namespace. Example:

paper.options.connectionStrategy = joint.connectionStrategies.pinAbsolute;

connectionStrategies.custom

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.

Connecting to Ancestors

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;

Connecting to Ports

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;

Replicating Built-in Anchor Functions

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;

Replicating Built-in Connection Point Functions

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.

connectionStrategies.pinAbsolute

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)
    }
};

connectionStrategies.pinRelative

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)
    }
};

connectionStrategies.useDefaults

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

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:

Example:

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');

connectors.custom

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;
});

connectors.jumpover

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'
});

connectors.normal

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');

connectors.rounded

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
});

connectors.smooth

The 'smooth' connector interpolates route points using a cubic bezier curve.

Example:

link.connector('smooth');

connectors.straight

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:
'point' Route segments meet at corner point with no special behavior.
'cubic' A cubic curve segment is inserted to smooth the corner. The curve radius of the smoothed corner is determined by cornerRadius argument.
'line' A straight line segment is inserted at the corner to form a bevel. The distance of the two endpoints of the bevel from the corner point is determined by cornerRadius argument.
'gap' A gap is created at the corner. The distance of the two endpoints of the gap from the corner point is determined by the cornerRadius argument.
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');

connectors.curve

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
});

connectors.curve.Directions

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.

connectors.curve.TangentDirections

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.

dia.attributes

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.

calc()

The calc() function lets you perform calculations when specifying SVG attributes values.

Syntax:

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:

  • Variable
    'calc(w)'
  • Variable Addition or Subtraction
    'calc(w + 5)'
    'calc(h - 10)'
  • Multiplication Variable
    'calc(2 * w)'
    'calc(0.5 * h)'
  • Variable Division
    'calc(w / 2)'
    'calc(h / 3)'
  • Variable Division
    'calc(w / 2)'
    'calc(h / 3)'
  • Multiplication Variable Addition or Subtraction
    'calc(2 * w + 5)'
    'calc(0.5 * h - 10)'
  • Variable Division Addition or Subtraction
    '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.

  • Multiplication is an optional floating number factor of the variable. It's a number followed by the * symbol.
    1.5 *
  • Division is an optional floating number divisor of the variable. It's the / symbol followed by a number.
    / 2
  • Addition or Subtraction is an optional floating number added or subtracted from the variable. It's a + or - symbol followed by a number.
    + 5
Notes:
  • Expression is case-sensitive.
  • The +, - and * operators do not require whitespace.
  • No extra parentheses are allowed.
  • It is permitted to nest 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:

Examples:
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.

dia.attributes.atConnectionLengthIgnoreGradient

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' });

dia.attributes.atConnectionLengthKeepGradient

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' });

dia.attributes.atConnectionRatioIgnoreGradient

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' });

dia.attributes.atConnectionRatioKeepGradient

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' });

dia.attributes.connection

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 }});

dia.attributes.containerSelector

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:

  • embedding highlighter

// `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');

dia.attributes.displayEmpty

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.

dia.attributes.event

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();
  }
});

dia.attributes.fill

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.

Gradient

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' }
    ]
});

Pattern

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'
        }
    }]
});

dia.attributes.filter

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
    }
});

dia.attributes.highlighterSelector

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:

  • magnet highlight

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');

dia.attributes.magnet

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'
    }]
};

dia.attributes.magnetSelector

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:

  • link anchor
  • link connection point
  • connector
  • router

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');

dia.attributes.port

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.

dia.attributes.props

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 });

dia.attributes.ref

Markup or CSS selector pointing to an element that is used as a reference for relative positioning attributes.

dia.attributes.refCx

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>.

dia.attributes.refCy

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>.

dia.attributes.refDKeepOffset

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');`

dia.attributes.refDResetOffset

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');`

dia.attributes.refDx

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.

dia.attributes.refDy

Make y-coordinate of the subelement relative to the bottom edge of the element referenced to by the selector in ref attribute.

dia.attributes.refHeight

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>.

dia.attributes.refHeight2

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

dia.attributes.refPointsKeepOffset

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');`

dia.attributes.refPointsResetOffset

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');`

dia.attributes.refRCircumscribed

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>.

dia.attributes.refRInscribed

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>.

dia.attributes.refRx

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>.

dia.attributes.refRy

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>.

dia.attributes.refWidth

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>.

dia.attributes.refWidth2

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

dia.attributes.refX

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.

dia.attributes.refX2

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.

dia.attributes.refY

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.

dia.attributes.refY2

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.

dia.attributes.resetOffset

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
  }
});

dia.attributes.sourceMarker

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
});

dia.attributes.stroke

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.

dia.attributes.style

An object containing CSS styles for a subelement.

dia.attributes.targetMarker

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).

dia.attributes.text

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.

dia.attributes.textPath

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.

dia.attributes.textVerticalAnchor

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.

dia.attributes.textWrap

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.

  • If not provided, the text is wrapped to fit the reference bounding box
  • It can be a calc() expression (e.g. 'calc(w - 10)')
  • Percentage expression (e.g. '50%' is half the width or height)
  • A positive number is an absolute dimension (e.g 50 fits the text within 50 pixels)
  • A negative number is a relative dimension (e.g. -10 is an equivalent to 'calc(w - 10)' resp. 'calc(h - 10)')
  • Use null to disable wrapping in a given dimension

If 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.

dia.attributes.title

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');

dia.attributes.vertexMarker

Valid for <path> subelements. The same as sourceMarker but draws SVG elements at every vertex of the path.

dia.attributes.xAlignment

alias: x-alignment

If set to 'middle', the subelement will be centered around its new x-coordinate.

dia.attributes.yAlignment

alias: y-alignment

If set to 'middle', the subelement will be centered around its new y-coordinate.

dia.Cell

The basic model for diagram cells. It's an mvc.Model with a few additional properties and methods. Most importantly, every cell has a unique ID that is stored in the id property.

dia.Cell.constructor

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 

dia.Cell.define

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);

dia.Cell.markup

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

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 using tagged template

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>`
JSON Markup attributes

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
        }
    }]
});
Setting attributes with selectors
Here are simple rules how SVG attributes are set on the nodes when one uses combination of 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' }
});

XML String

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 defining Cell markup with XML strings is slower than defining it with JSON arrays, precisely because of the need for parsing. We strongly recommend you to use the JSON markup for your Cell definitions.

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
    });

dia.Cell.prototype.getParentCell
cell.getParentCell()

Return the parent cell of cell or null if there is none.

dia.Cell.prototype.isElement
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.
}
dia.Cell.prototype.parent
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.

dia.Cell.prototype.z
cell.z()

Return z – the stacking order (an equivalent to HTML z-index).

When z is undefined or null, function returns 0.

dia.CellView

The view for the joint.dia.Cell model. It inherits from mvc.View and is responsible for:

  • Rendering a cell inside of a paper
  • Handling the cell's pointer events
  • Provides various methods for working with the cell (visually)

To find the view associated with a specific cell (model), use the findViewByModel method of the paper. For example:

var cellView = paper.findViewByModel(cell);

dia.CellView.custom

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.

Methods and terminology

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.

Usage
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;
    }
});
dia.CellView.prototype.findAttribute
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.

dia.CellView.prototype.findNode
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.

dia.CellView.prototype.findNodes
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.

dia.CellView.prototype.highlight
cellView.highlight([el[, options]])

Add a highlighter to the cell view.

When the method is called, the cell:highlight paper event is triggered.

For greater flexibility use highlighters API instead.

Arguments:

  • el - if not provided, then the cell view's el will be used
  • options:
    • highlighter:
      • name - the name of the highlighter (see highlighters)
      • options - an options object that will be passed directly to the highlighter specified by name
    • type - the type of highlighting
dia.CellView.prototype.isDefaultInteractionPrevented
cellView.isDefaultInteractionPrevented(event)

Returns whether preventDefaultInteraction was ever called on this event object.

dia.CellView.prototype.preventDefaultInteraction
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
dia.CellView.prototype.unhighlight
cellView.unhighlight([el[, options]])

Removes a highlighter added to the cell view.

When the method is called, the cell:unhighlight paper event is triggered.

It is important to note that if you highlighted a cell using custom options, you must provide those exact same options when using the unhighlight method.

To avoid this and for greater flexibility use highlighters API instead.

dia.Element

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:

Geometry

  • 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.

Presentation

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.

Styling

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

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.

Z

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.)

Ports

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.

Nesting

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.

Events

Elements trigger several special events, detailed in the element events documentation.

Custom Element

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.

dia.Element.events

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 });

dia.Element.ports

Ports

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.

Port API on joint.dia.Element
Port configuration
// 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 <circle class="joint-port-body" r="10" fill="#FFFFFF" stroke="#000000"/>.

The root of the port markup is referenced by the portRoot selector.

If the markup contains more than one node, an extra group is created to wrap the nodes. This group becomes the new portRoot.

// An example of port markup
markup: [{
    tagName: 'rect',
    selector: 'bodyInner',
    className: 'outer',
    attributes: {
        'width': 15,
        'height': 15,
        'fill': 'red'
    }
}, {
    tagName: 'rect',
    selector: 'bodyOuter',
    className: 'inner',
    attributes: {
        'width': 15,
        'height': 15,
        'fill': 'blue',
        'x': 10
    }
}]
label object Port label layout configuration. E.g. label position, label markup. More information about port label layouts can be found in layout.PortLabel.
  • label.position
string | object

Port label position configuration. It can be a string for setting the port layout type directly with default settings, or an object where it's possible to set the layout type and options.

{ position: 'left'}

// or ...
{
    position: {
        name: 'left',
        args: {
            dx: 10
        }
    }
}
  • label.position.name
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.
  • label.position.args
object Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the layout.PortLabel section.
  • label.markup
MarkupJSON | string

A custom port label markup.

The default port label markup is <text class="joint-port-label" fill="#000000"/>.

The root of the label markup is referenced by the labelRoot selector.

If the markup contains more than one node, an extra group is created to wrap the nodes. This group becomes the new labelRoot.

All <text/> nodes of the port are referenced by the labelText selector, unless the markup contains labelText explicitly.

Use an empty array [] to prevent the label from being rendered.

z number | string

An alternative to HTML z-index. z sets the position of a port in the list of DOM elements within an ElementView.

Shapes most likely consist of 1 or more DOM elements, <rect/>, <rect/><text/><circle/> etc. Ports are placed into the main group element elementView.el, so it will act as the port container. Ports with z: 'auto' are located right after the last element in the main group. Ports with z defined as a number are placed before a DOM element at the position (index within the children of the container, where only the original markup elements, and ports with z: 'auto' are taken into account) equal to z.

For instance, the first shape from the demo above with the following markup...

markup: [{
    tagName: 'rect',
    selector: 'bodyMain',
    className: 'bodyMain'
}, {
    tagName: 'rect',
    selector: 'bodyInner',
    className: 'bodyInner'
}, {
    tagName: 'text',
    selector: 'label',
    className: 'label'
}]

...will be rendered like this:

<g model-id="...">
    <g class="joint-port"></g>         <!-- z: 0 -->
    <rect class="bodyMain"></rect>
    <g class="joint-port"></g>         <!-- z: 1 -->
    <rect class="bodyInner"></rect>
    <text class="label"></text>
    <g class="joint-port"></g>         <!-- z: 3 -->
    <g class="joint-port"></g>         <!-- z: auto -->
</g>

Ports will be placed in the rotatable group if it's defined in the shape's markup. Ports with z: 'auto' are located right after the last element in the rotatable group. In the demo above, the second shape is defined with a rotatable group and the following markup:

markup: [{
    tagName: 'g',
    selector: 'rotatable',
    className: 'rotatable',
    children: [{
        tagName: 'g',
        selector: 'scalable',
        className: 'scalable',
        children: [{
            tagName: 'rect',
            selector: 'bodyMain',
            className: 'bodyMain'
        }]
    }, {
        tagName: 'rect',
        selector: 'bodyInner',
        className: 'bodyInner'
    }, {
        tagName: 'text',
        selector: 'label',
        className: 'label'
    }]
}]

It will be rendered like this:

<g model-id="...">
    <g class="rotatable">
        <g class="joint-port"></g>         <!-- z: 0 -->
        <g class="scalable"><rect class="bodyMain"></rect></g>
        <g class="joint-port"></g>         <!-- z: 1 -->
        <rect class="bodyInner"></rect>
        <text class="label"></text>
        <g class="joint-port"></g>         <!-- z: 3 -->
        <g class="joint-port"></g>         <!-- z: auto -->
    </g>
</g>

All properties described above are optional, and everything has its own default. E.g. element.addPorts([{}, {}]) will add 2 ports with default settings.

Port groups configuration

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.
  • position.name
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.
  • position.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

Custom port markup. Multiple roots are not allowed. Valid notation would be:

markup: [{
    tagName: 'g',
    children: [{
        tagName: 'rect',
        selector: 'bodyInner',
        className: 'outer',
        attributes: {
            'width': 15,
            'height': 15,
            'fill': 'red'
        }
    }, {
        tagName: 'rect',
        selector: 'bodyOuter',
        className: 'inner',
        attributes: {
            'width': 15,
            'height': 15,
            'fill': 'blue',
            'x': 10
        }
    }]
}]

The default port looks like the following: <circle class="joint-port-body" r="10" fill="#FFFFFF" stroke="#000000"/>.

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.
  • label.position
string | object

Port label position configuration. It can be a string for setting the port layout type directly with default settings, or an object where it's possible to set the layout type and options.

{ position: 'left'}

// or ...
{
    position: {
        name: 'left',
        args: {
            dx: 10
        }
    }
}
  • label.position.name
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.
  • label.position.args
object Additional arguments for the layout method. Available properties depend on the layout type. More information can be found in the layout.PortLabel section.
  • label.markup
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"/>.
Custom markup

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'
        }
    }]
    // ...
});
dia.Element.prototype.addPort
element.addPort(port, [opt])

Add a single port, where port could be defined as described in section Port interface

dia.Element.prototype.addPorts
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

dia.Element.prototype.addTo
element.addTo(graph)

Add the element to the graph (an instance of joint.dia.Graph). This is equivalent to calling graph.addCell(element).

dia.Element.prototype.angle
element.angle()

Return the rotation of the element in degrees (0° - 360°).

dia.Element.prototype.attr
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' }
});
The method is not suitable for storing custom data on the model. For that, use 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;
dia.Element.prototype.clone
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.

dia.Element.prototype.embed
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 });
dia.Element.prototype.findView
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)

dia.Element.prototype.fitEmbeds
element.fitEmbeds([opt])

An alias for the element.fitToChildren function.

dia.Element.prototype.fitParent
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 true, the algorithm is only ever allowed to shrink the bounding box of the embedding parent element, never to expand it. You can visualize this setting as the algorithm dragging the top-left and bottom-right corners of the parent's bounding box inward until only its children (including this element) are within the bounding box.

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 outside the parent), then this function does nothing (since satisfying the shrink-only restriction is impossible).

deep boolean

If true, this algorithm is applied recursively on the embedding parent element of this element's embedding parent element, and so on. The bounding box algorithm is evaluated in reverse-depth order - starting from this element, then going up (i.e. to the topmost embedding ancestor element - or opt.terminator), to make sure that any paddings applied on lower-level elements are taken into account by higher-level elements.

Note that if this option is used in conjunction with opt.shrinkOnly, the algorithm is still applied strictly recursively (i.e. one level at a time). Therefore, even if this element lies completely within the current bounding box of its grandfather element, the grandfather element's calculated bounding box will shrink only based on this element's parent - which may mean that this element will end up outside of the calculated bounding box of the grandfather element.

terminator Cell | Cell.ID

If opt.deep is true and a Cell reference or a Cell ID is provided as a value of this option, then the specified element is the last one for which the bounding box algorithm is applied during recursion - it is the last element whose bounding box is resized and repositioned based on the bounding boxes of its children.

Handling of edge cases:

  • If the provided value refers to this element, then nothing happens.
  • If the provided value refers to this element's embedding parent element, then the result is the same as calling the function without opt.deep and opt.terminator.
  • If the provided value does not refer to an ancestor of this element, then the result is the same as calling the function without opt.terminator.
  • If the provided value does not refer to a Cell of the Element type, then the result is the same as calling the function without opt.terminator.
dia.Element.prototype.fitToChildren
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 true, the algorithm is only ever allowed to shrink the bounding box of this element, never to expand it. You can visualize this setting as the algorithm dragging the top-left and bottom-right corners of this element's bounding box inward until only its embedded child elements are within the bounding box.

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 outside of this element), then this function does nothing (since satisfying the shrink-only restriction is impossible).

deep boolean

If true, this algorithm is applied recursively on all embedded children of this element which have embedded children of their own, and so on. The bounding box algorithm is evaluated in reverse-depth order - starting from the deepest descendant, then going up (i.e. to this element). This ensures that any paddings applied on lower-level elements are taken into account by higher-level elements.

Note that if this option is used in conjunction with opt.shrinkOnly, the algorithm is still applied strictly recursively - one level at a time. Therefore, even if this element's current bounding box completely overlaps one of its grandchild elements, this element's calculated bounding box will shrink only based on the grandchild's parent (this element's child), which may mean that the grandchild element will end up outside of the calculated bounding box of this element.

dia.Element.prototype.getAbsolutePointFromRelative
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.

dia.Element.prototype.getAncestors
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.

dia.Element.prototype.getBBox
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.
dia.Element.prototype.getEmbeddedCells
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.

dia.Element.prototype.getGroupPorts
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.
dia.Element.prototype.getPort
element.getPort(id)

Returns the port specified by an id. If such a port does not exist the method returns undefined.

dia.Element.prototype.getPortIndex
element.getPortIndex(portId)
Returns the port index in the array of port items.
dia.Element.prototype.getPorts
element.getPorts()

Returns an array of all ports on the element. If there is no port an empty array is returned.

dia.Element.prototype.getPortsPositions
element.getPortsPositions(groupName)

Returns the positions and the angle of all ports in the group, relatively to the element position.

dia.Element.prototype.getRelativePointFromAbsolute
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.

dia.Element.prototype.getTransitions
element.getTransitions()

Return an array of all active transitions (their paths).

dia.Element.prototype.hasPort
element.hasPort(id)

Check if an element contains a port with id. Returns boolean.

dia.Element.prototype.hasPorts
element.hasPorts()

Check if an element has any ports defined. Returns boolean.

dia.Element.prototype.insertPort
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.

dia.Element.prototype.isElement
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.
}
dia.Element.prototype.isEmbeddedIn
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.

dia.Element.prototype.portProp
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' }
dia.Element.prototype.position
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:

myElement.position(x, y, { restrictedArea: myElement.getParentCell().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).

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
dia.Element.prototype.prop
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 });
dia.Element.prototype.remove
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.

dia.Element.prototype.removeAttr
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.

dia.Element.prototype.removePort
element.removePort(port, [opt])

Remove a port from an element, where port is a port object, or portId.

dia.Element.prototype.removePorts
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.

dia.Element.prototype.resize
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.

dia.Element.prototype.rotate
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.

dia.Element.prototype.scale
element.scale(sx, sy, origin[, opt])

Scales the element's position and size relative to the given origin.

dia.Element.prototype.stopTransitions
element.stopTransitions([path])

Stops all running transitions. If parameter path is provided, it will stop only transitions specified by this path.

dia.Element.prototype.toBack
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).

dia.Element.prototype.toFront
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 });
dia.Element.prototype.toJSON
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().

dia.Element.prototype.transition
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.
dia.Element.prototype.translate
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).

dia.Element.prototype.unembed
element.unembed(cell, [opt])
element.unembed(cells, [opt])

Free up an embedded cell or an array of cells from its parent element.

dia.ElementView

The view for the joint.dia.Element model. It inherits from joint.dia.CellView and is responsible for:

  • Rendering an element inside of a paper
  • Handling the element's pointer events
  • Provides various methods for working with the element (visually)

To find the view associated with a specific element (model), use the findViewByModel method of the paper.

var elementView = paper.findViewByModel(element);
dia.ElementView.prototype.addTools
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();
});
dia.ElementView.prototype.findPortNode
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.

dia.ElementView.prototype.findPortNodes
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.

dia.ElementView.prototype.getBBox
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.

dia.ElementView.prototype.getNodeBBox
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.

dia.ElementView.prototype.getNodeUnrotatedBBox
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.

dia.ElementView.prototype.hasTools
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.

dia.ElementView.prototype.hideTools
elementView.hideTools()

Hide all tools attached to this element view.

dia.ElementView.prototype.removeTools
elementView.removeTools()

Remove the tools view attached to this element view.

dia.ElementView.prototype.showTools
elementView.showTools()

Show all tools attached to this element view.

dia.Graph.constructor

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.

dia.Graph.events

The following list contains events that you can react on:

  • change - generic event triggered for any change in the graph
  • add - triggered when a new cell is added to the graph
  • remove - triggered when a cell is removed from the graph
  • Moreover, all the events that are triggered on elements or links are propagated to the graph as well.
graph.on('add', function(cell) {
    alert('New cell with id ' + cell.id + ' added to the graph.')
})

dia.Graph.JSON

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
        }
    ]
}
dia.Graph.prototype.addCell
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);
dia.Graph.prototype.addCells
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.

dia.Graph.prototype.bfs
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:

graph BFS algorithm

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.

dia.Graph.prototype.clear
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.

dia.Graph.prototype.cloneCells
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).

dia.Graph.prototype.cloneSubgraph
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] }.

dia.Graph.prototype.dfs
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:

graph DFS algorithm

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.

dia.Graph.prototype.findModelsFromPoint
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.

dia.Graph.prototype.findModelsInArea
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.

dia.Graph.prototype.findModelsUnderElement
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'.

dia.Graph.prototype.fromJSON
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)
dia.Graph.prototype.getBBox
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);
dia.Graph.prototype.getCell
graph.getCell(id)

Get a cell from the graph by its id.

dia.Graph.prototype.getCells
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).

dia.Graph.prototype.getCellsBBox
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 });
dia.Graph.prototype.getCommonAncestor
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.

dia.Graph.prototype.getElements
graph.getElements()

Return an array of all elements in the graph. The elements are sorted by their z index (the smallest z being first).

dia.Graph.prototype.getFirstCell
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).

dia.Graph.prototype.getLastCell
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).

dia.Graph.prototype.getNeighbors
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 flow of directed links at link-link connections.
dia.Graph.prototype.getPredecessors
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).

dia.Graph.prototype.getSinks
graph.getSinks()

Return an array of all the leafs of the graph. Time complexity: O(|V|).

dia.Graph.prototype.getSources
graph.getSources()

Return an array of all the roots of the graph. Time complexity: O(|V|).

dia.Graph.prototype.getSubgraph
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.

dia.Graph.prototype.getSuccessors
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.

getSuccessors diagram no embedding

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.

getSuccessors diagram with embedding
dia.Graph.prototype.isNeighbor
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 behind the 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 of the 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 flow of directed links at link-link connections.
dia.Graph.prototype.isPredecessor
graph.isPredecessor(elementA, elementB)

Return true if elementB is a predecessor of elementA.

dia.Graph.prototype.isSink
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).

dia.Graph.prototype.isSource
graph.isSource(element)

Return true if element is a root, i.e. there is no link that targets the element. Time complexity: O(1).

dia.Graph.prototype.isSuccessor
graph.isSuccessor(elementA, elementB)

Return true if elementB is a successor of elementA.

dia.Graph.prototype.maxZIndex
graph.maxZIndex()

Get the highest Z value in the graph (the value of the cell on front).

dia.Graph.prototype.minZIndex
graph.minZIndex()

Get the lowest Z value in the graph (the value of the cell on the back).

dia.Graph.prototype.removeCells
graph.removeCells(cells[, opt])
graph.removeCells(cell, cell, ..[, opt])

Removes the given cells from the graph.

dia.Graph.prototype.resetCells
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.

dia.Graph.prototype.search
graph.search(element, iteratee [, opt])

Traverse the graph starting at element following links. This function is a wrapper around dfs() or bfs(). By default, it uses dfs(). If opt.breadthFirst is set to true, bfs() will be used instead.

dia.Graph.prototype.toJSON
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());
dia.Graph.prototype.translate
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.

dia.HighlighterView

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.

dia.HighlighterView.add

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().

  • cellView is an instance of dia.CellView.
  • selector points to an SVGElement of the cellView. It must be one of the following types:
    Type Description Example
    String A selector from the cell's markup.
    'root'
    'body'
    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.

    { selector: 'body' } // cells
    { port: 'port1', selector: 'portBody' } // elements
    { label: 1, selector: 'labelBody' } // links
    SVGElement An SVGElement, the descendant of cellView.el or the root itself.
    cellView.el // the root
    cellView.el.querySelector('rect') // an SVGRectangle
  • id is a unique identifier of the highlighter. Only single highlighter with given id can exist on the CellView at one time.
  • options passed to the constructor of the highlighter view.

dia.HighlighterView.get

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));

dia.HighlighterView.remove

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.

dia.HighlighterView.removeAll

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.

dia.HighlighterView.prototype.highlight
highlighter.highlight(cellView, node)

Mark/Emphasize the node (SVGElement) of the cellView. e.g. render a rectangle above the node, add a class to the node.

dia.HighlighterView.prototype.MOUNTABLE
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');
    }

});
dia.HighlighterView.prototype.unhighlight
highlighter.unhighlight(cellView, node)

Remove marks made by highlight() from the node (SVGElement) of the cellView.

dia.HighlighterView.prototype.UPDATABLE
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');
    }

});
dia.HighlighterView.prototype.UPDATE_ATTRIBUTES
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()
dia.HighlighterView.prototype.options.layer

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.
dia.HighlighterView.prototype.options.z

z - the stacking order (z-index) of the highlighter in the given stacking context. Applicable for mountable highlighters only.

dia.LinkView

The view for the joint.dia.Link model. It inherits from joint.dia.CellView and is responsible for:

  • Rendering a link inside a paper
  • Handling the link's pointer events
  • Providing various methods for working with the link (visually)

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

Custom LinkView

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,
});
dia.LinkView.prototype.addLabel
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
});
dia.LinkView.prototype.addTools
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.

dia.LinkView.prototype.addVertex
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
});
dia.LinkView.prototype.findLabelNode
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.

dia.LinkView.prototype.findLabelNodes
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.

dia.LinkView.prototype.getBBox
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.

dia.LinkView.prototype.getClosestPoint
linkView.getClosestPoint(point)

Return the point on the connection that lies closest to point.

dia.LinkView.prototype.getClosestPointLength
linkView.getClosestPointLength(point)

Return the length of the connection up to the point that lies closest to point.

dia.LinkView.prototype.getClosestPointRatio
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).

dia.LinkView.prototype.getConnection
linkView.getConnection()

Return a geometric representation of the connection (instance of g.Path).

dia.LinkView.prototype.getConnectionLength
linkView.getConnectionLength()

Return a cached total length of the connection in pixels.

dia.LinkView.prototype.getConnectionSubdivisions
linkView.getConnectionSubdivisions()

Return a cached array of segment subdivision arrays of the connection. (See g.Path.prototype.getSegmentSubdivisons() documentation for more information.)

dia.LinkView.prototype.getLabelCoordinates
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.

dia.LinkView.prototype.getLabelPosition
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
});
dia.LinkView.prototype.getNodeBBox
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.

dia.LinkView.prototype.getNodeUnrotatedBBox
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.

dia.LinkView.prototype.getPointAtLength
linkView.getPointAtLength(length)

Return the point on the path that lies length away from the beginning of the connection.

dia.LinkView.prototype.getPointAtRatio
linkView.getPointAtRatio(ratio)

Return the point on the path that lies ratio (normalized length) away from the beginning of the connection.

dia.LinkView.prototype.getSerializedConnection
linkView.getSerializedConnection()

Return a cached path data of the connection (the value of the 'd' SVGPathElement attribute).

dia.LinkView.prototype.getTangentAtLength
linkView.getTangentAtLength(length)

Return a line tangent to the path at point that lies length away from the beginning of the connection.

dia.LinkView.prototype.getTangentAtRatio
linkView.getTangentAtRatio(ratio)

Return a line tangent to the path at point that lies ratio (normalized length) away from the beginning of the connection.

dia.LinkView.prototype.getVertexIndex
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.

dia.LinkView.prototype.hasTools
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.

dia.LinkView.prototype.hideTools
linkView.hideTools()

Hide all tools attached to this link view.

dia.LinkView.prototype.removeRedundantLinearVertices
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.)

dia.LinkView.prototype.removeTools
linkView.removeTools()

Remove the tools view attached to this link view.

dia.LinkView.prototype.requestConnectionUpdate
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.

dia.LinkView.prototype.sendToken
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.

dia.LinkView.prototype.showTools
linkView.showTools()

Show all tools attached to this link view.

dia.LinkView.prototype.sourceAnchor
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.

dia.LinkView.prototype.sourceBBox
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).

dia.LinkView.prototype.sourcePoint
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.

dia.LinkView.prototype.targetAnchor
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.

dia.LinkView.prototype.targetBBox
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).

dia.LinkView.prototype.targetPoint
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.

dia.Paper.constructor

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.

dia.Paper.events

The following list contains events that you can react on in a paper:

pointerdblclick

Triggered when pointer is double-clicked on a target (a dblclick event is detected).

The callback function is passed cellView, evt, x and y as arguments.

cell:pointerdblclick Triggered when pointer is double-clicked on a cell.
link:pointerdblclick Triggered when pointer is double-clicked on a link.
element:pointerdblclick Triggered when pointer is double-clicked on an element.
blank:pointerdblclick

Triggered when pointer is double-clicked on the paper outside any cell.

The callback function is passed evt, x and y as arguments.

pointerclick

Triggered when pointer is left-clicked on a target without pointer movement (a click or touchend event is detected). Occurs alongside pointerdown and pointerup events.

The callback function is passed cellView, evt, x and y as arguments.

cell:pointerclick Triggered when pointer is clicked on a cell.
link:pointerclick Triggered when pointer is clicked on a link.
element:pointerclick Triggered when pointer is clicked on an element.
blank:pointerclick

Triggered when pointer is clicked on the paper outside any cell.

The callback function is passed evt, x and y as arguments.

contextmenu

Triggered when pointer is right-clicked on a target. This trigger is fired on a mousedown event and it does not depend on different implementations of the contextmenu event.

The callback function is passed cellView, evt, x and y as arguments.

cell:contextmenu Triggered when pointer is right-clicked on a cell.
link:contextmenu Triggered when pointer is right-clicked on a link.
element:contextmenu Triggered when pointer is right-clicked on an element.
blank:contextmenu

Triggered when pointer is right-clicked on the paper outside any cell.

The callback function is passed evt, x and y as arguments.

pointerdown

Triggered when pointer is pressed down on a target (a mousedown or touchstart event is detected). The paper also starts delegating respective pointermove and pointerup events (including their touch counterparts; see below).

The callback function is passed cellView, evt, x and y as arguments.

cell:pointerdown Triggered when pointer is pressed down on a cell.
link:pointerdown Triggered when pointer is pressed down on a link.
element:pointerdown Triggered when pointer is pressed down on an element.
blank:pointerdown

Triggered when pointer is pressed down on the paper outside any cell.

The callback function is passed evt, x and y as arguments.

pointermove

Triggered when pointer is moved over a target while pressed down (a mousemove or touchmove event is detected).

The callback function is passed cellView, evt, x and y as arguments.

cell:pointermove Triggered when pointer is moved over a cell.
link:pointermove Triggered when pointer is moved over a link.
element:pointermove Triggered when pointer is moved over an element.
blank:pointermove

Triggered when pointer is moved over the paper outside any cell.

The callback function is passed evt, x and y as arguments.

pointerup

Triggered when pointer is released on a target after being pressed down (a mouseup or touchend event is detected).

The callback function is passed cellView, evt, x and y as arguments.

cell:pointerup Triggered when pointer is released on a cell.
link:pointerup Triggered when pointer is relased on a link.
element:pointerup Triggered when pointer is released on an element.
blank:pointerup

Triggered when pointer is released on the paper outside any cell.

The callback function is passed evt, x and y as arguments.

Calling evt.stopPropagation() prevents triggering a subsequent pointerclick event.

mouseover

Triggered when pointer begins to hover directly over a target.

The callback function is passed cellView and evt as arguments.

cell:mouseover Triggered when pointer begins to hover directly over a cell.
link:mouseover Triggered when pointer begins to hover directly over a link.
element:mouseover Triggered when pointer begins to hover directly over an element.
blank:mouseover

Triggered when pointer begins to hover over the svg element of the paper outside any cell.

The callback function is passed evt as argument.

mouseout

Triggered when pointer ceases to hover directly over a target.

The callback function is passed cellView and evt as arguments.

cell:mouseout Triggered when pointer ceases to hover directly over a cell.
link:mouseout Triggered when pointer ceases to hover directly over a link.
element:mouseout Triggered when pointer ceases to hover directly over an element.
blank:mouseout

Triggered when pointer ceases to hover over the svg element of the paper outside any cell.

The callback function is passed evt as argument.

mouseenter

Triggered when pointer enters the area above a target.

The callback function is passed cellView and evt as arguments.

cell:mouseenter Triggered when pointer enters the area above a cell.
link:mouseenter Triggered when pointer enters the area above a link.
element:mouseenter Triggered when pointer enters the area above an element.
paper:mouseenter

Triggered when pointer enters the area of the paper (including paper border, if present).

The callback function is passed evt as argument.

mouseleave

Triggered when pointer leaves the area above a target.

The callback function is passed cellView and evt as arguments.

cell:mouseleave Triggered when pointer leaves the area above a cell.
link:mouseleave Triggered when pointer leaves the area above a link.
element:mouseleave Triggered when pointer leaves the area above an element.
paper:mouseleave

Triggered when pointer leaves the area of the paper (including paper border, if present).

The callback function is passed evt as argument.

mousewheel

Triggered when mouse wheel is rotated while pointer is on a target.

The callback function is passed cellView, evt, x, y and delta as arguments.

cell:mousewheel Triggered when mouse wheel is rotated while the pointer is on a cell.
link:mousewheel Triggered when mouse wheel is rotated while the pointer is on a link.
element:mousewheel Triggered when mouse wheel is rotated while the pointer is on an element.
blank:mousewheel

Triggered when mouse wheel is rotated while the pointer is on the paper outside any cell.

The callback function is passed evt, x, y and delta as arguments.

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 evt, deltaX and deltaY as arguments.

paper:pan

Triggered when a pan event is emitted while the pointer is on the paper or any cell.

The callback function is passed evt, deltaX and deltaY as arguments.

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 evt, x, y and scale as arguments.

paper:pinch

Triggered when a pinch event is emitted while the pointer is on the paper or any cell.

The callback function is passed evt, x y and scale as arguments.

magnet

Triggered when interacting with a magnet target.

The callback function is passed elementView, evt, magnetSVGElement, x, y as arguments.

element:magnet:pointerdown Triggered when pointer is pressed down on a magnet element.
element:magnet:pointermove Triggered when pointer is moved after pointerdown on the magnet element.
element:magnet:pointerup Triggered when pointer is released after pointerdown on the magnet element.
element:magnet:pointerclick Triggered when pointer is clicked on an element magnet and no link was created yet from this magnet (controlled by magnetThreshold option). Calling evt.stopPropagation() prevents triggering cell:pointerclick and element:pointerclick events.
element:magnet:pointerdblclick Triggered when pointer is double-clicked on an element magnet. Calling evt.stopPropagation() prevents triggering cell:pointerdblclick and element:pointerdblclick events.
element:magnet:contextmenu Triggered when pointer is right-clicked on an element magnet. Calling evt.stopPropagation() prevents triggering cell:contextmenu and element:contextmenu events.
cell:highlight

Triggered when the cellView.highlight method is called on a link or element view.

The callback function is invoked with the following arguments:

  • cellView - highlighted CellView
  • node - highlighted node (SVGElement) of the CellView
  • options - options used when cellView.highlight(node, options) was called. The type property is added to the option object and specifies what type of highlighting interactions caused the highlight.
// Disable built-in highlighting
paper.options.highlighting = false;
// Handle each type of the highlight manually
paper.on('cell:highlight', (cellView, node, options) => {
  switch (options.type) {
    case: joint.dia.CellView.Highlighting.CONNECTING: {
      joint.highlighters.stroke.add(cellView, node, 'id1', { attrs: { 'stroke': 'blue' }});
      break;
    }
    case: joint.dia.CellView.Highlighting.EMBEDDING: {
      joint.highlighters.stroke.add(cellView, node, 'id2', { attrs: { 'stroke': 'red' }});
      break;
    }
    default: {
      joint.highlighters.stroke.add(cellView, node, 'id3');
      break;
    }
  }
});
cell:unhighlight

Triggered when the cellView.unhighlight method is called on a link or element view.

The callback function is invoked with the following arguments:

  • cellView - highlighted CellView
  • node - highlighted node (SVGElement) of the CellView
  • options - options used when cellView.unhighlight(node, options) was called. The type property is added to the option object and specifies what type of highlighting interactions caused the unhighlight.
// Disable built-in highlighting
paper.options.highlighting = false;
// Handle each type of the highlight manually
paper.on('cell:unhighlight', (cellView, _node, options) => {
  switch (options.type) {
    case: joint.dia.CellView.Highlighting.CONNECTING: {
      joint.dia.Highlighter.remove(cellView, 'id1');
      break;
    }
    case: joint.dia.CellView.Highlighting.EMBEDDING: {
      joint.dia.Highlighter.remove(cellView, 'id2');
      break;
    }
    default: {
      joint.dia.Highlighter.remove(cellView, 'id3');
      break;
    }
  }
});
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 cellView, highlighterId and highlighter as arguments.

paper.on('cell:highlight:invalid', (cellView, id) => {
  // Remove the invalid highlighter
  joint.dia.HighlighterView.remove(cellView, id);
});
element1.addPort({ id: 'port1' });
const elementView1 = element1.findView(paper);
const highlighter1 = joint.highlighters.mask.add(elementView1, { port: 'port1' }, 'highlighter-id'));
// The following line will trigger the event with (elementView1, 'highlighter-id', highlighter1) arguments.
element1.removePorts();
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 linkView, evt, elementViewConnected, magnet and arrowhead as arguments.

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 linkView, evt, elementViewDisconnected, magnet and arrowhead as arguments.

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 linkView, evt, elementViewConnected, magnet and arrowhead as arguments.

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 linkView, evt, elementViewDisconnected, magnet and arrowhead as arguments.

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 evt.stopPropagation() prevents triggering all subsequent events.

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();
    }
  }
});
dia.Paper.prototype.checkViewport
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).

dia.Paper.prototype.clientOffset
paper.clientOffset()

Returns coordinates of the paper viewport, relative to the application's client area.

dia.Paper.prototype.clientToLocalPoint
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);

dia.Paper.prototype.clientToLocalRect
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);

dia.Paper.prototype.defineFilter
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 blur
    • y - vertical blur [optional, if not defined y is the same as x]
  • dropShadow
    • dx - horizontal shift
    • dy - vertical shift
    • blur - blur
    • color - color
    • opacity - opacity
  • grayscale
    • 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 adjusted
  • invert
    • 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.
dia.Paper.prototype.defineGradient
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.

dia.Paper.prototype.defineMarker
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})`);

dia.Paper.prototype.definePattern
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})`);
dia.Paper.prototype.drawBackground
paper.drawBackground(opt);

Sets the paper background defined by the opt object. Please see the background paper option for available configuration.

dia.Paper.prototype.dumpViews
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:
done boolean Are we done? Was this the last batch?
processed number How far along are we? How many elements have been processed as part of a batch so far?
total number How many views are there? How many elements in will have been processed as part of a batch when we are done?
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.
dia.Paper.prototype.findView
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.

dia.Paper.prototype.findViewByModel
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.

dia.Paper.prototype.findViewsFromPoint
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.

dia.Paper.prototype.findViewsInArea
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).

dia.Paper.prototype.fitToContent
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.

dia.Paper.prototype.freeze
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).

dia.Paper.prototype.getArea
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.

dia.Paper.prototype.getComputedSize
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).

dia.Paper.prototype.getContentArea
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.)

dia.Paper.prototype.getContentBBox
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.)

dia.Paper.prototype.getFitToContentArea
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'.
    • By default, the method only recognizes the content at positive coordinates and puts the origin of resulting paper at the original point (0,0).
    • '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).
dia.Paper.prototype.hasScheduledUpdates
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.

dia.Paper.prototype.hideTools
paper.hideTools()

Hide all tools attached to all element and link views on this paper.

dia.Paper.prototype.isDefined
paper.isDefined(graphicalObjectId)

Return true if there is a graphical object (gradient, filter, marker) with graphicalObjectId already defined within the paper. Return false otherwise.

dia.Paper.prototype.isFrozen
paper.isFrozen()

Return true if the paper is currently frozen. Return false otherwise.

dia.Paper.prototype.localToClientPoint
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);

dia.Paper.prototype.localToClientRect
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);

dia.Paper.prototype.localToPagePoint
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);

dia.Paper.prototype.localToPageRect
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);

dia.Paper.prototype.localToPaperPoint
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); 

dia.Paper.prototype.localToPaperRect
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);

dia.Paper.prototype.matrix
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
dia.Paper.prototype.pageOffset
paper.pageOffset()

Returns coordinates of the paper viewport, relative to the document.

dia.Paper.prototype.pageToLocalPoint
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);
});

dia.Paper.prototype.pageToLocalRect
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);
});

dia.Paper.prototype.paperToLocalPoint
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);

dia.Paper.prototype.paperToLocalRect
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);

dia.Paper.prototype.properties

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.

dia.Paper.prototype.removeTools
paper.removeTools()

Remove all tools view objects attached to all element and link views on this paper.

dia.Paper.prototype.requireView
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).

dia.Paper.prototype.scale
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).

The event handlers receive the current scale / 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('transform', (matrix, data) => console.log(matrix, data.foo));
paper.scale(2, 2, { foo: 'bar' }); // will trigger both events
dia.Paper.prototype.scaleContentToFit
paper.scaleContentToFit([opt])

Scale the paper content so that it fits the paper dimensions.

Deprecated alias of the transformToFitContent.

dia.Paper.prototype.scaleUniformAtPoint
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);
});
dia.Paper.prototype.setDimensions
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.
// Responsive Paper
var paper = new joint.dia.Paper({ width: '100%', height: '100%' });
window.on('resize', joint.util.debounce(function() {
  paper.scaleContentToFit({ padding: 10 });
}), false);
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
dia.Paper.prototype.setGrid
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)' }});
dia.Paper.prototype.setGridSize
paper.setGridSize(gridSize)

Set the grid size of the paper.

dia.Paper.prototype.setInteractivity
paper.setInteractivity(interactive)

Set the interactivity of the paper. For example, to disable interactivity:

paper.setInteractivity(false);
dia.Paper.prototype.showTools
paper.showTools()

Show all tools attached to all element and link views on this paper.

dia.Paper.prototype.transformToFitContent
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.

dia.Paper.prototype.translate
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
dia.Paper.prototype.unfreeze
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:
done boolean Are we done? Was this the last batch?
processed number How far along are we? How many elements have been processed as part of a batch so far?
total number How many views are there? How many elements in will have been processed as part of a batch when we are done?
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.

dia.Paper.prototype.updateViews
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:
done boolean Are we done? Was this the last batch?
processed number How far along are we? How many elements have been processed as part of a batch so far?
total number How many views are there? How many elements in will have been processed as part of a batch when we are done?
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.
dia.Paper.prototype.options.afterRender
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
dia.Paper.prototype.options.anchorNamespace
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.
dia.Paper.prototype.options.async

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...

  • ...the views of freshly added cell models may not have yet been added to this paper's DOM.
  • ...some views may have been removed from the DOM by the paper.options.viewport function.
  • ...views already present in the DOM may not have been updated to reflect changes made in this paper's graph since the last render.
  • ...re-rendering may have been manually disabled with 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:

dia.Paper.prototype.options.autoFreeze
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.
dia.Paper.prototype.options.background

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.

  • color property sets the paper background color. It accepts all the values accepted by the CSS background-color property. e.g 'red', 'rgba(255, 255, 0, 0.5)', 'radial-gradient(ellipse at center, red, green);'
  • image property defines the path to the background image file. e.g. '/my-background.png'.
  • position is an object { 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.
  • size is an object { 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.
  • repeat property defines how the background image is repeated. The value could be any value accepted by the CSS background-repeat property and few more defined by JointJS. Those are flip-x, flip-y, flip-xy and watermark. It defaults to no-repeat.
  • quality is a coefficient specifying the quality of the image (e.g 0.5 uses only 50% the image size for creating a pattern). Applicable only for the JointJS repeat option values. It defaults to 1.
  • opacity is a number in the range [0,1] specifying the transparency of the background image (0 is fully transparent and 1 is fully opaque). It defaults to 1.
  • watermarkAngle is an angle in degrees speficying the slope of the watermark image. Applicable only for the 'watermark' repeat option. It defaults to 20 deg.
background: {
   color: '#6764A7',
   image: 'jointjs-logo.png',
   repeat: 'watermark',
   opacity: 0.3
}
dia.Paper.prototype.options.beforeRender
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.
dia.Paper.prototype.options.cellViewNamespace

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.

dia.Paper.prototype.options.clickThreshold
clickThreshold - When number of mousemove events exceeds the clickThreshold there is no pointerclick event triggered after mouseup. It defaults to 0.
dia.Paper.prototype.options.connectionPointNamespace
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.
dia.Paper.prototype.options.connectionStrategy
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.
dia.Paper.prototype.options.connectorNamespace
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.
dia.Paper.prototype.options.defaultAnchor
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.
dia.Paper.prototype.options.defaultConnectionPoint
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.
dia.Paper.prototype.options.defaultConnector
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.
dia.Paper.prototype.options.defaultLinkAnchor
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.
dia.Paper.prototype.options.defaultRouter
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.
dia.Paper.prototype.options.drawGrid

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 grid pattern option
dot
drawGrid grid pattern option
fixedDot
drawGrid grid pattern option
mesh
drawGrid grid pattern option
doubleMesh
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
]}
dia.Paper.prototype.options.drawGridSize
drawGridSize - the size of the visual grid drawn using the drawGrid option. If this option is set to null (default), the gridSize option is used instead. If the option is changed after the paper has been initialized, call drawGrid to update the grid.
dia.Paper.prototype.options.el
el - CSS selector, or a DOM element holding the container for the paper
dia.Paper.prototype.options.elementView
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)
dia.Paper.prototype.options.embeddingMode
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.
dia.Paper.prototype.options.findParentBy

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.
findParentBy: function(elementView, evt, x, y) {
    const graph = elementView.paper.model;
    return graph.findModelsFromPoint({ x, y });
}
dia.Paper.prototype.options.frontParentOnly
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,
dia.Paper.prototype.options.frozen
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();
dia.Paper.prototype.options.gridSize
gridSize - size of the grid in pixels
dia.Paper.prototype.options.guard
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.
dia.Paper.prototype.options.height
height - height of the paper (see setDimensions()).
dia.Paper.prototype.options.highlighterNamespace
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.
dia.Paper.prototype.options.highlighting

highlighting - Configure which highlighter to use (if any) for each type of interaction.

List of highlighting interactions
Name Enum Description Default
default joint.dia.CellView.Highlighting.DEFAULT the default highlighter to use (and options) when none is specified
{
    name: 'stroke',
    options: {
        padding: 3
    }
}
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
{
    name: 'addClass',
    options: {
        className: 'available-magnet'
    }
}
elementAvailability joint.dia.CellView.Highlighting.ELEMENT_AVAILABILITY when showing all elements to which a valid connection can be made
{
    name: 'addClass',
    options: {
        className: 'available-cell'
    }
}

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"]');
});
dia.Paper.prototype.options.interactive

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 paper.options.snapLabels paper option to only allow the label to be dragged along the length of the link.

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
dia.Paper.prototype.options.labelsLayer
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.
dia.Paper.prototype.options.linkAnchorNamespace
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.
dia.Paper.prototype.options.linkPinning
linkPinning - when set to true (the default), links can be pinned to 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.
dia.Paper.prototype.options.linkView
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).
dia.Paper.prototype.options.magnetThreshold
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.
dia.Paper.prototype.options.markAvailable
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.
dia.Paper.prototype.options.model
model - joint.dia.Graph object
dia.Paper.prototype.options.moveThreshold
moveThreshold - Number of required mousemove events before the first pointermove event is triggered. It defaults to 0.
dia.Paper.prototype.options.overflow
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.
dia.Paper.prototype.options.preventContextMenu
preventContextMenu - Prevents default context menu action (when right button of the mouse is clicked). It defaults to true.
dia.Paper.prototype.options.preventDefaultBlankAction
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.
dia.Paper.prototype.options.preventDefaultViewAction
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.
dia.Paper.prototype.options.restrictTranslate
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:
  • rectangle - (an object of the form { x: Number, y: Number, width: Number, height: Number }) that defines the area in which the element represented by elementView can be moved.
  • null - no restriction for element translation
  • (x, y) => Position - For a given element position 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;
    };
}
dia.Paper.prototype.options.routerNamespace
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.
dia.Paper.prototype.options.snapLabels
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.
dia.Paper.prototype.options.snapLinksSelf
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).
dia.Paper.prototype.options.sorting

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.)
dia.Paper.prototype.options.validateConnection
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;
}
dia.Paper.prototype.options.validateEmbedding
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).
dia.Paper.prototype.options.validateMagnet
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).
dia.Paper.prototype.options.validateUnembedding
validateUnembedding - a function that allows you to control which elements can be unembedded when the paper is put into the embeddingMode (see above).
The function signature is function(childView) and should return true if the childView element can be unembedded from the parent and become a new root.
By default, all elements can become roots (the function returns true no matter what).
The callback is called at the end of drag & drop action and if false is returned, the position of the element, as well as the parent reference is reverted to the values before dragging.
The callback is called only on elements which were previously embedded in another element.
dia.Paper.prototype.options.viewport
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);
    });
}
dia.Paper.prototype.options.width
width - width of the paper (see setDimensions()).

dia.ToolsView

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).

dia.ToolsView.prototype.blurTool
toolsView.blurTool(tool)

Stop focusing the specified tool (of the joint.dia.ToolView type) within this tools view.

This function reverses the effect of the toolsView.focusTool function.

dia.ToolsView.prototype.blurTools
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.

dia.ToolsView.prototype.focusTool
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.

dia.ToolsView.prototype.getName
toolsView.getName()

Return the name of this tools view. If no name was set during the construction of the tools view, null is returned.

dia.ToolsView.prototype.hide
toolsView.hide()

Hide all tools within this tools view.

dia.ToolsView.prototype.show
toolsView.show()

Show all tools within this tools view.

dia.ToolsView.prototype.options.layer

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.
dia.ToolsView.prototype.options.z

z - the stacking order (z-index) of the tools view in the given stacking context.

dia.ToolView

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).

dia.ToolView.prototype.blur
toolView.blur()

Stop focusing the tool within its containing toolsView.

This function reverses the effect of the toolView.focus function.

dia.ToolView.prototype.focus
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.

dia.ToolView.prototype.getName
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);
        }
    }
});
dia.ToolView.prototype.hide
toolView.hide()

Hide the tool.

The effect of this function can be reversed by the toolView.show function.

dia.ToolView.prototype.isVisible
toolView.isVisible()

Return true if the tool is currently visible.

A tool is not visible if it has been hidden (e.g. with the toolView.hide function) or if another tool within the containing toolsView has been focused (e.g. with the toolView.focus function).

dia.ToolView.prototype.show
toolView.show()

Show the tool.

The effect of this function can be reversed by the toolView.hide function.

elementTools

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:

To 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:

Example:

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
});

elementTools.Boundary

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
});

elementTools.Button

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();

elementTools.Connect

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.
'body' // as a string Selector
linkView.el // as an SVGElement
(linkView) => linkView.el // as a function

The callback function is expected to have the signature function(linkView, toolView) where linkView is the link view with the tool attached. The connect button view is available inside the function as this and as the last parameter.

Example:

const connectButton = new joint.elementTools.Connect({
    x: '100%',
    y: '0%',
    offset: { x: -5, y: -5 },
    magnet: 'body'
});

elementTools.Control

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' }})
    ]
}));

elementTools.HoverConnect

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'])
});

elementTools.Remove

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:
function(evt, elementView, toolView) {
    elementView.model.remove({ ui: true, tool: toolView.cid });
}
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

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

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:

  • svgforeignobject - Tests whether the browser supports foreignObject.

highlighters

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.

A custom paper event can be defined with event attribute.
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');
When adding a highlighter, prefer using a node selector over a reference to an actual node.
// Prefer this:
joint.highlighters.mask.add(elementView, { port: 'port1' }, 'port-highlight');
// Over this:
joint.highlighters.mask.add(elementView, elementView.findPortNode('port1'), 'port-highlight');
  • Using the node selector supports asynchronous rendering. No need to wait for an element to be rendered, when adding the highlighter.
  • Using the node reference highlights the specific node only. If the view is redrawn, the node could have been replaced with another node which wouldn't be highlighted.

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);
})

highlighters.addClass

Toggles a class name on an arbitrary cell view's SVG node.

Available options:

  • className - the class name to toggle on the cell's node

Example usage:

joint.highlighters.addClass.add(cellView, 'root', 'my-highlighter-id', {
    className: 'some-custom-class'
});

highlighters.custom

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
});

highlighters.list

Adds a list of arbitrary SVGElements to the cell view.

Available options:

  • layer - the stacking order of the highlighter. See dia.Highlighter for supported values.
  • attribute - the name of the model attribute to read the list items from (and automatically update the list when the attribute changes). The option is mandatory and it's an array of items (each item is then passed as is to the createListItem) method.
  • gap - the space between items. Expects a number.
  • size - the size of each item. It's either a number or an object with width and height properties.
  • direction - specifies how list items are placed in the list container defining the main axis. It's either row (for growth on x coordinate) or column (for growth on y coordinate).
  • position - specifies the position and the alignment of the list container. The value is a position name. Using one of the position values sets the list anchor and the position within the item view to that value. For instance the bottom-right value positions the list by its bottom-right corner to the bottom-right corner of the element.
  • margin - the space around the list container. It's a number or an object with left, top, right, bottom properties.

Methods:

highlighters.list.createListItem

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.

Example usage:

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'])

highlighters.mask

Adds a stroke around an arbitrary cell view's SVG node.

It's an alternative to the stroke highlighter.

Pros:
  • more precise - equidistant padding along the node
  • more powerful - ability to draw a stroke around the descendant nodes or draw a stroke around a path with no fill
Cons:
  • lesser performance - uses SVG masking to find the outline
  • no fill - the color inside the mask must stay transparent.
  • no dasharray - the stroke patterns of dashes and gaps are not applicable.

Available options:

  • layer - the stacking order of the highlighter. See dia.Highlighter for supported values.
  • padding - the space between the stroke and the node
  • deep - if the node is a container element (e.g. SVGGroupElement), draw the mask around its descending nodes. Otherwise a rectangular mask is drawn.
  • attrs - an object with SVG attributes to be set on the mask element
  • maskClip - it determines the area which is affected by a mask. The painted content of an element must be restricted to this area. It defaults to 20 (20 pixels around the node's bounding box).

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
    }
});

highlighters.opacity

Changes the opacity of an arbitrary cell view's SVG node.

Available options:

  • alphaValue - a value that represents the degree to which content behind an element is hidden. It's a number in the range 0.0 to 1.0, inclusive. The default is 0.3.

Example usage:

joint.highlighters.opacity.add(cellView, 'body', 'my-highlighter-id', {
  alphaValue: 0.4
});

highlighters.stroke

Adds a stroke around an arbitrary cell view's SVG node.

Available options:

  • layer - the stacking order of the highlighter. See dia.Highlighter for supported values.
  • padding - the space between the stroke and the element
  • rx - the stroke's border radius on the x-axis
  • ry - the stroke's border radius on the y-axis
  • attrs - an object with SVG attributes to be set on the highlighter element
  • useFirstSubpath - draw the stroke by using the first subpath of the target el compound path.
  • nonScalingStroke - when enabled the stroke width of the highlighter is not dependent on the transformations of the paper (e.g. zoom level). It defaults to 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'
    }
});

layout.DirectedGraph

Automatic layout of directed graphs. This plugin uses the open-source (MIT license) Dagre library internally.

Installation

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';

Usage

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.

Configuration

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.

API

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.
import { DirectedGraph } from '@joint/layout-directed-graph';
import * as graphlib from '@dagrejs/graphlib';

var graph = new joint.dia.Graph({}, { cellNamespace: joint.shapes });
// ... populate the graph with elements connected with links

// Get a Graphlib representation of the graph:
var glGraph = DirectedGraph.toGraphLib(graph);

// Use Graphlib algorithms:
graphlib.alg.isAcyclic(glGraph); // true if the graph is acyclic
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.
import { DirectedGraph } from '@joint/layout-directed-graph';
import * as graphlib from 'graphlib';

// Create a graph in Graphlib:
var glGraph = new graphlib.Graph();
glGraph.setNode(1);
glGraph.setNode(2);
glGraph.setNode(3);
glGraph.setEdge(1, 2);
glGraph.setEdge(2, 3);

// Get a JointJS representation of the Graphlib graph:
var graph = DirectedGraph.fromGraphLib(glGraph, {
    importNode: function(node) {
        return new joint.shapes.standard.Rectangle({
            position: { x: node.x, y: node.y },
            size: { width: node.width, height: node.height }
        });
    },
    importEdge: function(edge) {
        return new joint.shapes.standard.Link({
            source: { id: edge.v },
            target: { id: edge.w }
        });
    }
});

layout.Port

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' });

Pre-defined layouts:

On sides

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
x number Overrides the x / y value calculated by the layout function. It can be defined as a percentage string e.g. '50%', calc() expression or a number
y number
dx number Added to the x value calculated by the layout function
dy number Added to the y value calculated by the layout function
angle number The port rotation angle.
{
    name: 'left',
    args: {
        x: 10,
        y: 10,
        angle: 30,
        dx: 1,
        dy: 1
    }
}

Line

A layout which evenly spreads ports along a line defined by a start and end point.

name string 'line'
args object
start { x: number|string, y: number|string } The line starting point. x and y can be a percentage string e.g. '50%', calc() expression, or a number
end The line end point
{
    name: 'line',
    args: {
        start: { x: 10, y: 10 },
        end: { x: 'calc(w)', y: '50%' }
    }
}

Absolute

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
x number | string Sets the port's x coordinate. It can be defined as a percentage string e.g. '50%', calc() expression or a number
y
angle number The port rotation angle.
{
    name: 'absolute',
    args: {
        x: '10%',
        y: 10,
        angle: 45
    }
}

Radial

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
x number Overrides the x / y value calculated by the layout function. It can be defined as a percentage string e.g. '50%', calc() expression or a number
y number
dx number Added to the x value calculated by the layout function
dy number Added to the y value calculated by the layout function
dr number Added to the port delta rotation
startAngle number Default value is 0.
step number Default 360 / portsCount for the ellipseSpread, 20 for the ellipse
compensateRotation boolean set compensateRotation: true when you need to have ports in the same angle as an ellipse tangent at the port position.
{
    name: 'ellipseSpread',
    args: {
        dx: 1,
        dy: 1,
        dr: 1,
        startAngle: 10,
        step: 10,
        compensateRotation: false
    }
}

Custom layout

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 layouts demo

layout.PortLabel

Port label layout functions calculate port label positions relatively to port positions.

Pre-defined port label layouts

On Sides

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
x number Overrides the x value calculated by the layout function.
y number Overrides the y value calculated by the layout function.
angle number The port label rotation angle (in degrees in clockwise direction).
attrs object JointJS-style attributes applied on label's DOM element and its children. The same notation as the attrs property on Element. Overridden by port group attrs, if provided.
label: {
    position: {
        name : 'right',
        args: {
            x: 0,
            y: 0,
            angle: 0,
            attrs: {}
        }
    }
}

Inside/Outside

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
offset number Offset in direction from the shape's center. Overridden by x and y values, if provided.
x number Overrides the x value calculated by the layout function.
y number Overrides the y value calculated by the layout function.
angle number The port label rotation angle (in degrees in clockwise direction). Overrides the value calculated by the 'insideOriented' and 'outsideOriented' layout functions.
attrs object JointJS-style attributes applied on label's DOM element and its children. The same notation as the attrs property on Element. Overridden by port group attrs, if provided.

label: {
    position: {
        name :'outsideOriented',
        args: {
            offset: 10,
            attrs: {}
    }
}

Radial

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
offset number Offset in direction from the shape's center. Overridden by x and y values, if provided.
x number Overrides the x value calculated by the layout function.
y number Overrides the y value calculated by the layout function.
angle number The port label rotation angle (in degrees in clockwise direction). Overrides the value calculated by the 'radialOriented' layout function.
attrs number JointJS-style attributes applied on label's DOM element and its children. The same notation as the attrs property on Element. Overridden by port group attrs, if provided.
label: {
    position: {
        name :'radialOriented',
        args: {
            offset: 0,
            attrs: {}
        }
    }
}

Manual placement

It allows setting label position directly.

name string 'manual'
args object
x number Sets the label's x coordinate.
y number Sets the label's y coordinate.
angle number The port label rotation angle (in degrees in clockwise direction).
attrs object JointJS-style attributes applied on label's DOM element and its children. The same notation as the attrs property on Element. Overridden by port group attrs, if provided.
label: {
    position: {
        name: 'manual',
        args: {
            x: 10,
            y: 20,
            angle: 45,
            attrs: {}
        }
    }
}

Port label layout demo

linkAnchors

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:

Example:

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.

linkAnchors.connectionClosest

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'
    }
});

linkAnchors.connectionLength

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
        }
    }
});

linkAnchors.connectionPerpendicular

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',
    }
});

linkAnchors.connectionRatio

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
        }
    }
});

linkAnchors.custom

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).

linkTools

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:

To 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:

Example:

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
});

linkTools.Boundary

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
});

linkTools.Button

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();

linkTools.Connect

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.
'body' // as a string Selector
linkView.el // as an SVGElement
(linkView) => linkView.el // as a function

The callback function is expected to have the signature function(elementView, toolView) where elementView is the element view with the tool attached. The connect button view is available inside the function as this and as the last parameter.

Example:

const connectButton = new joint.linkTools.Connect({
    rotate: true,
    distance: -20,
    offset: 20,
    magnet: 'body'
});

linkTools.HoverConnect

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
});

linkTools.Remove

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:
function(evt, linkView, toolView) {
                linkView.model.remove({ ui: true, tool: toolView.cid });
            }
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:
[{
    tagName: 'circle',
    selector: 'button',
    attributes: {
        'r': 7,
        'fill': '#FF1D00',
        'cursor': 'pointer'
    }
}, {
    tagName: 'path',
    selector: 'icon',
    attributes: {
        'd': 'M -3 -3 3 3 M -3 3 3 -3',
        'fill': 'none',
        'stroke': '#FFFFFF',
        'stroke-width': 2,
        'pointer-events': 'none'
    }
}]
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
});

linkTools.Segments

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 redundant fashion, 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
});

linkTools.SourceAnchor

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 redundant fashion, 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:
  • true - remove the current source anchor from the link. It is a default.
  • false - do nothing
  • { name: [AnchorName], args: [AnchorArguments] } - replace the current source anchor with the option value.
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):

coords g.Point A Point object recording the x-y coordinates of the user pointer when the snap function was invoked.
endView dia.ElementView The ElementView which contains the restricted area. 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) which serves as the restricted area.
endType string Is the end view the source ('source') or target ('target') of the link?
linkView dia.LinkView The LinkView to which the tool is attached. The Link model can be accessed as linkView.model; this may be useful for writing conditional logic based on link attributes.
toolView dia.ToolView This tool. May be useful for writing conditional logic based on tool options (e.g. the default algorithm refers to toolView.options.snapRadius).
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.

linkTools.SourceArrowhead

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
});

linkTools.TargetAnchor

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 redundant fashion, 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:
  • true - remove the current target anchor from the link. It is a default.
  • false - do nothing
  • { name: [AnchorName], args: [AnchorArguments] } - replace the current target anchor with the option value.
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):

coords g.Point A Point object recording the x-y coordinates of the user pointer when the snap function was invoked.
endView dia.ElementView The ElementView which contains the restricted area. 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) which serves as the restricted area.
endType string Is the end view the source ('source') or target ('target') of the link?
linkView dia.LinkView The LinkView to which the tool is attached. The Link model can be accessed as linkView.model; this may be useful for writing conditional logic based on link attributes.
toolView dia.ToolView This tool. May be useful for writing conditional logic based on tool options (e.g. the default algorithm refers to toolView.options.snapRadius).
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.

linkTools.TargetArrowhead

<>The 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
});

linkTools.Vertices

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 redundant fashion, 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,
});

mvc.Collection

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.

mvc.Collection.prototype.add
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.

mvc.Collection.prototype.at
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.

mvc.Collection.prototype.clone
collection.clone()

Returns a new instance of the collection with an identical list of models.

mvc.Collection.prototype.comparator
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.

mvc.Collection.prototype.each
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.prototype.extend
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.

mvc.Collection.prototype.filter
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
mvc.Collection.prototype.find
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
mvc.Collection.prototype.findIndex
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
mvc.Collection.prototype.first
collection.first()

Return the first model of a collection.

mvc.Collection.prototype.get
collection.get(id)

Get a model from a collection, specified by an id, a cid, or by passing in a model.

mvc.Collection.prototype.includes
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
mvc.Collection.prototype.initialize

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
});
mvc.Collection.prototype.isEmpty
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
mvc.Collection.prototype.last
collection.last()

Return the last model of a collection.

mvc.Collection.prototype.length
collection.length

Like an array, a Collection maintains a length property, counting the number of models it contains.

mvc.Collection.prototype.map
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'
mvc.Collection.prototype.model
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);
      }
    }
});
mvc.Collection.prototype.modelId
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
mvc.Collection.prototype.models
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.

mvc.Collection.prototype.pop
collection.pop([options])

Remove and return the last model from a collection. Takes the same options as remove().

mvc.Collection.prototype.preinitialize

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!');
      });
    }
}
mvc.Collection.prototype.push
collection.push(model, [options])

Like add(), but always adds a model at the end of the collection and never sorts.

mvc.Collection.prototype.reduce
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
mvc.Collection.prototype.remove
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.

mvc.Collection.prototype.reset
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.

mvc.Collection.prototype.set
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.
mvc.Collection.prototype.shift
collection.shift([options])

Remove and return the first model from a collection. Takes the same options as remove().

mvc.Collection.prototype.slice
collection.slice(begin, end)

Return a shallow copy of this collection's models, using the same options as native Array.prototype.slice.

mvc.Collection.prototype.sort
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.

mvc.Collection.prototype.toJSON
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.

mvc.Collection.prototype.unshift
collection.unshift(model, [options])

Like add(), but always adds a model at the beginning of the collection and never sorts.

mvc.Events

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');

mvc.Events.events

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.

mvc.Events.listenTo

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);

mvc.Events.listenToOnce

object.listenToOnce(other, event, callback)

Just like listenTo, but causes the bound callback to fire only once before being removed.

mvc.Events.off

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.

mvc.Events.on

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).

mvc.Events.once

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.

mvc.Events.stopListening

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);

mvc.Events.trigger

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.

mvc.Listener

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);
mvc.Listener.prototype.callbackArguments
listener.callbackArguments
An array of constructor arguments.
const listener = new mvc.Listener(callbackArg1, callbackArg2);
listener.callbackArguments; // [callbackArg1, callbackArg2]
mvc.Listener.prototype.listenTo
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) => {}
});
mvc.Listener.prototype.stopListening
listener.stopListening()
Tell the listener to remove all of its registered callbacks.

mvc.Model

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.

mvc.Model.prototype.attributes
model.attributes

The attributes property is the internal hash containing the model's state. Please use set() to update the attributes instead of modifying them directly.

Due to the fact that Events accepts space separated lists of events, attribute names should not include spaces.

mvc.Model.prototype.changed
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.

mvc.Model.prototype.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.

mvc.Model.prototype.cid
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.

mvc.Model.prototype.cidPrefix
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.

mvc.Model.prototype.clear
model.clear([options])

Removes all attributes from the model, including the idattribute. Fires a "change" event unless silent is passed as an option.

mvc.Model.prototype.clone
model.clone()

Returns a new instance of the model with identical attributes.

mvc.Model.prototype.defaults
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.prototype.extend
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);
        ...
    }
});
mvc.Model.prototype.get
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.

mvc.Model.prototype.has
model.has(attribute)

Returns true if the attribute is set to a non-null or non-undefined value.

mvc.Model.prototype.hasChanged
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")) {
        ...
    }
});
mvc.Model.prototype.id
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', …).

mvc.Model.prototype.idAttribute
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
mvc.Model.prototype.initialize

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.

mvc.Model.prototype.isValid
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.

mvc.Model.prototype.preinitialize

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() {...}
}
mvc.Model.prototype.previous
model.previous(attribute)

During a "change" event, this method can be used to get the previous value of a changed attribute.

mvc.Model.prototype.previousAttributes
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.

mvc.Model.prototype.set
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.

mvc.Model.prototype.toJSON
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().

mvc.Model.prototype.unset
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.

mvc.Model.prototype.validate
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.

mvc.Model.prototype.validationError
model.validationError

The value returned by validate during the last failed validation.

mvc.View

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.

mvc.View.prototype.attributes
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.

mvc.View.prototype.delegateEvents
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.

mvc.View.prototype.el
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>
mvc.View.prototype.events
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.prototype.extend
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.

mvc.View.prototype.initialize

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 })

mvc.View.prototype.preinitialize

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);
        }
    }
}
mvc.View.prototype.remove
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.

mvc.View.prototype.render
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.

mvc.View.prototype.setElement
view.setElement(element)

Change the view's element (this.el property) and re-delegate the view's events on the new element.

mvc.View.prototype.undelegateEvents
undelegateEvents()

Removes all of the view's delegated events. Useful if you want to disable or remove a view from the DOM temporarily.

routers

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:

Example:

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');

routers.custom

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;
});

routers.manhattan

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']
});

routers.metro

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']
});

routers.normal

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');

routers.orthogonal

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
});

routers.rightAngle

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:

This router completely ignores vertices, so using vertex tools such as 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
});

routers.rightAngle.Directions

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.

shapes.standard

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.

source code

shapes.standard.BorderedImage

An image with a border and label.

Supported attrs 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);

shapes.standard.Circle

A circle with a label.

Supported attrs 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);

shapes.standard.Cylinder

A cylinder with a label.

Supported attrs properties

Selector Node Description
root SVGGElement Container of all nodes
body SVGPathElement Lateral area of the cylinder

The shape has a custom attribute lateralArea set to 10 by default. The attribute sets the SVGPathElement's d in the shape of the lateral area of a tilted cylinder. The attribute expects a topRy value; either a percentage or a number. The value determines the vertical radius of the exposed area of cylinder base; either relative to shape height, or directly. (Refer to the cylinder.topRy function for more detailed specification.)

top SVGEllipseElement Top of the cylinder (cylinder base)
label SVGTextElement Text inside the body
shapes.standard.Cylinder.prototype.topRy
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);

shapes.standard.Ellipse

An ellipse with a label.

Supported attrs 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);

shapes.standard.EmbeddedImage

An image embedded into a rectangle with a label.

Supported attrs 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);

shapes.standard.HeaderedRectangle

A rectangle with header.

Supported attrs 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);

shapes.standard.Image

An image with a label.

Supported attrs 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);

shapes.standard.InscribedImage

An image inscribed in an ellipse with a label.

Supported attrs 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);

shapes.standard.Path

A path with a label.

Supported attrs 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);

shapes.standard.Polygon

A polygon with a label.

Supported attrs 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);

shapes.standard.Polyline

A polyline with a label.

Supported attrs 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);

shapes.standard.Rectangle

A rectangle with a label.

Supported attrs 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);

shapes.standard.TextBlock

A rectangle with an HTML label (with a fallback to SVG label for IE).

Supported attrs 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

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

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

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

util.camelCase([string=''])

Converts string to camel case.

[string=''] string The string to convert

util.cancelFrame

util.cancelFrame(requestId)

Cancels an animation frame request identified by requestId previously scheduled through a call to joint.util.nextFrame.

util.clone

util.clone(value)

Creates a shallow clone of value.

value any The value to clone

util.cloneDeep

util.cloneDeep(value)

This method is like util.clone except that it recursively clones value.

value any The value to recursively clone

util.dataUriToBlob

util.dataUriToBlob(dataUri)

Convert a Data URI string into a Blob object.

util.debounce

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

util.deepMixin(destinationObject, [...sourceObjects])

(Deprecated) An alias for util.assign, use it instead.

util.deepSupplement

util.deepSupplement(destinationObject [...sourceObjects])

(Deprecated) An alias for util.defaultsDeep, use it instead.

util.defaults

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

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

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

util.downloadBlob(blob, fileName)

Download provided Blob object as a file with given fileName.

util.downloadDataUri

util.downloadDataUri(dataUri, fileName)

Download provided Data URI string as a file with given fileName.

util.flattenDeep

util.flattenDeep(array)

Recursively flattens array.

array any[] The array to flatten

util.flattenObject

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

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

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

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

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

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

util.guid()

Return an identifier unique for the page.

util.has

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

util.hashCode(str)

Return a simple hash code from a string.

util.imageToDataUri

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

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

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

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

util.isBoolean(value)

Return true when value is a boolean.

util.isEmpty

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

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

util.isFunction(value)

Checks if value is classified as a Function object.

value any The value to check

util.isNumber

util.isNumber(value)

Return true when value is a number.

util.isObject

util.isObject(value)

Return true when value is an object (plain object or a function).

util.isPercentage

util.isPercentage(value)

Return true when value is a string that holds a percentage value, e.g. '10%'.

util.isPlainObject

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

util.isString(value)

Return true when value is a string.

util.merge

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

util.mixin(destinationObject, [...sourceObjects])

(Deprecated) An alias for util.assign, use it instead.

util.nextFrame

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

util.noop()

An empty function with no return statement (void in Typescript).

util.normalizeEvent

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

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

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

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

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

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

util.sanitizeHTML(html)

Sanitize the provided HTML (string) to protect against XSS attacks. The algorithm has several steps:

  • Wrap the provided HTML inside a <div> tag. This will remove tags that are invalid in that context (e.g. <body> and <head>).
  • Parse the provided HTML in a new document context. This prevents inline events from firing and also prevents image GET requests from being sent.
  • Discard all <script> tags.
  • Iterate through all DOM nodes and remove all on... attributes (e.g. onload, onerror).
  • Iterate through all attributes of the nodes and remove all that use the javascript: pseudo-protocol as value.
  • Return the sanitized HTML back as a string.

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(&quot;Hacked&quot;);">'); // => '<p>Hello</p><img>'
joint.util.sanitizeHTML('<p>Hello</p><img src="javascript:alert(&quot;Hacked&quot;);">'); // => '<p>Hello</p><img>'

util.setAttributesBySelector

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

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

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

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

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

util.supplement(destinationObject [...sourceObjects])

(Deprecated) An alias for util.defaults, use it instead.

util.svg

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.

Text nodes are placed into the children array as strings. For example, the markup <span>a<span>b</span></span> is converted to { tagName: 'span', children: ['a', { tagName: 'span', children: ['b']}]}.

util.template

util.template(html)

Return a template function that returns pre-compiled html when called.

util.toArray

util.toArray(value)

Converts value to an array.

value any The value to convert

util.toggleFullScreen

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

util.toKebabCase(str)

Convert the string to kebab-case. Example:

joint.util.toKebabCase('strokeWidth'); // => 'stroke-width'

util.union

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

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

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

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.uuid

util.uuid()

Return a pseudo-UUID.

util.without

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

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****