jstd-web/node_modules/diagram-js/lib/features/preview-support/PreviewSupport.js

261 lines
5.2 KiB
JavaScript

import {
forEach
} from 'min-dash';
import {
append as svgAppend,
attr as svgAttr,
classes as svgClasses,
clone as svgClone,
create as svgCreate,
remove as svgRemove
} from 'tiny-svg';
import { query as domQuery } from 'min-dom';
import { getVisual } from '../../util/GraphicsUtil';
/**
* @typedef {import('../../model').Base} Base
*
* @typedef {import('../../core/Canvas').default} Canvas
* @typedef {import('../../core/ElementRegistry').default} ElementRegistry
* @typedef {import('../../core/EventBus').default} EventBus
* @typedef {import('../../draw/Styles').default} Styles
*/
var MARKER_TYPES = [
'marker-start',
'marker-mid',
'marker-end'
];
var NODES_CAN_HAVE_MARKER = [
'circle',
'ellipse',
'line',
'path',
'polygon',
'polyline',
'path',
'rect'
];
/**
* Adds support for previews of moving/resizing elements.
*
* @param {ElementRegistry} elementRegistry
* @param {EventBus} eventBus
* @param {Canvas} canvas
* @param {Styles} styles
*/
export default function PreviewSupport(elementRegistry, eventBus, canvas, styles) {
this._elementRegistry = elementRegistry;
this._canvas = canvas;
this._styles = styles;
this._clonedMarkers = {};
var self = this;
eventBus.on('drag.cleanup', function() {
forEach(self._clonedMarkers, function(clonedMarker) {
svgRemove(clonedMarker);
});
self._clonedMarkers = {};
});
}
PreviewSupport.$inject = [
'elementRegistry',
'eventBus',
'canvas',
'styles'
];
/**
* Returns graphics of an element.
*
* @param {Base} element
*
* @return {SVGElement}
*/
PreviewSupport.prototype.getGfx = function(element) {
return this._elementRegistry.getGraphics(element);
};
/**
* Adds a move preview of a given shape to a given svg group.
*
* @param {Base} element
* @param {SVGElement} group
* @param {SVGElement} [gfx]
*
* @return {SVGElement} dragger
*/
PreviewSupport.prototype.addDragger = function(element, group, gfx) {
gfx = gfx || this.getGfx(element);
var dragger = svgClone(gfx);
var bbox = gfx.getBoundingClientRect();
this._cloneMarkers(getVisual(dragger));
svgAttr(dragger, this._styles.cls('djs-dragger', [], {
x: bbox.top,
y: bbox.left
}));
svgAppend(group, dragger);
return dragger;
};
/**
* Adds a resize preview of a given shape to a given svg group.
*
* @param {Base} element
* @param {SVGElement} group
*
* @return {SVGElement} frame
*/
PreviewSupport.prototype.addFrame = function(shape, group) {
var frame = svgCreate('rect', {
class: 'djs-resize-overlay',
width: shape.width,
height: shape.height,
x: shape.x,
y: shape.y
});
svgAppend(group, frame);
return frame;
};
/**
* Clone all markers referenced by a node and its child nodes.
*
* @param {SVGElement} gfx
*/
PreviewSupport.prototype._cloneMarkers = function(gfx) {
var self = this;
if (gfx.childNodes) {
// TODO: use forEach once we drop PhantomJS
for (var i = 0; i < gfx.childNodes.length; i++) {
// recursively clone markers of child nodes
self._cloneMarkers(gfx.childNodes[ i ]);
}
}
if (!canHaveMarker(gfx)) {
return;
}
MARKER_TYPES.forEach(function(markerType) {
if (svgAttr(gfx, markerType)) {
var marker = getMarker(gfx, markerType, self._canvas.getContainer());
self._cloneMarker(gfx, marker, markerType);
}
});
};
/**
* Clone marker referenced by an element.
*
* @param {SVGElement} gfx
* @param {SVGElement} marker
* @param {string} markerType
*/
PreviewSupport.prototype._cloneMarker = function(gfx, marker, markerType) {
var markerId = marker.id;
var clonedMarker = this._clonedMarkers[ markerId ];
if (!clonedMarker) {
clonedMarker = svgClone(marker);
var clonedMarkerId = markerId + '-clone';
clonedMarker.id = clonedMarkerId;
svgClasses(clonedMarker)
.add('djs-dragger')
.add('djs-dragger-marker');
this._clonedMarkers[ markerId ] = clonedMarker;
var defs = domQuery('defs', this._canvas._svg);
if (!defs) {
defs = svgCreate('defs');
svgAppend(this._canvas._svg, defs);
}
svgAppend(defs, clonedMarker);
}
var reference = idToReference(this._clonedMarkers[ markerId ].id);
svgAttr(gfx, markerType, reference);
};
// helpers //////////
/**
* Get marker of given type referenced by node.
*
* @param {Node} node
* @param {string} markerType
* @param {Node} [parentNode]
*
* @param {Node}
*/
function getMarker(node, markerType, parentNode) {
var id = referenceToId(svgAttr(node, markerType));
return domQuery('marker#' + id, parentNode || document);
}
/**
* Get ID of fragment within current document from its functional IRI reference.
* References may use single or double quotes.
*
* @param {string} reference
*
* @returns {string}
*/
function referenceToId(reference) {
return reference.match(/url\(['"]?#([^'"]*)['"]?\)/)[1];
}
/**
* Get functional IRI reference for given ID of fragment within current document.
*
* @param {string} id
*
* @returns {string}
*/
function idToReference(id) {
return 'url(#' + id + ')';
}
/**
* Check wether node type can have marker attributes.
*
* @param {Node} node
*
* @returns {boolean}
*/
function canHaveMarker(node) {
return NODES_CAN_HAVE_MARKER.indexOf(node.nodeName) !== -1;
}