UNPKG

diagram-js

Version:

A toolbox for displaying and modifying diagrams on the web

279 lines (229 loc) 6.32 kB
var ELEMENT_ID = 'data-element-id'; import { attr as svgAttr } from 'tiny-svg'; /** * @typedef {import('./Types').ElementLike} ElementLike * * @typedef {import('./EventBus').default} EventBus * * @typedef { (element: ElementLike, gfx: SVGElement) => boolean|any } ElementRegistryFilterCallback * @typedef { (element: ElementLike, gfx: SVGElement) => any } ElementRegistryForEachCallback */ /** * A registry that keeps track of all shapes in the diagram. * * @class * @constructor * * @param {EventBus} eventBus */ export default function ElementRegistry(eventBus) { /** * @type { { * [id: string]: { * element: ElementLike; * gfx?: SVGElement; * secondaryGfx?: SVGElement; * } * } } */ this._elements = {}; this._eventBus = eventBus; } ElementRegistry.$inject = [ 'eventBus' ]; /** * Add an element and its graphical representation(s) to the registry. * * @param {ElementLike} element The element to be added. * @param {SVGElement} gfx The primary graphical representation. * @param {SVGElement} [secondaryGfx] The secondary graphical representation. */ ElementRegistry.prototype.add = function(element, gfx, secondaryGfx) { var id = element.id; this._validateId(id); // associate dom node with element svgAttr(gfx, ELEMENT_ID, id); if (secondaryGfx) { svgAttr(secondaryGfx, ELEMENT_ID, id); } this._elements[id] = { element: element, gfx: gfx, secondaryGfx: secondaryGfx }; }; /** * Remove an element from the registry. * * @param {ElementLike|string} element */ ElementRegistry.prototype.remove = function(element) { var elements = this._elements, id = element.id || element, container = id && elements[id]; if (container) { // unset element id on gfx svgAttr(container.gfx, ELEMENT_ID, ''); if (container.secondaryGfx) { svgAttr(container.secondaryGfx, ELEMENT_ID, ''); } delete elements[id]; } }; /** * Update an elements ID. * * @param {ElementLike|string} element The element or its ID. * @param {string} newId The new ID. */ ElementRegistry.prototype.updateId = function(element, newId) { this._validateId(newId); if (typeof element === 'string') { element = this.get(element); } this._eventBus.fire('element.updateId', { element: element, newId: newId }); var gfx = this.getGraphics(element), secondaryGfx = this.getGraphics(element, true); this.remove(element); element.id = newId; this.add(element, gfx, secondaryGfx); }; /** * Update the graphical representation of an element. * * @param {ElementLike|string} filter The element or its ID. * @param {SVGElement} gfx The new graphical representation. * @param {boolean} [secondary=false] Whether to update the secondary graphical representation. */ ElementRegistry.prototype.updateGraphics = function(filter, gfx, secondary) { var id = filter.id || filter; var container = this._elements[id]; if (secondary) { container.secondaryGfx = gfx; } else { container.gfx = gfx; } if (gfx) { svgAttr(gfx, ELEMENT_ID, id); } return gfx; }; /** * Get the element with the given ID or graphical representation. * * @example * * ```javascript * elementRegistry.get('SomeElementId_1'); * * elementRegistry.get(gfx); * ``` * * @param {string|SVGElement} filter The elements ID or graphical representation. * * @return {ElementLike|undefined} The element. */ ElementRegistry.prototype.get = function(filter) { var id; if (typeof filter === 'string') { id = filter; } else { id = filter && svgAttr(filter, ELEMENT_ID); } var container = this._elements[id]; return container && container.element; }; /** * Return all elements that match a given filter function. * * @param {ElementRegistryFilterCallback} fn The filter function. * * @return {ElementLike[]} The matching elements. */ ElementRegistry.prototype.filter = function(fn) { var filtered = []; this.forEach(function(element, gfx) { if (fn(element, gfx)) { filtered.push(element); } }); return filtered; }; /** * Return the first element that matches the given filter function. * * @param {ElementRegistryFilterCallback} fn The filter function. * * @return {ElementLike|undefined} The matching element. */ ElementRegistry.prototype.find = function(fn) { var map = this._elements, keys = Object.keys(map); for (var i = 0; i < keys.length; i++) { var id = keys[i], container = map[id], element = container.element, gfx = container.gfx; if (fn(element, gfx)) { return element; } } }; /** * Get all elements. * * @return {ElementLike[]} All elements. */ ElementRegistry.prototype.getAll = function() { return this.filter(function(e) { return e; }); }; /** * Execute a given function for each element. * * @param {ElementRegistryForEachCallback} fn The function to execute. */ ElementRegistry.prototype.forEach = function(fn) { var map = this._elements; Object.keys(map).forEach(function(id) { var container = map[id], element = container.element, gfx = container.gfx; return fn(element, gfx); }); }; /** * Return the graphical representation of an element. * * @example * * ```javascript * elementRegistry.getGraphics('SomeElementId_1'); * * elementRegistry.getGraphics(rootElement); // <g ...> * * elementRegistry.getGraphics(rootElement, true); // <svg ...> * ``` * * @param {ElementLike|string} filter The element or its ID. * @param {boolean} [secondary=false] Whether to return the secondary graphical representation. * * @return {SVGElement} The graphical representation. */ ElementRegistry.prototype.getGraphics = function(filter, secondary) { var id = filter.id || filter; var container = this._elements[id]; return container && (secondary ? container.secondaryGfx : container.gfx); }; /** * Validate an ID and throw an error if invalid. * * @param {string} id * * @throws {Error} Error indicating that the ID is invalid or already assigned. */ ElementRegistry.prototype._validateId = function(id) { if (!id) { throw new Error('element must have an id'); } if (this._elements[id]) { throw new Error('element with id ' + id + ' already added'); } };