var ELEMENT_ID = 'data-element-id'; import { attr as svgAttr } from 'tiny-svg'; /** * @class * * A registry that keeps track of all shapes in the diagram. */ export default function ElementRegistry(eventBus) { this._elements = {}; this._eventBus = eventBus; } ElementRegistry.$inject = [ 'eventBus' ]; /** * Register a pair of (element, gfx, (secondaryGfx)). * * @param {djs.model.Base} element * @param {SVGElement} gfx * @param {SVGElement} [secondaryGfx] optional other element to register, too */ 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 }; }; /** * Removes an element from the registry. * * @param {djs.model.Base} 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 the id of an element * * @param {djs.model.Base} element * @param {String} newId */ 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); }; /** * Return the model element for a given id or graphics. * * @example * * elementRegistry.get('SomeElementId_1'); * elementRegistry.get(gfx); * * * @param {String|SVGElement} filter for selecting the element * * @return {djs.model.Base} */ 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 {Function} fn * * @return {Array} */ ElementRegistry.prototype.filter = function(fn) { var filtered = []; this.forEach(function(element, gfx) { if (fn(element, gfx)) { filtered.push(element); } }); return filtered; }; /** * Return all rendered model elements. * * @return {Array} */ ElementRegistry.prototype.getAll = function() { return this.filter(function(e) { return e; }); }; /** * Iterate over all diagram elements. * * @param {Function} fn */ 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 or its id. * * @example * elementRegistry.getGraphics('SomeElementId_1'); * elementRegistry.getGraphics(rootElement); // * * elementRegistry.getGraphics(rootElement, true); // * * * @param {String|djs.model.Base} filter * @param {Boolean} [secondary=false] whether to return the secondary connected element * * @return {SVGElement} */ ElementRegistry.prototype.getGraphics = function(filter, secondary) { var id = filter.id || filter; var container = this._elements[id]; return container && (secondary ? container.secondaryGfx : container.gfx); }; /** * Validate the suitability of the given id and signals a problem * with an exception. * * @param {String} id * * @throws {Error} if id is empty 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'); } };